// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
/** This module is browser compatible. */ import { ascend } from "./_comparators.ts";
import { BinarySearchNode } from "./binary_search_node.ts";
export * from "./_comparators.ts";
/**
 * An unbalanced binary search tree. The values are in ascending order by default,
 * using JavaScript's built in comparison operators to sort the values.
 */ export class BinarySearchTree {
    root;
    _size;
    constructor(compare = ascend){
        this.compare = compare;
        this.root = null;
        this._size = 0;
    }
    static from(collection, options) {
        let result;
        let unmappedValues = [];
        if (collection instanceof BinarySearchTree) {
            result = new BinarySearchTree(options?.compare ?? collection.compare);
            if (options?.compare || options?.map) {
                unmappedValues = collection;
            } else {
                const nodes = [];
                if (collection.root) {
                    result.root = BinarySearchNode.from(collection.root);
                    nodes.push(result.root);
                }
                while(nodes.length){
                    const node = nodes.pop();
                    const left = node.left ? BinarySearchNode.from(node.left) : null;
                    const right = node.right ? BinarySearchNode.from(node.right) : null;
                    if (left) {
                        left.parent = node;
                        nodes.push(left);
                    }
                    if (right) {
                        right.parent = node;
                        nodes.push(right);
                    }
                }
            }
        } else {
            result = options?.compare ? new BinarySearchTree(options.compare) : new BinarySearchTree();
            unmappedValues = collection;
        }
        const values = options?.map ? Array.from(unmappedValues, options.map, options.thisArg) : unmappedValues;
        for (const value of values)result.insert(value);
        return result;
    }
    /** The amount of values stored in the binary search tree. */ get size() {
        return this._size;
    }
    findNode(value) {
        let node = this.root;
        while(node){
            const order = this.compare(value, node.value);
            if (order === 0) break;
            const direction = order < 0 ? "left" : "right";
            node = node[direction];
        }
        return node;
    }
    rotateNode(node, direction) {
        const replacementDirection = direction === "left" ? "right" : "left";
        if (!node[replacementDirection]) {
            throw new TypeError(`cannot rotate ${direction} without ${replacementDirection} child`);
        }
        const replacement = node[replacementDirection];
        node[replacementDirection] = replacement[direction] ?? null;
        if (replacement[direction]) replacement[direction].parent = node;
        replacement.parent = node.parent;
        if (node.parent) {
            const parentDirection = node === node.parent[direction] ? direction : replacementDirection;
            node.parent[parentDirection] = replacement;
        } else {
            this.root = replacement;
        }
        replacement[direction] = node;
        node.parent = replacement;
    }
    insertNode(Node, value) {
        if (!this.root) {
            this.root = new Node(null, value);
            this._size++;
            return this.root;
        } else {
            let node = this.root;
            while(true){
                const order = this.compare(value, node.value);
                if (order === 0) break;
                const direction = order < 0 ? "left" : "right";
                if (node[direction]) {
                    node = node[direction];
                } else {
                    node[direction] = new Node(node, value);
                    this._size++;
                    return node[direction];
                }
            }
        }
        return null;
    }
    removeNode(value) {
        let removeNode = this.findNode(value);
        if (removeNode) {
            const successorNode = !removeNode.left || !removeNode.right ? removeNode : removeNode.findSuccessorNode();
            const replacementNode = successorNode.left ?? successorNode.right;
            if (replacementNode) replacementNode.parent = successorNode.parent;
            if (!successorNode.parent) {
                this.root = replacementNode;
            } else {
                successorNode.parent[successorNode.directionFromParent()] = replacementNode;
            }
            if (successorNode !== removeNode) {
                removeNode.value = successorNode.value;
                removeNode = successorNode;
            }
            this._size--;
        }
        return removeNode;
    }
    /**
   * Adds the value to the binary search tree if it does not already exist in it.
   * Returns true if successful.
   */ insert(value) {
        return !!this.insertNode(BinarySearchNode, value);
    }
    /**
   * Removes node value from the binary search tree if found.
   * Returns true if found and removed.
   */ remove(value) {
        return !!this.removeNode(value);
    }
    /** Returns node value if found in the binary search tree. */ find(value) {
        return this.findNode(value)?.value ?? null;
    }
    /** Returns the minimum value in the binary search tree or null if empty. */ min() {
        return this.root ? this.root.findMinNode().value : null;
    }
    /** Returns the maximum value in the binary search tree or null if empty. */ max() {
        return this.root ? this.root.findMaxNode().value : null;
    }
    /** Removes all values from the binary search tree. */ clear() {
        this.root = null;
        this._size = 0;
    }
    /** Checks if the binary search tree is empty. */ isEmpty() {
        return this.size === 0;
    }
    /**
   * Returns an iterator that uses in-order (LNR) tree traversal for
   * retrieving values from the binary search tree.
   */ *lnrValues() {
        const nodes = [];
        let node = this.root;
        while(nodes.length || node){
            if (node) {
                nodes.push(node);
                node = node.left;
            } else {
                node = nodes.pop();
                yield node.value;
                node = node.right;
            }
        }
    }
    /**
   * Returns an iterator that uses reverse in-order (RNL) tree traversal for
   * retrieving values from the binary search tree.
   */ *rnlValues() {
        const nodes = [];
        let node = this.root;
        while(nodes.length || node){
            if (node) {
                nodes.push(node);
                node = node.right;
            } else {
                node = nodes.pop();
                yield node.value;
                node = node.left;
            }
        }
    }
    /**
   * Returns an iterator that uses pre-order (NLR) tree traversal for
   * retrieving values from the binary search tree.
   */ *nlrValues() {
        const nodes = [];
        if (this.root) nodes.push(this.root);
        while(nodes.length){
            const node = nodes.pop();
            yield node.value;
            if (node.right) nodes.push(node.right);
            if (node.left) nodes.push(node.left);
        }
    }
    /**
   * Returns an iterator that uses post-order (LRN) tree traversal for
   * retrieving values from the binary search tree.
   */ *lrnValues() {
        const nodes = [];
        let node = this.root;
        let lastNodeVisited = null;
        while(nodes.length || node){
            if (node) {
                nodes.push(node);
                node = node.left;
            } else {
                const lastNode = nodes[nodes.length - 1];
                if (lastNode.right && lastNode.right !== lastNodeVisited) {
                    node = lastNode.right;
                } else {
                    yield lastNode.value;
                    lastNodeVisited = nodes.pop();
                }
            }
        }
    }
    /**
   * Returns an iterator that uses level order tree traversal for
   * retrieving values from the binary search tree.
   */ *lvlValues() {
        const children = [];
        let cursor = this.root;
        while(cursor){
            yield cursor.value;
            if (cursor.left) children.push(cursor.left);
            if (cursor.right) children.push(cursor.right);
            cursor = children.shift() ?? null;
        }
    }
    /**
   * Returns an iterator that uses in-order (LNR) tree traversal for
   * retrieving values from the binary search tree.
   */ *[Symbol.iterator]() {
        yield* this.lnrValues();
    }
    compare;
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 23876 characters]