(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define('dag-map', ['exports'], factory) :
    (factory((global.DAG = global.DAG || {})));
}(this, (function (exports) { 'use strict';

/**
 * A topologically ordered map of key/value pairs with a simple API for adding constraints.
 *
 * Edges can forward reference keys that have not been added yet (the forward reference will
 * map the key to undefined).
 */
var DAG = (function () {
    function DAG() {
        this._vertices = new Vertices();
    }
    /**
     * Adds a key/value pair with dependencies on other key/value pairs.
     *
     * @public
     * @param key    The key of the vertex to be added.
     * @param value  The value of that vertex.
     * @param before A key or array of keys of the vertices that must
     *               be visited before this vertex.
     * @param after  An string or array of strings with the keys of the
     *               vertices that must be after this vertex is visited.
     */
    DAG.prototype.add = function (key, value, before, after) {
        if (!key)
            throw new Error('argument `key` is required');
        var vertices = this._vertices;
        var v = vertices.add(key);
        v.val = value;
        if (before) {
            if (typeof before === "string") {
                vertices.addEdge(v, vertices.add(before));
            }
            else {
                for (var i = 0; i < before.length; i++) {
                    vertices.addEdge(v, vertices.add(before[i]));
                }
            }
        }
        if (after) {
            if (typeof after === "string") {
                vertices.addEdge(vertices.add(after), v);
            }
            else {
                for (var i = 0; i < after.length; i++) {
                    vertices.addEdge(vertices.add(after[i]), v);
                }
            }
        }
    };
    /**
     * @deprecated please use add.
     */
    DAG.prototype.addEdges = function (key, value, before, after) {
        this.add(key, value, before, after);
    };
    /**
     * Visits key/value pairs in topological order.
     *
     * @public
     * @param callback The function to be invoked with each key/value.
     */
    DAG.prototype.each = function (callback) {
        this._vertices.walk(callback);
    };
    /**
     * @deprecated please use each.
     */
    DAG.prototype.topsort = function (callback) {
        this.each(callback);
    };
    return DAG;
}());
/** @private */
var Vertices = (function () {
    function Vertices() {
        this.length = 0;
        this.stack = new IntStack();
        this.path = new IntStack();
        this.result = new IntStack();
    }
    Vertices.prototype.add = function (key) {
        if (!key)
            throw new Error("missing key");
        var l = this.length | 0;
        var vertex;
        for (var i = 0; i < l; i++) {
            vertex = this[i];
            if (vertex.key === key)
                return vertex;
        }
        this.length = l + 1;
        return this[l] = {
            idx: l,
            key: key,
            val: undefined,
            out: false,
            flag: false,
            length: 0
        };
    };
    Vertices.prototype.addEdge = function (v, w) {
        this.check(v, w.key);
        var l = w.length | 0;
        for (var i = 0; i < l; i++) {
            if (w[i] === v.idx)
                return;
        }
        w.length = l + 1;
        w[l] = v.idx;
        v.out = true;
    };
    Vertices.prototype.walk = function (cb) {
        this.reset();
        for (var i = 0; i < this.length; i++) {
            var vertex = this[i];
            if (vertex.out)
                continue;
            this.visit(vertex, "");
        }
        this.each(this.result, cb);
    };
    Vertices.prototype.check = function (v, w) {
        if (v.key === w) {
            throw new Error("cycle detected: " + w + " <- " + w);
        }
        // quick check
        if (v.length === 0)
            return;
        // shallow check
        for (var i = 0; i < v.length; i++) {
            var key = this[v[i]].key;
            if (key === w) {
                throw new Error("cycle detected: " + w + " <- " + v.key + " <- " + w);
            }
        }
        // deep check
        this.reset();
        this.visit(v, w);
        if (this.path.length > 0) {
            var msg_1 = "cycle detected: " + w;
            this.each(this.path, function (key) {
                msg_1 += " <- " + key;
            });
            throw new Error(msg_1);
        }
    };
    Vertices.prototype.reset = function () {
        this.stack.length = 0;
        this.path.length = 0;
        this.result.length = 0;
        for (var i = 0, l = this.length; i < l; i++) {
            this[i].flag = false;
        }
    };
    Vertices.prototype.visit = function (start, search) {
        var _a = this, stack = _a.stack, path = _a.path, result = _a.result;
        stack.push(start.idx);
        while (stack.length) {
            var index = stack.pop() | 0;
            if (index >= 0) {
                // enter
                var vertex = this[index];
                if (vertex.flag)
                    continue;
                vertex.flag = true;
                path.push(index);
                if (search === vertex.key)
                    break;
                // push exit
                stack.push(~index);
                this.pushIncoming(vertex);
            }
            else {
                // exit
                path.pop();
                result.push(~index);
            }
        }
    };
    Vertices.prototype.pushIncoming = function (incomming) {
        var stack = this.stack;
        for (var i = incomming.length - 1; i >= 0; i--) {
            var index = incomming[i];
            if (!this[index].flag) {
                stack.push(index);
            }
        }
    };
    Vertices.prototype.each = function (indices, cb) {
        for (var i = 0, l = indices.length; i < l; i++) {
            var vertex = this[indices[i]];
            cb(vertex.key, vertex.val);
        }
    };
    return Vertices;
}());
/** @private */
var IntStack = (function () {
    function IntStack() {
        this.length = 0;
    }
    IntStack.prototype.push = function (n) {
        this[this.length++] = n | 0;
    };
    IntStack.prototype.pop = function () {
        return this[--this.length] | 0;
    };
    return IntStack;
}());

exports['default'] = DAG;

Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=dag-map.umd.js.map
