"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findRoot = exports.setHeapNode = exports.asyncAddHeapNode = exports.addHeapNode = exports.asyncBubbleUp = exports.bubbleUp = exports.asyncBubbleDown = exports.bubbleDown = exports.asyncRemoveHeapNode = exports.removeHeapNode = exports.readHeap = exports.parentHeap = exports.asyncToSortedArray = exports.toSortedArray = exports.toArray = exports.traverseHeap = exports.findLastLeaf = exports.findInsertionPoint = exports.bffHeap = void 0;
function bffHeap(heap, callback, queue = []) {
    queue.push(heap);
    while (queue.length) {
        const node = queue.shift();
        callback(node);
        node.left && queue.push(node.left);
        node.right && queue.push(node.right);
    }
}
exports.bffHeap = bffHeap;
function findInsertionPoint(heap) {
    let result = null;
    bffHeap(heap, (node) => {
        if (!node.left || !node.right) {
            if (!result) {
                result = node;
            }
        }
    });
    return result;
}
exports.findInsertionPoint = findInsertionPoint;
function findLastLeaf(heap) {
    let result = null;
    bffHeap(heap, (node) => {
        result = node;
    });
    return result;
}
exports.findLastLeaf = findLastLeaf;
function traverseHeap(heap, callback) {
    const { value, left, right } = heap;
    callback(value);
    left && traverseHeap(left, callback);
    right && traverseHeap(right, callback);
}
exports.traverseHeap = traverseHeap;
function toArray(heap) {
    const array = [];
    traverseHeap(heap, (value) => {
        if (value !== undefined) {
            array.push(value);
        }
    });
    return array;
}
exports.toArray = toArray;
function toSortedArray(heap, predicate) {
    const array = [];
    do {
        array.push(heap.value);
        const lastLeaf = findLastLeaf(heap);
        heap = removeHeapNode(lastLeaf, predicate);
        if (heap) {
            heap.value = lastLeaf.value;
            bubbleDown(heap, predicate);
        }
        heap = heap && findRoot(heap);
    } while (heap);
    return array;
}
exports.toSortedArray = toSortedArray;
function asyncToSortedArray(heap, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        const array = [];
        do {
            array.push(heap.value);
            const lastLeaf = findLastLeaf(heap);
            heap = yield asyncRemoveHeapNode(lastLeaf, predicate);
            if (heap) {
                heap.value = lastLeaf.value;
                yield asyncBubbleDown(heap, predicate);
            }
            heap = heap && findRoot(heap);
        } while (heap);
        return array;
    });
}
exports.asyncToSortedArray = asyncToSortedArray;
function parentHeap(heap, parent = null) {
    const { value, left, right } = heap;
    heap.parent = parent;
    if (left) {
        parentHeap(left, heap);
    }
    if (right) {
        parentHeap(right, heap);
    }
    return heap;
}
exports.parentHeap = parentHeap;
function readHeap(heap) {
    const { value, left, right } = heap;
    const _heap = {
        value,
    };
    if (left) {
        const _child = readHeap(left);
        _heap.left = _child;
    }
    if (right) {
        const _child = readHeap(right);
        _heap.right = _child;
    }
    return _heap;
}
exports.readHeap = readHeap;
function removeHeapNode(heap, predicate) {
    // console.log('Graph', 'removeHeapNode', heap)
    const { parent, left, right } = heap;
    const root = findRoot(heap);
    const lastLeaf = findLastLeaf(root);
    if (lastLeaf.parent) {
        const isLastLeafLeft = lastLeaf.parent.left === lastLeaf;
        if (isLastLeafLeft) {
            lastLeaf.parent.left = null;
        }
        else {
            lastLeaf.parent.right = null;
        }
    }
    heap.parent = null;
    heap.left = null;
    heap.right = null;
    if (lastLeaf === root) {
        return null;
    }
    if (lastLeaf === heap) {
        return (parent && findRoot(parent)) || null;
    }
    lastLeaf.parent = parent;
    if (parent) {
        const isLeft = parent.left === heap;
        if (isLeft) {
            parent.left = lastLeaf;
        }
        else {
            parent.right = lastLeaf;
        }
        bubbleUp(lastLeaf, predicate);
    }
    else {
        if (lastLeaf !== left) {
            lastLeaf.left = left;
            left && (left.parent = lastLeaf);
        }
        if (lastLeaf !== right) {
            lastLeaf.right = right;
            right && (right.parent = lastLeaf);
        }
        bubbleDown(lastLeaf, predicate);
    }
    return findRoot(lastLeaf);
}
exports.removeHeapNode = removeHeapNode;
function asyncRemoveHeapNode(heap, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        const { parent, left, right } = heap;
        const root = findRoot(heap);
        const lastLeaf = findLastLeaf(root);
        if (lastLeaf.parent) {
            const isLastLeafLeft = lastLeaf.parent.left === lastLeaf;
            if (isLastLeafLeft) {
                lastLeaf.parent.left = null;
            }
            else {
                lastLeaf.parent.right = null;
            }
        }
        heap.parent = null;
        heap.left = null;
        heap.right = null;
        if (lastLeaf === root) {
            return null;
        }
        if (lastLeaf === heap) {
            return (parent && findRoot(parent)) || null;
        }
        lastLeaf.parent = parent;
        if (parent) {
            const isLeft = parent.left === heap;
            if (isLeft) {
                parent.left = lastLeaf;
            }
            else {
                parent.right = lastLeaf;
            }
            yield asyncBubbleUp(lastLeaf, predicate);
        }
        else {
            if (lastLeaf !== left) {
                lastLeaf.left = left;
                left && (left.parent = lastLeaf);
            }
            if (lastLeaf !== right) {
                lastLeaf.right = right;
                right && (right.parent = lastLeaf);
            }
            yield asyncBubbleDown(lastLeaf, predicate);
        }
        return findRoot(lastLeaf);
    });
}
exports.asyncRemoveHeapNode = asyncRemoveHeapNode;
function bubbleDown(root, predicate) {
    const { parent, value, left, right } = root;
    const gtLeft = !left || predicate(value, left.value);
    const gtRight = !right || predicate(value, right.value);
    const bubbleDownRight = () => {
        root.left = right.left;
        right.left && (right.left.parent = root);
        root.right = right.right;
        right.right && (right.right.parent = root);
        right.left = left;
        left.parent = right;
        right.right = root;
        root.parent = right;
        right.parent = parent;
        if (parent) {
            const isLeft = parent.left === root;
            if (isLeft) {
                parent.left = right;
            }
            else {
                parent.right = right;
            }
        }
        bubbleDown(root, predicate);
    };
    const bubbleDownLeft = () => {
        root.left = left.left;
        left.left && (left.left.parent = root);
        root.right = left.right;
        left.right && (left.right.parent = root);
        root.parent = left;
        left.left = root;
        left.right = right;
        left.parent = parent;
        if (parent) {
            const isLeft = parent.left === root;
            if (isLeft) {
                parent.left = left;
            }
            else {
                parent.right = left;
            }
        }
        right && (right.parent = left);
        bubbleDown(root, predicate);
    };
    if (left && right) {
        if (gtLeft && gtRight) {
            //
        }
        else if (gtLeft) {
            bubbleDownRight();
        }
        else if (gtRight) {
            bubbleDownLeft();
        }
        else {
            if (predicate(left.value, right.value)) {
                bubbleDownLeft();
            }
            else {
                bubbleDownRight();
            }
        }
    }
    else if (left) {
        if (gtLeft) {
            //
        }
        else {
            bubbleDownLeft();
        }
    }
    else {
        return null;
    }
}
exports.bubbleDown = bubbleDown;
function asyncBubbleDown(root, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        const { parent, value, left, right } = root;
        const gtLeft = !left || (yield predicate(value, left.value));
        const gtRight = !right || (yield predicate(value, right.value));
        const asyncBubbleDownRight = () => __awaiter(this, void 0, void 0, function* () {
            root.left = right.left;
            right.left && (right.left.parent = root);
            root.right = right.right;
            right.right && (right.right.parent = root);
            right.left = left;
            left.parent = right;
            right.right = root;
            root.parent = right;
            right.parent = parent;
            if (parent) {
                const isLeft = parent.left === root;
                if (isLeft) {
                    parent.left = right;
                }
                else {
                    parent.right = right;
                }
            }
            yield asyncBubbleDown(root, predicate);
        });
        const asyncBubbleDownLeft = () => __awaiter(this, void 0, void 0, function* () {
            root.left = left.left;
            left.left && (left.left.parent = root);
            root.right = left.right;
            left.right && (left.right.parent = root);
            root.parent = left;
            left.left = root;
            left.right = right;
            left.parent = parent;
            if (parent) {
                const isLeft = parent.left === root;
                if (isLeft) {
                    parent.left = left;
                }
                else {
                    parent.right = left;
                }
            }
            right && (right.parent = left);
            yield asyncBubbleDown(root, predicate);
        });
        if (left && right) {
            if (gtLeft && gtRight) {
                //
            }
            else if (gtLeft) {
                yield asyncBubbleDownRight();
            }
            else if (gtRight) {
                yield asyncBubbleDownLeft();
            }
            else {
                if (yield predicate(left.value, right.value)) {
                    yield asyncBubbleDownLeft();
                }
                else {
                    yield asyncBubbleDownRight();
                }
            }
        }
        else if (left) {
            if (gtLeft) {
                //
            }
            else {
                yield asyncBubbleDownLeft();
            }
        }
        else {
            return null;
        }
    });
}
exports.asyncBubbleDown = asyncBubbleDown;
function bubbleUp(child, predicate) {
    if (child.parent && predicate(child.value, child.parent.value)) {
        const { parent, left, right } = child;
        child.parent = parent.parent;
        const isLeft = parent.left === child;
        if (parent.parent) {
            const isParentLeft = parent.parent.left === parent;
            if (isParentLeft) {
                parent.parent.left = child;
            }
            else {
                parent.parent.right = child;
            }
        }
        parent.parent = child;
        if (isLeft) {
            child.left = parent;
            child.right = parent.right;
            if (parent.right) {
                parent.right.parent = child;
            }
        }
        else {
            child.right = parent;
            child.left = parent.left;
            if (parent.left) {
                parent.left.parent = child;
            }
        }
        parent.left = left;
        if (left) {
            left.parent = parent;
        }
        parent.right = right;
        if (right) {
            right.parent = parent;
        }
        bubbleUp(child, predicate);
        return parent;
    }
    return null;
}
exports.bubbleUp = bubbleUp;
function asyncBubbleUp(child, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        if (child.parent && (yield predicate(child.value, child.parent.value))) {
            const { parent, left, right } = child;
            child.parent = parent.parent;
            const isLeft = parent.left === child;
            if (parent.parent) {
                const isParentLeft = parent.parent.left === parent;
                if (isParentLeft) {
                    parent.parent.left = child;
                }
                else {
                    parent.parent.right = child;
                }
            }
            parent.parent = child;
            if (isLeft) {
                child.left = parent;
                child.right = parent.right;
                if (parent.right) {
                    parent.right.parent = child;
                }
            }
            else {
                child.right = parent;
                child.left = parent.left;
                if (parent.left) {
                    parent.left.parent = child;
                }
            }
            parent.left = left;
            if (left) {
                left.parent = parent;
            }
            parent.right = right;
            if (right) {
                right.parent = parent;
            }
            yield asyncBubbleUp(child, predicate);
            return parent;
        }
        return null;
    });
}
exports.asyncBubbleUp = asyncBubbleUp;
function addHeapNode(root, child, predicate) {
    if (root === null) {
        return child;
    }
    while (child) {
        const nextChild = removeHeapNode(child, predicate);
        const lastLeaf = findInsertionPoint(root);
        if (!lastLeaf.left) {
            lastLeaf.left = child;
        }
        else {
            lastLeaf.right = child;
        }
        child.parent = lastLeaf;
        bubbleUp(child, predicate);
        child = nextChild;
    }
    return findRoot(root);
}
exports.addHeapNode = addHeapNode;
function asyncAddHeapNode(root, child, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        if (root === null) {
            return child;
        }
        while (child) {
            const nextChild = yield asyncRemoveHeapNode(child, predicate);
            const lastLeaf = findInsertionPoint(root);
            if (!lastLeaf.left) {
                lastLeaf.left = child;
            }
            else {
                lastLeaf.right = child;
            }
            child.parent = lastLeaf;
            yield asyncBubbleUp(child, predicate);
            child = nextChild;
        }
        return findRoot(root);
    });
}
exports.asyncAddHeapNode = asyncAddHeapNode;
function setHeapNode(heap, newValue, predicate) {
    const { parent, left, right } = heap;
    heap.value = newValue;
    if (parent) {
        if (predicate(parent.value, newValue)) {
            bubbleDown(heap, predicate);
        }
        else {
            bubbleUp(heap, predicate);
        }
    }
    else {
        bubbleDown(heap, predicate);
    }
    return findRoot(heap);
}
exports.setHeapNode = setHeapNode;
function findRoot(node) {
    if (node.parent) {
        return findRoot(node.parent);
    }
    else {
        return node;
    }
}
exports.findRoot = findRoot;
//# sourceMappingURL=Heap.js.map