"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * @file The free group generated by elements of `A`, up to equality. Note that the `Eq` and `Monoid` instances differ
 * from the standard such instances for `Array<Either<A, A>>`; two elements of the free group are equal iff they are equal
 * after being reduced to "canonical form", i.e., cancelling adjacent inverses.
 *
 * Adapted from https://hackage.haskell.org/package/free-algebras-0.0.7.0/docs/Data-Group-Free.html
 */
var Array_1 = require("./Array");
var Either_1 = require("./Either");
var Eq_1 = require("./Eq");
exports.URI = 'FreeGroup';
/**
 * @since 1.13.0
 */
var FreeGroup = /** @class */ (function () {
    function FreeGroup(value) {
        this.value = value;
    }
    FreeGroup.prototype.map = function (f) {
        return new FreeGroup(this.value.map(function (e) { return e.bimap(f, f); }));
    };
    FreeGroup.prototype.ap = function (fab) {
        var _this = this;
        return fab.chain(function (f) { return _this.map(f); }); // <- derived
    };
    FreeGroup.prototype.ap_ = function (fb) {
        return fb.ap(this);
    };
    FreeGroup.prototype.chain = function (f) {
        return new FreeGroup(Array_1.array.chain(this.value, function (e) { return e.bimap(f, f).value.value; }));
    };
    return FreeGroup;
}());
exports.FreeGroup = FreeGroup;
var of = function (a) {
    return new FreeGroup([Either_1.right(a)]);
};
var map = function (fa, f) {
    return fa.map(f);
};
var ap = function (fab, fa) {
    return fa.ap(fab);
};
var chain = function (fa, f) {
    return fa.chain(f);
};
/**
 * Smart constructor which normalizes an array
 *
 * @since 1.13.0
 */
exports.fromArray = function (E) {
    var normalizeS = exports.normalize(E);
    return function (as) { return new FreeGroup(normalizeS(as)); };
};
/**
 * Reduce a term of a free group to canonical form, i.e. cancelling adjacent inverses.
 *
 * @since 1.13.0
 */
exports.normalize = function (E) { return function (g) {
    return g.reduceRight(function (acc, s) {
        if (acc.length > 0) {
            var head = acc[0];
            var tail = acc.slice(1);
            if (head._tag !== s._tag && E.equals(head.value, s.value)) {
                return tail;
            }
        }
        acc.unshift(s);
        return acc;
    }, []);
}; };
/**
 * Use `getEq`
 *
 * @since 1.13.0
 * @deprecated
 */
exports.getSetoid = getEq;
/**
 * @since 1.19.0
 */
function getEq(S) {
    var AS = Array_1.getEq(Either_1.getEq(S, S));
    var normalizeS = exports.normalize(S);
    return Eq_1.fromEquals(function (x, y) { return AS.equals(normalizeS(x.value), normalizeS(y.value)); });
}
exports.getEq = getEq;
/**
 * @since 1.13.0
 */
exports.empty = new FreeGroup(Array_1.empty);
/**
 * @since 1.13.0
 */
exports.getGroup = function (E) {
    var M = Array_1.getMonoid();
    var normalizeS = exports.normalize(E);
    return {
        concat: function (x, y) { return new FreeGroup(normalizeS(M.concat(x.value, y.value))); },
        empty: exports.empty,
        inverse: function (x) { return new FreeGroup(x.value.reverse().map(function (s) { return (s.isLeft() ? Either_1.right(s.value) : Either_1.left(s.value)); })); }
    };
};
/**
 * @since 1.13.0
 */
exports.freeGroup = {
    URI: exports.URI,
    of: of,
    map: map,
    ap: ap,
    chain: chain
};
