UNPKG

molstar

Version:

A comprehensive macromolecular library.

359 lines (358 loc) 10.6 kB
"use strict"; /** * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Gianluca Tomasello <giagitom@gmail.com> * * Adapted from https://github.com/gwtw/ts-fibonacci-heap, Copyright (c) 2014 Daniel Imms, MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FibonacciHeap = void 0; class Node { constructor(key, value) { this.parent = null; this.child = null; this.degree = 0; this.isMarked = false; this.key = key; this.value = value; this.prev = this; this.next = this; } } class NodeListIterator { /** * Creates an Iterator used to simplify the consolidate() method. It works by * making a shallow copy of the nodes in the root list and iterating over the * shallow copy instead of the source as the source will be modified. * @param start A node from the root list. */ constructor(start) { this._index = -1; this._items = []; this._len = 0; if (start) { let current = start, l = 0; do { this._items[l++] = current; current = current.next; } while (start !== current); this._len = l; } } /** * @return Whether there is a next node in the iterator. */ hasNext() { return this._index < this._len - 1; } /** * @return The next node. */ next() { return this._items[++this._index]; } /** * @return Resets iterator to reuse it. */ reset(start) { this._index = -1; this._len = 0; let current = start, l = 0; do { this._items[l++] = current; current = current.next; } while (start !== current); this._len = l; } } const tmpIt = new NodeListIterator(); /** * A Fibonacci heap data structure with a key and optional value. */ class FibonacciHeap { constructor(compare) { this._minNode = null; this._nodeCount = 0; this._compare = compare ? compare : this._defaultCompare; } /** * Clears the heap's data, making it an empty heap. */ clear() { this._minNode = null; this._nodeCount = 0; } /** * Decreases a key of a node. * @param node The node to decrease the key of. * @param newKey The new key to assign to the node. */ decreaseKey(node, newKey) { if (!node) { throw new Error('Cannot decrease key of non-existent node'); } if (this._compare({ key: newKey }, { key: node.key }) > 0) { throw new Error('New key is larger than old key'); } node.key = newKey; const parent = node.parent; if (parent && this._compare(node, parent) < 0) { this._cut(node, parent, this._minNode); this._cascadingCut(parent, this._minNode); } if (this._compare(node, this._minNode) < 0) { this._minNode = node; } } /** * Deletes a node. * @param node The node to delete. */ delete(node) { // This is a special implementation of decreaseKey that sets the argument to // the minimum value. This is necessary to make generic keys work, since there // is no MIN_VALUE constant for generic types. const parent = node.parent; if (parent) { this._cut(node, parent, this._minNode); this._cascadingCut(parent, this._minNode); } this._minNode = node; this.extractMinimum(); } /** * Extracts and returns the minimum node from the heap. * @return The heap's minimum node or null if the heap is empty. */ extractMinimum() { const extractedMin = this._minNode; if (extractedMin) { // Set parent to null for the minimum's children if (extractedMin.child) { let child = extractedMin.child; do { child.parent = null; child = child.next; } while (child !== extractedMin.child); } let nextInRootList = null; if (extractedMin.next !== extractedMin) { nextInRootList = extractedMin.next; } // Remove min from root list this._removeNodeFromList(extractedMin); this._nodeCount--; // Merge the children of the minimum node with the root list this._minNode = this._mergeLists(nextInRootList, extractedMin.child); if (this._minNode) { this._minNode = this._consolidate(this._minNode); } } return extractedMin; } /** * Returns the minimum node from the heap. * @return The heap's minimum node or null if the heap is empty. */ findMinimum() { return this._minNode; } /** * Inserts a new key-value pair into the heap. * @param key The key to insert. * @param value The value to insert. * @return node The inserted node. */ insert(key, value) { const node = new Node(key, value); this._minNode = this._mergeLists(this._minNode, node); this._nodeCount++; return node; } /** * @return Whether the heap is empty. */ isEmpty() { return this._minNode === null; } /** * @return The size of the heap. */ size() { if (this._minNode === null) { return 0; } return this._getNodeListSize(this._minNode); } /** * Joins another heap to this heap. * @param other The other heap. */ union(other) { this._minNode = this._mergeLists(this._minNode, other._minNode); this._nodeCount += other._nodeCount; } /** * Compares two nodes with each other. * @param a The first key to compare. * @param b The second key to compare. * @return -1, 0 or 1 if a < b, a == b or a > b respectively. */ _defaultCompare(a, b) { if (a.key > b.key) { return 1; } if (a.key < b.key) { return -1; } return 0; } /** * Cut the link between a node and its parent, moving the node to the root list. * @param node The node being cut. * @param parent The parent of the node being cut. * @param minNode The minimum node in the root list. * @return The heap's new minimum node. */ _cut(node, parent, minNode) { node.parent = null; parent.degree--; if (node.next === node) { parent.child = null; } else { parent.child = node.next; } this._removeNodeFromList(node); const newMinNode = this._mergeLists(minNode, node); node.isMarked = false; return newMinNode; } /** * Perform a cascading cut on a node; mark the node if it is not marked, * otherwise cut the node and perform a cascading cut on its parent. * @param node The node being considered to be cut. * @param minNode The minimum node in the root list. * @return The heap's new minimum node. */ _cascadingCut(node, minNode) { const parent = node.parent; if (parent) { if (node.isMarked) { minNode = this._cut(node, parent, minNode); minNode = this._cascadingCut(parent, minNode); } else { node.isMarked = true; } } return minNode; } /** * Merge all trees of the same order together until there are no two trees of * the same order. * @param minNode The current minimum node. * @return The new minimum node. */ _consolidate(minNode) { const aux = []; tmpIt.reset(minNode); while (tmpIt.hasNext()) { let current = tmpIt.next(); // If there exists another node with the same degree, merge them let auxCurrent = aux[current.degree]; while (auxCurrent) { if (this._compare(current, auxCurrent) > 0) { const temp = current; current = auxCurrent; auxCurrent = temp; } this._linkHeaps(auxCurrent, current); aux[current.degree] = null; current.degree++; auxCurrent = aux[current.degree]; } aux[current.degree] = current; } let newMinNode = null; for (let i = 0; i < aux.length; i++) { const node = aux[i]; if (node) { // Remove siblings before merging node.next = node; node.prev = node; newMinNode = this._mergeLists(newMinNode, node); } } return newMinNode; } /** * Removes a node from a node list. * @param node The node to remove. */ _removeNodeFromList(node) { const prev = node.prev; const next = node.next; prev.next = next; next.prev = prev; node.next = node; node.prev = node; } /** * Links two heaps of the same order together. * * @private * @param max The heap with the larger root. * @param min The heap with the smaller root. */ _linkHeaps(max, min) { this._removeNodeFromList(max); min.child = this._mergeLists(max, min.child); max.parent = min; max.isMarked = false; } /** * Merge two lists of nodes together. * * @private * @param a The first list to merge. * @param b The second list to merge. * @return The new minimum node from the two lists. */ _mergeLists(a, b) { if (!a) { if (!b) { return null; } return b; } if (!b) { return a; } const temp = a.next; a.next = b.next; a.next.prev = a; b.next = temp; b.next.prev = b; return this._compare(a, b) < 0 ? a : b; } /** * Gets the size of a node list. * @param node A node within the node list. * @return The size of the node list. */ _getNodeListSize(node) { let count = 0; let current = node; do { count++; if (current.child) { count += this._getNodeListSize(current.child); } current = current.next; } while (current !== node); return count; } } exports.FibonacciHeap = FibonacciHeap;