"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.struct = exports.tuple = exports.array = exports.date = exports.boolean = exports.symbol = exports.string = exports.number = exports.strict = exports.contramap = exports.orElseEither = exports.both = exports.never = exports.any = exports.makeEqual = void 0;
/**
 * Constructs an `Equal[A]` from a function. The instance will be optimized
 * to first compare the values for reference equality and then compare the
 * values for value equality.
 */
function makeEqual(f) {
    return {
        equals: f
    };
}
exports.makeEqual = makeEqual;
/**
 * Equality for `Any` values. Note that since values of type `Any` contain
 * no information, all values of type `Any` can be treated as equal to each
 * other.
 */
exports.any = makeEqual(() => true);
/**
 * Equality for `Nothing` values. Note that since there are not values of
 * type `Nothing` the `equals` method of this instance can never be called
 * but it can be useful in deriving instances for more complex types.
 */
exports.never = makeEqual(() => false);
/**
 * Constructs an `Equal[(A, B)]` given an `Equal[A]` and `Equal[B]` by first
 * comparing the `A` values for equality and then comparing the `B` values
 * for equality, if necessary.
 */
function both(fb) {
    return (fa) => makeEqual(({ tuple: [x0, x1] }, { tuple: [y0, y1] }) => fa.equals(x0, y0) && fb.equals(x1, y1));
}
exports.both = both;
/**
 * Constructs an `Equal[Either[A, B]]` given an `Equal[A]` and an
 * `Equal[B]`. The instance will compare the `Either[A, B]` values and if
 * both are `Right` or `Left` compare them for equality.
 */
function orElseEither(fb) {
    return (fa) => makeEqual((ex, ey) => ex._tag === "Left" && ey._tag === "Left"
        ? fa.equals(ex.left, ey.left)
        : ex._tag === "Right" && ey._tag === "Right"
            ? fb().equals(ex.right, ey.right)
            : false);
}
exports.orElseEither = orElseEither;
/**
 * Constructs an `Equal[B]` given an `Equal[A]` and a function `f` to
 * transform a `B` value into an `A` value. The instance will convert each
 * `B` value into an `A` and the compare the `A` values for equality.
 */
function contramap(f) {
    return (fa) => makeEqual((x, y) => fa.equals(f(x), f(y)));
}
exports.contramap = contramap;
/**
 * Constructs an `Equal[A]` that uses the default notion of equality
 * embodied in the implementation of `equals` for values of type `A`.
 */
function strict() {
    return makeEqual((x, y) => x === y);
}
exports.strict = strict;
/**
 * Equality for `number` values.
 */
exports.number = strict();
/**
 * Equality for `string` values.
 */
exports.string = strict();
/**
 * Equality for `symbol` values.
 */
exports.symbol = strict();
/**
 * Equality for `boolean` values.
 */
exports.boolean = strict();
/**
 * Equality for `Date` values.
 */
exports.date = contramap((date) => date.valueOf())(exports.number);
/**
 * Derives an `Equal[Array[A]]` given an `Equal[A]`.
 */
function array(EqA) {
    return {
        equals: (x, y) => {
            if (x.length === y.length) {
                for (let i = 0; i < x.length; i++) {
                    if (!EqA.equals(x[i], y[i])) {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    };
}
exports.array = array;
/**
 * Given a tuple of `Equal`s returns a `Equal` for the tuple
 */
function tuple(...eqs) {
    return makeEqual((x, y) => eqs.every((E, i) => E.equals(x.get(i), y.get(i))));
}
exports.tuple = tuple;
/**
 * Given a struct of `Equal`s returns a `Equal` for the struct
 */
function struct(eqs) {
    return makeEqual((x, y) => {
        for (const k in eqs) {
            if (!eqs[k].equals(x[k], y[k])) {
                return false;
            }
        }
        return true;
    });
}
exports.struct = struct;
//# sourceMappingURL=operations.js.map