"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.reduce = exports.reduce_ = exports.reduceWithIndex = exports.reduceWithIndex_ = exports.removeFirst = exports.removeFirst_ = exports.has = exports.has_ = exports.findFirst = exports.findFirst_ = exports.find = exports.find_ = exports.from = exports.keys = exports.keys_ = exports.values = exports.values_ = exports.backwards = exports.gt = exports.gt_ = exports.ge = exports.ge_ = exports.lt = exports.lt_ = exports.le = exports.le_ = exports.getAt = exports.getAt_ = exports.at = exports.at_ = exports.getLast = exports.getFirst = exports.RedBlackTreeIterator = exports.forEachBetween = exports.forEachBetween_ = exports.visitBetween = exports.forEachLt = exports.forEachLt_ = exports.visitLt = exports.forEachGe = exports.forEachGe_ = exports.visitGe = exports.forEach = exports.forEach_ = exports.visitFull = exports.insert = exports.insert_ = exports.size = exports.make = exports.RedBlackTree = void 0;
// ets_tracing: off
require("../../../Operator");
const Function_1 = require("../../../Function");
const I = __importStar(require("../../../Iterable"));
const O = __importStar(require("../../../Option"));
const Stack_1 = require("../../../Stack");
const St = __importStar(require("../../../Structural"));
const A = __importStar(require("../Array"));
const Tp = __importStar(require("../Tuple"));
class Node {
    constructor(color, key, value, left, right, count) {
        this.color = color;
        this.key = key;
        this.value = value;
        this.left = left;
        this.right = right;
        this.count = count;
    }
}
function cloneNode(node) {
    return new Node(node.color, node.key, node.value, node.left, node.right, node.count);
}
function swapNode(n, v) {
    n.key = v.key;
    n.value = v.value;
    n.left = v.left;
    n.right = v.right;
    n.color = v.color;
    n.count = v.count;
}
function repaintNode(node, color) {
    return new Node(color, node.key, node.value, node.left, node.right, node.count);
}
function recountNode(node) {
    var _a, _b, _c, _d;
    node.count = 1 + ((_b = (_a = node.left) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = node.right) === null || _c === void 0 ? void 0 : _c.count) !== null && _d !== void 0 ? _d : 0);
}
/**
 * A Red-Black Tree
 */
class RedBlackTree {
    constructor(ord, root) {
        this.ord = ord;
        this.root = root;
    }
    [Symbol.iterator]() {
        const stack = [];
        let n = this.root;
        while (n) {
            stack.push(n);
            n = n.left;
        }
        return new RedBlackTreeIterator(this, stack, "Forward");
    }
    get [St.hashSym]() {
        return St.hashIterator(this[Symbol.iterator]());
    }
    [St.equalsSym](that) {
        return (that instanceof RedBlackTree &&
            size(this) === size(that) &&
            I.corresponds(this, that, St.equals));
    }
}
exports.RedBlackTree = RedBlackTree;
/**
 * Creates a new Red-Black Tree
 */
function make(ord) {
    return new RedBlackTree(ord, undefined);
}
exports.make = make;
/**
 * Returns the length of the tree
 */
function size(self) {
    var _a, _b;
    return (_b = (_a = self.root) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0;
}
exports.size = size;
/**
 * Insert a new item into the tree
 */
function insert_(self, key, value) {
    const cmp = self.ord.compare;
    //Find point to insert new node at
    let n = self.root;
    const n_stack = [];
    const d_stack = [];
    while (n) {
        const d = cmp(key, n.key);
        n_stack.push(n);
        d_stack.push(d);
        if (d <= 0) {
            n = n.left;
        }
        else {
            n = n.right;
        }
    }
    //Rebuild path to leaf node
    n_stack.push(new Node("Red", key, value, undefined, undefined, 1));
    for (let s = n_stack.length - 2; s >= 0; --s) {
        const n2 = n_stack[s];
        if (d_stack[s] <= 0) {
            n_stack[s] = new Node(n2.color, n2.key, n2.value, n_stack[s + 1], n2.right, n2.count + 1);
        }
        else {
            n_stack[s] = new Node(n2.color, n2.key, n2.value, n2.left, n_stack[s + 1], n2.count + 1);
        }
    }
    //Rebalance tree using rotations
    for (let s = n_stack.length - 1; s > 1; --s) {
        const p = n_stack[s - 1];
        const n3 = n_stack[s];
        if (p.color === "Black" || n3.color === "Black") {
            break;
        }
        const pp = n_stack[s - 2];
        if (pp.left === p) {
            if (p.left === n3) {
                const y = pp.right;
                if (y && y.color === "Red") {
                    p.color = "Black";
                    pp.right = repaintNode(y, "Black");
                    pp.color = "Red";
                    s -= 1;
                }
                else {
                    pp.color = "Red";
                    pp.left = p.right;
                    p.color = "Black";
                    p.right = pp;
                    n_stack[s - 2] = p;
                    n_stack[s - 1] = n3;
                    recountNode(pp);
                    recountNode(p);
                    if (s >= 3) {
                        const ppp = n_stack[s - 3];
                        if (ppp.left === pp) {
                            ppp.left = p;
                        }
                        else {
                            ppp.right = p;
                        }
                    }
                    break;
                }
            }
            else {
                const y = pp.right;
                if (y && y.color === "Red") {
                    p.color = "Black";
                    pp.right = repaintNode(y, "Black");
                    pp.color = "Red";
                    s -= 1;
                }
                else {
                    p.right = n3.left;
                    pp.color = "Red";
                    pp.left = n3.right;
                    n3.color = "Black";
                    n3.left = p;
                    n3.right = pp;
                    n_stack[s - 2] = n3;
                    n_stack[s - 1] = p;
                    recountNode(pp);
                    recountNode(p);
                    recountNode(n3);
                    if (s >= 3) {
                        const ppp = n_stack[s - 3];
                        if (ppp.left === pp) {
                            ppp.left = n3;
                        }
                        else {
                            ppp.right = n3;
                        }
                    }
                    break;
                }
            }
        }
        else {
            if (p.right === n3) {
                const y = pp.left;
                if (y && y.color === "Red") {
                    p.color = "Black";
                    pp.left = repaintNode(y, "Black");
                    pp.color = "Red";
                    s -= 1;
                }
                else {
                    pp.color = "Red";
                    pp.right = p.left;
                    p.color = "Black";
                    p.left = pp;
                    n_stack[s - 2] = p;
                    n_stack[s - 1] = n3;
                    recountNode(pp);
                    recountNode(p);
                    if (s >= 3) {
                        const ppp = n_stack[s - 3];
                        if (ppp.right === pp) {
                            ppp.right = p;
                        }
                        else {
                            ppp.left = p;
                        }
                    }
                    break;
                }
            }
            else {
                const y = pp.left;
                if (y && y.color === "Red") {
                    p.color = "Black";
                    pp.left = repaintNode(y, "Black");
                    pp.color = "Red";
                    s -= 1;
                }
                else {
                    p.left = n3.right;
                    pp.color = "Red";
                    pp.right = n3.left;
                    n3.color = "Black";
                    n3.right = p;
                    n3.left = pp;
                    n_stack[s - 2] = n3;
                    n_stack[s - 1] = p;
                    recountNode(pp);
                    recountNode(p);
                    recountNode(n3);
                    if (s >= 3) {
                        const ppp = n_stack[s - 3];
                        if (ppp.right === pp) {
                            ppp.right = n3;
                        }
                        else {
                            ppp.left = n3;
                        }
                    }
                    break;
                }
            }
        }
    }
    //Return new tree
    n_stack[0].color = "Black";
    return new RedBlackTree(self.ord, n_stack[0]);
}
exports.insert_ = insert_;
/**
 * Insert a new item into the tree
 */
function insert(key, value) {
    return (self) => insert_(self, key, value);
}
exports.insert = insert;
/**
 * Visit all nodes inorder until a Some is returned
 */
function visitFull(node, visit) {
    let current = node;
    let stack = undefined;
    let done = false;
    while (!done) {
        if (current) {
            stack = new Stack_1.Stack(current, stack);
            current = current.left;
        }
        else if (stack) {
            const v = visit(stack.value.key, stack.value.value);
            if (O.isSome(v)) {
                return v;
            }
            current = stack.value.right;
            stack = stack.previous;
        }
        else {
            done = true;
        }
    }
    return O.none;
}
exports.visitFull = visitFull;
/**
 * Visit each node of the tree in order
 */
function forEach_(self, visit) {
    if (self.root) {
        visitFull(self.root, (key, value) => {
            visit(key, value);
            return O.none;
        });
    }
}
exports.forEach_ = forEach_;
/**
 * Visit each node of the tree in order
 */
function forEach(visit) {
    return (self) => forEach_(self, visit);
}
exports.forEach = forEach;
/**
 * Visit nodes greater than or equal to key
 */
function visitGe(node, min, ord, visit) {
    let current = node;
    let stack = undefined;
    let done = false;
    while (!done) {
        if (current) {
            stack = new Stack_1.Stack(current, stack);
            if (ord.compare(min, current.key) <= 0) {
                current = current.left;
            }
            else {
                current = undefined;
            }
        }
        else if (stack) {
            if (ord.compare(min, stack.value.key) <= 0) {
                const v = visit(stack.value.key, stack.value.value);
                if (O.isSome(v)) {
                    return v;
                }
            }
            current = stack.value.right;
            stack = stack.previous;
        }
        else {
            done = true;
        }
    }
    return O.none;
}
exports.visitGe = visitGe;
/**
 * Visit each node of the tree in order with key greater then or equal to max
 */
function forEachGe_(self, min, visit) {
    if (self.root) {
        visitGe(self.root, min, self.ord, (key, value) => {
            visit(key, value);
            return O.none;
        });
    }
}
exports.forEachGe_ = forEachGe_;
/**
 * Visit each node of the tree in order with key greater then or equal to max
 */
function forEachGe(min, visit) {
    return (self) => forEachGe_(self, min, visit);
}
exports.forEachGe = forEachGe;
/**
 * Visit nodes lower than key
 */
function visitLt(node, max, ord, visit) {
    let current = node;
    let stack = undefined;
    let done = false;
    while (!done) {
        if (current) {
            stack = new Stack_1.Stack(current, stack);
            current = current.left;
        }
        else if (stack && ord.compare(max, stack.value.key) > 0) {
            const v = visit(stack.value.key, stack.value.value);
            if (O.isSome(v)) {
                return v;
            }
            current = stack.value.right;
            stack = stack.previous;
        }
        else {
            done = true;
        }
    }
    return O.none;
}
exports.visitLt = visitLt;
/**
 * Visit each node of the tree in order with key lower then max
 */
function forEachLt_(self, max, visit) {
    if (self.root) {
        visitLt(self.root, max, self.ord, (key, value) => {
            visit(key, value);
            return O.none;
        });
    }
}
exports.forEachLt_ = forEachLt_;
/**
 * Visit each node of the tree in order with key lower then max
 */
function forEachLt(max, visit) {
    return (self) => forEachLt_(self, max, visit);
}
exports.forEachLt = forEachLt;
/**
 * Visit nodes with key lower than max and greater then or equal to min
 */
function visitBetween(node, min, max, ord, visit) {
    let current = node;
    let stack = undefined;
    let done = false;
    while (!done) {
        if (current) {
            stack = new Stack_1.Stack(current, stack);
            if (ord.compare(min, current.key) <= 0) {
                current = current.left;
            }
            else {
                current = undefined;
            }
        }
        else if (stack && ord.compare(max, stack.value.key) > 0) {
            if (ord.compare(min, stack.value.key) <= 0) {
                const v = visit(stack.value.key, stack.value.value);
                if (O.isSome(v)) {
                    return v;
                }
            }
            current = stack.value.right;
            stack = stack.previous;
        }
        else {
            done = true;
        }
    }
    return O.none;
}
exports.visitBetween = visitBetween;
/**
 * Visit each node of the tree in order with key lower than max and greater then or equal to min
 */
function forEachBetween_(self, min, max, visit) {
    if (self.root) {
        visitBetween(self.root, min, max, self.ord, (key, value) => {
            visit(key, value);
            return O.none;
        });
    }
}
exports.forEachBetween_ = forEachBetween_;
/**
 * Visit each node of the tree in order with key lower than max and greater then or equal to min
 */
function forEachBetween(min, max, visit) {
    return (self) => forEachBetween_(self, min, max, visit);
}
exports.forEachBetween = forEachBetween;
/**
 * Fix up a double black node in a tree
 */
function fixDoubleBlack(stack) {
    let n, p, s, z;
    for (let i = stack.length - 1; i >= 0; --i) {
        n = stack[i];
        if (i === 0) {
            n.color = "Black";
            return;
        }
        //console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key)
        p = stack[i - 1];
        if (p.left === n) {
            //console.log("left child")
            s = p.right;
            if (s && s.right && s.right.color === "Red") {
                //console.log("case 1: right sibling child red")
                s = p.right = cloneNode(s);
                z = s.right = cloneNode(s.right);
                p.right = s.left;
                s.left = p;
                s.right = z;
                s.color = p.color;
                n.color = "Black";
                p.color = "Black";
                z.color = "Black";
                recountNode(p);
                recountNode(s);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.left === p) {
                        pp.left = s;
                    }
                    else {
                        pp.right = s;
                    }
                }
                stack[i - 1] = s;
                return;
            }
            else if (s && s.left && s.left.color === "Red") {
                //console.log("case 1: left sibling child red")
                s = p.right = cloneNode(s);
                z = s.left = cloneNode(s.left);
                p.right = z.left;
                s.left = z.right;
                z.left = p;
                z.right = s;
                z.color = p.color;
                p.color = "Black";
                s.color = "Black";
                n.color = "Black";
                recountNode(p);
                recountNode(s);
                recountNode(z);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.left === p) {
                        pp.left = z;
                    }
                    else {
                        pp.right = z;
                    }
                }
                stack[i - 1] = z;
                return;
            }
            if (s && s.color === "Black") {
                if (p.color === "Red") {
                    //console.log("case 2: black sibling, red parent", p.right.value)
                    p.color = "Black";
                    p.right = repaintNode(s, "Red");
                    return;
                }
                else {
                    //console.log("case 2: black sibling, black parent", p.right.value)
                    p.right = repaintNode(s, "Red");
                    continue;
                }
            }
            else if (s) {
                //console.log("case 3: red sibling")
                s = cloneNode(s);
                p.right = s.left;
                s.left = p;
                s.color = p.color;
                p.color = "Red";
                recountNode(p);
                recountNode(s);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.left === p) {
                        pp.left = s;
                    }
                    else {
                        pp.right = s;
                    }
                }
                stack[i - 1] = s;
                stack[i] = p;
                if (i + 1 < stack.length) {
                    stack[i + 1] = n;
                }
                else {
                    stack.push(n);
                }
                i = i + 2;
            }
        }
        else {
            //console.log("right child")
            s = p.left;
            if (s && s.left && s.left.color === "Red") {
                //console.log("case 1: left sibling child red", p.value, p._color)
                s = p.left = cloneNode(s);
                z = s.left = cloneNode(s.left);
                p.left = s.right;
                s.right = p;
                s.left = z;
                s.color = p.color;
                n.color = "Black";
                p.color = "Black";
                z.color = "Black";
                recountNode(p);
                recountNode(s);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.right === p) {
                        pp.right = s;
                    }
                    else {
                        pp.left = s;
                    }
                }
                stack[i - 1] = s;
                return;
            }
            else if (s && s.right && s.right.color === "Red") {
                //console.log("case 1: right sibling child red")
                s = p.left = cloneNode(s);
                z = s.right = cloneNode(s.right);
                p.left = z.right;
                s.right = z.left;
                z.right = p;
                z.left = s;
                z.color = p.color;
                p.color = "Black";
                s.color = "Black";
                n.color = "Black";
                recountNode(p);
                recountNode(s);
                recountNode(z);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.right === p) {
                        pp.right = z;
                    }
                    else {
                        pp.left = z;
                    }
                }
                stack[i - 1] = z;
                return;
            }
            if (s && s.color === "Black") {
                if (p.color === "Red") {
                    //console.log("case 2: black sibling, red parent")
                    p.color = "Black";
                    p.left = repaintNode(s, "Red");
                    return;
                }
                else {
                    //console.log("case 2: black sibling, black parent")
                    p.left = repaintNode(s, "Red");
                    continue;
                }
            }
            else if (s) {
                //console.log("case 3: red sibling")
                s = cloneNode(s);
                p.left = s.right;
                s.right = p;
                s.color = p.color;
                p.color = "Red";
                recountNode(p);
                recountNode(s);
                if (i > 1) {
                    const pp = stack[i - 2];
                    if (pp.right === p) {
                        pp.right = s;
                    }
                    else {
                        pp.left = s;
                    }
                }
                stack[i - 1] = s;
                stack[i] = p;
                if (i + 1 < stack.length) {
                    stack[i + 1] = n;
                }
                else {
                    stack.push(n);
                }
                i = i + 2;
            }
        }
    }
}
/**
 * Stateful iterator
 */
class RedBlackTreeIterator {
    constructor(self, stack, direction) {
        this.self = self;
        this.stack = stack;
        this.direction = direction;
        this.count = 0;
    }
    /**
     * Clones the iterator
     */
    clone() {
        return new RedBlackTreeIterator(this.self, this.stack.slice(), this.direction);
    }
    /**
     * Reverse the traversal direction
     */
    reversed() {
        return new RedBlackTreeIterator(this.self, this.stack.slice(), this.direction === "Forward" ? "Backward" : "Forward");
    }
    /**
     * Iterator next
     */
    next() {
        const entry = this.entry;
        this.count++;
        if (this.direction === "Forward") {
            this.moveNext();
        }
        else {
            this.movePrev();
        }
        return O.fold_(entry, () => ({ done: true, value: this.count }), (kv) => ({ done: false, value: kv }));
    }
    /**
     * Returns the key
     */
    get key() {
        if (this.stack.length > 0) {
            return O.some(this.stack[this.stack.length - 1].key);
        }
        return O.none;
    }
    /**
     * Returns the value
     */
    get value() {
        if (this.stack.length > 0) {
            return O.some(this.stack[this.stack.length - 1].value);
        }
        return O.none;
    }
    /**
     * Returns the key
     */
    get entry() {
        if (this.stack.length > 0) {
            return O.some((0, Function_1.tuple)(this.stack[this.stack.length - 1].key, this.stack[this.stack.length - 1].value));
        }
        return O.none;
    }
    /**
     * Returns the position of this iterator in the sorted list
     */
    get index() {
        let idx = 0;
        const stack = this.stack;
        if (stack.length === 0) {
            const r = this.self.root;
            if (r) {
                return r.count;
            }
            return 0;
        }
        else if (stack[stack.length - 1].left) {
            idx = stack[stack.length - 1].left.count;
        }
        for (let s = stack.length - 2; s >= 0; --s) {
            if (stack[s + 1] === stack[s].right) {
                ++idx;
                if (stack[s].left) {
                    idx += stack[s].left.count;
                }
            }
        }
        return idx;
    }
    /**
     * Advances iterator to next element in list
     */
    moveNext() {
        const stack = this.stack;
        if (stack.length === 0) {
            return;
        }
        let n = stack[stack.length - 1];
        if (n.right) {
            n = n.right;
            while (n) {
                stack.push(n);
                n = n.left;
            }
        }
        else {
            stack.pop();
            while (stack.length > 0 && stack[stack.length - 1].right === n) {
                n = stack[stack.length - 1];
                stack.pop();
            }
        }
    }
    /**
     * Checks if there is a next element
     */
    get hasNext() {
        const stack = this.stack;
        if (stack.length === 0) {
            return false;
        }
        if (stack[stack.length - 1].right) {
            return true;
        }
        for (let s = stack.length - 1; s > 0; --s) {
            if (stack[s - 1].left === stack[s]) {
                return true;
            }
        }
        return false;
    }
    /**
     * Advances iterator to previous element in list
     */
    movePrev() {
        const stack = this.stack;
        if (stack.length === 0) {
            return;
        }
        let n = stack[stack.length - 1];
        if (n && n.left) {
            n = n.left;
            while (n) {
                stack.push(n);
                n = n.right;
            }
        }
        else {
            stack.pop();
            while (stack.length > 0 && stack[stack.length - 1].left === n) {
                n = stack[stack.length - 1];
                stack.pop();
            }
        }
    }
    /**
     * Checks if there is a previous element
     */
    get hasPrev() {
        const stack = this.stack;
        if (stack.length === 0) {
            return false;
        }
        if (stack[stack.length - 1].left) {
            return true;
        }
        for (let s = stack.length - 1; s > 0; --s) {
            if (stack[s - 1].right === stack[s]) {
                return true;
            }
        }
        return false;
    }
}
exports.RedBlackTreeIterator = RedBlackTreeIterator;
/**
 * Returns the first entry in the tree
 */
function getFirst(tree) {
    let n = tree.root;
    let c = tree.root;
    while (n) {
        c = n;
        n = n.left;
    }
    return c ? O.some(Tp.tuple(c.key, c.value)) : O.none;
}
exports.getFirst = getFirst;
/**
 * Returns the last entry in the tree
 */
function getLast(tree) {
    let n = tree.root;
    let c = tree.root;
    while (n) {
        c = n;
        n = n.right;
    }
    return c ? O.some(Tp.tuple(c.key, c.value)) : O.none;
}
exports.getLast = getLast;
/**
 * Returns an iterator that points to the element i of the tree
 */
function at_(tree, idx, direction = "Forward") {
    return {
        ord: tree.ord,
        [Symbol.iterator]: () => {
            if (idx < 0) {
                return new RedBlackTreeIterator(tree, [], direction);
            }
            let n = tree.root;
            const stack = [];
            while (n) {
                stack.push(n);
                if (n.left) {
                    if (idx < n.left.count) {
                        n = n.left;
                        continue;
                    }
                    idx -= n.left.count;
                }
                if (!idx) {
                    return new RedBlackTreeIterator(tree, stack, direction);
                }
                idx -= 1;
                if (n.right) {
                    if (idx >= n.right.count) {
                        break;
                    }
                    n = n.right;
                }
                else {
                    break;
                }
            }
            return new RedBlackTreeIterator(tree, [], direction);
        }
    };
}
exports.at_ = at_;
/**
 * Returns an iterator that points to the element i of the tree
 */
function at(idx) {
    return (tree) => at_(tree, idx);
}
exports.at = at;
/**
 * Returns the element i of the tree
 */
function getAt_(tree, idx) {
    if (idx < 0) {
        return O.none;
    }
    let n = tree.root;
    let node = undefined;
    while (n) {
        node = n;
        if (n.left) {
            if (idx < n.left.count) {
                n = n.left;
                continue;
            }
            idx -= n.left.count;
        }
        if (!idx) {
            return O.some(Tp.tuple(node.key, node.value));
        }
        idx -= 1;
        if (n.right) {
            if (idx >= n.right.count) {
                break;
            }
            n = n.right;
        }
        else {
            break;
        }
    }
    return O.none;
}
exports.getAt_ = getAt_;
/**
 * Returns the element i of the tree
 */
function getAt(idx) {
    return (tree) => getAt_(tree, idx);
}
exports.getAt = getAt;
/**
 * Returns an iterator that traverse entries with keys less then or equal to key
 */
function le_(tree, key, direction = "Forward") {
    return {
        ord: tree.ord,
        [Symbol.iterator]: () => {
            const cmp = tree.ord.compare;
            let n = tree.root;
            const stack = [];
            let last_ptr = 0;
            while (n) {
                const d = cmp(key, n.key);
                stack.push(n);
                if (d <= 0) {
                    last_ptr = stack.length;
                }
                if (d <= 0) {
                    n = n.left;
                }
                else {
                    n = n.right;
                }
            }
            stack.length = last_ptr;
            return new RedBlackTreeIterator(tree, stack, direction);
        }
    };
}
exports.le_ = le_;
/**
 * Returns an iterator that traverse entries with keys less then or equal to key
 */
function le(key, direction = "Forward") {
    return (tree) => le_(tree, key, direction);
}
exports.le = le;
/**
 * Returns an iterator that traverse entries with keys less then key
 */
function lt_(tree, key, direction = "Forward") {
    return {
        ord: tree.ord,
        [Symbol.iterator]: () => {
            const cmp = tree.ord.compare;
            let n = tree.root;
            const stack = [];
            let last_ptr = 0;
            while (n) {
                const d = cmp(key, n.key);
                stack.push(n);
                if (d > 0) {
                    last_ptr = stack.length;
                }
                if (d <= 0) {
                    n = n.left;
                }
                else {
                    n = n.right;
                }
            }
            stack.length = last_ptr;
            return new RedBlackTreeIterator(tree, stack, direction);
        }
    };
}
exports.lt_ = lt_;
/**
 * Returns an iterator that traverse entries with keys less then key
 */
function lt(key, direction = "Forward") {
    return (tree) => lt_(tree, key, direction);
}
exports.lt = lt;
/**
 * Returns an iterator that traverse entries with keys greater then or equal to key
 */
function ge_(tree, key, direction = "Forward") {
    return {
        ord: tree.ord,
        [Symbol.iterator]: () => {
            const cmp = tree.ord.compare;
            let n = tree.root;
            const stack = [];
            let last_ptr = 0;
            while (n) {
                const d = cmp(key, n.key);
                stack.push(n);
                if (d <= 0) {
                    last_ptr = stack.length;
                }
                if (d <= 0) {
                    n = n.left;
                }
                else {
                    n = n.right;
                }
            }
            stack.length = last_ptr;
            return new RedBlackTreeIterator(tree, stack, direction);
        }
    };
}
exports.ge_ = ge_;
/**
 * Returns an iterator that traverse entries with keys greater then or equal to key
 */
function ge(key, direction = "Forward") {
    return (tree) => ge_(tree, key, direction);
}
exports.ge = ge;
/**
 * Returns an iterator that traverse entries with keys greater then or equal to key
 */
function gt_(tree, key, direction = "Forward") {
    return {
        ord: tree.ord,
        [Symbol.iterator]: () => {
            const cmp = tree.ord.compare;
            let n = tree.root;
            const stack = [];
            let last_ptr = 0;
            while (n) {
                const d = cmp(key, n.key);
                stack.push(n);
                if (d < 0) {
                    last_ptr = stack.length;
                }
                if (d < 0) {
                    n = n.left;
                }
                else {
                    n = n.right;
                }
            }
            stack.length = last_ptr;
            return new RedBlackTreeIterator(tree, stack, direction);
        }
    };
}
exports.gt_ = gt_;
/**
 * Returns an iterator that traverse entries with keys greater then or equal to key
 */
function gt(key, direction = "Forward") {
    return (tree) => gt_(tree, key, direction);
}
exports.gt = gt;
/**
 * Traverse the tree backwards
 */
function backwards(self) {
    return {
        ord: self.ord,
        [Symbol.iterator]: () => {
            const stack = [];
            let n = self.root;
            while (n) {
                stack.push(n);
                n = n.right;
            }
            return new RedBlackTreeIterator(self, stack, "Backward");
        }
    };
}
exports.backwards = backwards;
/**
 * Get the values of the tree
 */
function values_(self, direction = "Forward") {
    const begin = self[Symbol.iterator]();
    let count = 0;
    return {
        [Symbol.iterator]: () => values_(self, direction),
        next: () => {
            count++;
            const entry = begin.value;
            if (direction === "Forward") {
                begin.moveNext();
            }
            else {
                begin.movePrev();
            }
            return O.fold_(entry, () => ({ value: count, done: true }), (entry) => ({ value: entry, done: false }));
        }
    };
}
exports.values_ = values_;
/**
 * Get the values of the tree
 */
function values(direction = "Forward") {
    return (self) => values_(self, direction);
}
exports.values = values;
/**
 * Get the keys of the tree
 */
function keys_(self, direction = "Forward") {
    const begin = self[Symbol.iterator]();
    let count = 0;
    return {
        [Symbol.iterator]: () => keys_(self, direction),
        next: () => {
            count++;
            const entry = begin.key;
            if (direction === "Forward") {
                begin.moveNext();
            }
            else {
                begin.movePrev();
            }
            return O.fold_(entry, () => ({ value: count, done: true }), (entry) => ({ value: entry, done: false }));
        }
    };
}
exports.keys_ = keys_;
/**
 * Get the keys of the tree
 */
function keys(direction = "Forward") {
    return (self) => keys_(self, direction);
}
exports.keys = keys;
function from(...args) {
    let tree = args.length === 2 ? make(args[1]) : make(args[0].ord);
    for (const [k, v] of args[0]) {
        tree = insert_(tree, k, v);
    }
    return tree;
}
exports.from = from;
/**
 * Finds the item with key if it exists
 */
function find_(tree, key) {
    const cmp = tree.ord.compare;
    let n = tree.root;
    const res = [];
    while (n) {
        const d = cmp(key, n.key);
        if (d === 0 && St.equals(key, n.key)) {
            res.push(n.value);
        }
        if (d <= 0) {
            n = n.left;
        }
        else {
            n = n.right;
        }
    }
    return A.reverse(res);
}
exports.find_ = find_;
/**
 * Finds the item with key if it exists
 */
function find(key) {
    return (tree) => find_(tree, key);
}
exports.find = find;
/**
 * Finds the item with key if it exists
 */
function findFirst_(tree, key) {
    const cmp = tree.ord.compare;
    let n = tree.root;
    while (n) {
        const d = cmp(key, n.key);
        if (St.equals(key, n.key)) {
            return O.some(n.value);
        }
        if (d <= 0) {
            n = n.left;
        }
        else {
            n = n.right;
        }
    }
    return O.none;
}
exports.findFirst_ = findFirst_;
/**
 * Finds the item with key if it exists
 */
function findFirst(key) {
    return (tree) => findFirst_(tree, key);
}
exports.findFirst = findFirst;
/**
 * Finds the item with key if it exists
 */
function has_(tree, key) {
    return findFirst_(tree, key)._tag === "Some";
}
exports.has_ = has_;
/**
 * Finds the item with key if it exists
 */
function has(key) {
    return (tree) => has_(tree, key);
}
exports.has = has;
/**
 * Removes entry with key
 */
function removeFirst_(self, key) {
    const cmp = self.ord.compare;
    let node = self.root;
    const stack = [];
    while (node) {
        const d = cmp(key, node.key);
        stack.push(node);
        if (St.equals(key, node.key)) {
            node = undefined;
        }
        else if (d <= 0) {
            node = node.left;
        }
        else {
            node = node.right;
        }
    }
    if (stack.length === 0) {
        return self;
    }
    const cstack = new Array(stack.length);
    let n = stack[stack.length - 1];
    cstack[cstack.length - 1] = new Node(n.color, n.key, n.value, n.left, n.right, n.count);
    for (let i = stack.length - 2; i >= 0; --i) {
        n = stack[i];
        if (n.left === stack[i + 1]) {
            cstack[i] = new Node(n.color, n.key, n.value, cstack[i + 1], n.right, n.count);
        }
        else {
            cstack[i] = new Node(n.color, n.key, n.value, n.left, cstack[i + 1], n.count);
        }
    }
    //Get node
    n = cstack[cstack.length - 1];
    //console.log("start remove: ", n.value)
    //If not leaf, then swap with previous node
    if (n.left && n.right) {
        //console.log("moving to leaf")
        //First walk to previous leaf
        const split = cstack.length;
        n = n.left;
        while (n.right) {
            cstack.push(n);
            n = n.right;
        }
        //Copy path to leaf
        const v = cstack[split - 1];
        cstack.push(new Node(n.color, v.key, v.value, n.left, n.right, n.count));
        cstack[split - 1].key = n.key;
        cstack[split - 1].value = n.value;
        //Fix up stack
        for (let i = cstack.length - 2; i >= split; --i) {
            n = cstack[i];
            cstack[i] = new Node(n.color, n.key, n.value, n.left, cstack[i + 1], n.count);
        }
        cstack[split - 1].left = cstack[split];
    }
    //console.log("stack=", cstack.map(function(v) { return v.value }))
    //Remove leaf node
    n = cstack[cstack.length - 1];
    if (n.color === "Red") {
        //Easy case: removing red leaf
        //console.log("RED leaf")
        const p = cstack[cstack.length - 2];
        if (p.left === n) {
            p.left = null;
        }
        else if (p.right === n) {
            p.right = null;
        }
        cstack.pop();
        for (let i = 0; i < cstack.length; ++i) {
            cstack[i]._count--;
        }
        return new RedBlackTree(self.ord, cstack[0]);
    }
    else {
        if (n.left || n.right) {
            //Second easy case:  Single child black parent
            //console.log("BLACK single child")
            if (n.left) {
                swapNode(n, n.left);
            }
            else if (n.right) {
                swapNode(n, n.right);
            }
            //Child must be red, so repaint it black to balance color
            n.color = "Black";
            for (let i = 0; i < cstack.length - 1; ++i) {
                cstack[i]._count--;
            }
            return new RedBlackTree(self.ord, cstack[0]);
        }
        else if (cstack.length === 1) {
            //Third easy case: root
            //console.log("ROOT")
            return new RedBlackTree(self.ord, undefined);
        }
        else {
            //Hard case: Repaint n, and then do some nasty stuff
            //console.log("BLACK leaf no children")
            for (let i = 0; i < cstack.length; ++i) {
                cstack[i]._count--;
            }
            const parent = cstack[cstack.length - 2];
            fixDoubleBlack(cstack);
            //Fix up links
            if (parent.left === n) {
                parent.left = null;
            }
            else {
                parent.right = null;
            }
        }
    }
    return new RedBlackTree(self.ord, cstack[0]);
}
exports.removeFirst_ = removeFirst_;
/**
 * Removes entry with key
 */
function removeFirst(key) {
    return (tree) => removeFirst_(tree, key);
}
exports.removeFirst = removeFirst;
/**
 * Reduce a state over the map entries
 */
function reduceWithIndex_(map, z, f) {
    let x = z;
    for (const [k, v] of map) {
        x = f(x, k, v);
    }
    return x;
}
exports.reduceWithIndex_ = reduceWithIndex_;
/**
 * Reduce a state over the map entries
 *
 * @ets_data_first reduceWithIndex_
 */
function reduceWithIndex(z, f) {
    return (map) => reduceWithIndex_(map, z, f);
}
exports.reduceWithIndex = reduceWithIndex;
/**
 * Reduce a state over the map entries
 */
function reduce_(map, z, f) {
    return reduceWithIndex_(map, z, (z1, _, v) => f(z1, v));
}
exports.reduce_ = reduce_;
/**
 * Reduce a state over the map entries
 *
 * @ets_data_first reduceWithIndex_
 */
function reduce(z, f) {
    return (map) => reduce_(map, z, f);
}
exports.reduce = reduce;
//# sourceMappingURL=index.js.map