"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bffHeap = bffHeap;
exports.findInsertionPoint = findInsertionPoint;
exports.findLastLeaf = findLastLeaf;
exports.traverseHeap = traverseHeap;
exports.toArray = toArray;
exports.toSortedArray = toSortedArray;
exports.asyncToSortedArray = asyncToSortedArray;
exports.parentHeap = parentHeap;
exports.readHeap = readHeap;
exports.removeHeapNode = removeHeapNode;
exports.asyncRemoveHeapNode = asyncRemoveHeapNode;
exports.bubbleDown = bubbleDown;
exports.asyncBubbleDown = asyncBubbleDown;
exports.bubbleUp = bubbleUp;
exports.asyncBubbleUp = asyncBubbleUp;
exports.addHeapNode = addHeapNode;
exports.asyncAddHeapNode = asyncAddHeapNode;
exports.setHeapNode = setHeapNode;
exports.findRoot = findRoot;
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);
    }
}
function findInsertionPoint(heap) {
    let result = null;
    bffHeap(heap, (node) => {
        if (!node.left || !node.right) {
            if (!result) {
                result = node;
            }
        }
    });
    return result;
}
function findLastLeaf(heap) {
    let result = null;
    bffHeap(heap, (node) => {
        result = node;
    });
    return result;
}
function traverseHeap(heap, callback) {
    const { value, left, right } = heap;
    callback(value);
    left && traverseHeap(left, callback);
    right && traverseHeap(right, callback);
}
function toArray(heap) {
    const array = [];
    traverseHeap(heap, (value) => {
        if (value !== undefined) {
            array.push(value);
        }
    });
    return array;
}
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;
}
async function asyncToSortedArray(heap, predicate) {
    const array = [];
    do {
        array.push(heap.value);
        const lastLeaf = findLastLeaf(heap);
        heap = await asyncRemoveHeapNode(lastLeaf, predicate);
        if (heap) {
            heap.value = lastLeaf.value;
            await asyncBubbleDown(heap, predicate);
        }
        heap = heap && findRoot(heap);
    } while (heap);
    return array;
}
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;
}
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;
}
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);
}
async function asyncRemoveHeapNode(heap, predicate) {
    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;
        }
        await asyncBubbleUp(lastLeaf, predicate);
    }
    else {
        if (lastLeaf !== left) {
            lastLeaf.left = left;
            left && (left.parent = lastLeaf);
        }
        if (lastLeaf !== right) {
            lastLeaf.right = right;
            right && (right.parent = lastLeaf);
        }
        await asyncBubbleDown(lastLeaf, predicate);
    }
    return findRoot(lastLeaf);
}
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;
    }
}
async function asyncBubbleDown(root, predicate) {
    const { parent, value, left, right } = root;
    const gtLeft = !left || (await predicate(value, left.value));
    const gtRight = !right || (await predicate(value, right.value));
    const asyncBubbleDownRight = async () => {
        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;
            }
        }
        await asyncBubbleDown(root, predicate);
    };
    const asyncBubbleDownLeft = async () => {
        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);
        await asyncBubbleDown(root, predicate);
    };
    if (left && right) {
        if (gtLeft && gtRight) {
            //
        }
        else if (gtLeft) {
            await asyncBubbleDownRight();
        }
        else if (gtRight) {
            await asyncBubbleDownLeft();
        }
        else {
            if (await predicate(left.value, right.value)) {
                await asyncBubbleDownLeft();
            }
            else {
                await asyncBubbleDownRight();
            }
        }
    }
    else if (left) {
        if (gtLeft) {
            //
        }
        else {
            await asyncBubbleDownLeft();
        }
    }
    else {
        return null;
    }
}
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;
}
async function asyncBubbleUp(child, predicate) {
    if (child.parent && !(await predicate(child.parent.value, child.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;
        }
        await asyncBubbleUp(child, predicate);
        return parent;
    }
    return null;
}
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);
}
async function asyncAddHeapNode(root, child, predicate) {
    if (root === null) {
        return child;
    }
    while (child) {
        const nextChild = await asyncRemoveHeapNode(child, predicate);
        const lastLeaf = findInsertionPoint(root);
        if (!lastLeaf.left) {
            lastLeaf.left = child;
        }
        else {
            lastLeaf.right = child;
        }
        child.parent = lastLeaf;
        await asyncBubbleUp(child, predicate);
        child = nextChild;
    }
    return findRoot(root);
}
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);
}
function findRoot(node) {
    if (node.parent) {
        return findRoot(node.parent);
    }
    else {
        return node;
    }
}
//# sourceMappingURL=Heap.js.map