UNPKG

i2bplustree

Version:

A package to implement the Improved Interval B+ tree, in TypeScript

214 lines (213 loc) 7.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const internal_1 = require("./internal"); /** * Class implementation of a generic Interval B+-Tree node */ class IBplusNode { /** * @param order Node's order. Must be the same in all tree nodes. * @param parent This node's parent node. If null, node is root * @param keys Keys are the lower bounds of this tree subtree * @param maximums Maximum end point of the intervals indexed by its subtree */ constructor(order, parent, keys, maximums) { this.order = order; this.parent = parent; this.keys = keys; this.maximums = maximums; this.rightSibling = null; this.leftSibling = null; } /** * Get this node's parent node */ getParent() { return this.parent; } /** * Set this node parent node * * @param newParent new parent node */ setParent(newParent) { this.parent = newParent; } /** * Get the minimum key out of this node keys */ getMinKey() { return this.keys[0]; } /** * Get the maximum number out of this node maximums */ getMax() { return Math.max(...this.maximums); } /** * Gets this node right sibling. */ getRightSibling() { return this.rightSibling; } /** * Sets this node right sibling to the given value. * * @param sibling new right sibling */ setRightSibling(sibling) { this.rightSibling = sibling; } /** * Gets this node left sibling. */ getLeftSibling() { return this.leftSibling; } /** * Sets this node left sibling to the given value. * * @param sibling new left sibling */ setLeftSibling(sibling) { this.leftSibling = sibling; } /** * Function to be called on node deletion. * Sets this node siblings' siblings to one each other, making this node no one's sibling */ concatSiblings() { if (this.rightSibling !== null) this.rightSibling.setLeftSibling(this.leftSibling); if (this.leftSibling !== null) this.leftSibling.setRightSibling(this.rightSibling); } /** * Checks if this node is the root. * * @returns true if this node is Root, false otherwise. */ isRoot() { return this.parent == null; } /** * Update the key and the maximum representing a node in its parent */ updateParentValues() { this.parent.updateMin(this); this.parent.updateMax(this); } /** * Insert the given Interval in the tree. * If alpha parameter is > 0, TimeSplit is applied to a leaf node N after a * new interval is inserted into N and the end point of the new interval is * greater than the maximum end point among the intervals that were already in N. * * @param int the interval to be inserted * @param alpha the value of alpha used in the PickSplitPoint algorithm. If alpha <= 0, time splits aren't used. */ insert(int, alpha) { // Insertions must always start in the root -> Valid since this function is not recursive if (!this.isRoot()) return this.parent.insert(int, alpha); // Perform a search to determine what leaf the new record should go into. let insertionNode = this.findInsertNode(int); // No leafs in the tree if (insertionNode === null && this instanceof internal_1.IBplusInternalNode) { insertionNode = new internal_1.IBplusLeafNode(this.order, this); this.setChildren([insertionNode]); } // If we are adding and the keys's size is already equal to order, split before insertion if (insertionNode.keys.length >= this.order) // Split also inserts insertionNode = insertionNode.split(int); else insertionNode.addInterval(int); // Comparison viable since maximums and minimums were not yet updated if (alpha > 0 && int.getUpperBound() > this.getMax()) insertionNode.timeSplit(this, alpha); insertionNode.updateParentValues(); } /** * Remove an entry from the sibling node and add it to this node * * @param sibling The sibling this node is going to borrow from * @param insertId The index where the element will be inserted * @param removeId The index of the element going to be removed from the sibling */ borrow(sibling, insertId, removeId) { this.keys.splice(insertId, 0, sibling.keys.splice(removeId, 1)[0]); this.maximums.splice(insertId, 0, sibling.maximums.splice(removeId, 1)[0]); this.setChildParentOnBorrow(sibling.getChildren().splice(removeId, 1)[0], insertId); this.updateParentValues(); sibling.updateParentValues(); } /** * Merge this node into the given node * * @param sibling The sibling to merge with * @param id The position where this node elements will be inserted */ merge(sibling, id) { sibling.keys.splice(id, 0, ...this.keys); sibling.maximums.splice(id, 0, ...this.maximums); sibling.getChildren().splice(id, 0, ...this.getChildren()); this.setSubstitutionNode(sibling); this.setChildrenParentOnMerge(sibling); this.concatSiblings(); this.parent.removeChild(this.parent.getChildren().indexOf(this)); if (!sibling.isRoot()) sibling.updateParentValues(); } /** * Handle underflow by borrowing form siblings or merging with them */ handleUnderflow() { // Minimum number of entries a node must have let minEntries = Math.floor(this.order / 2); // Borrow from left sibling if (this.leftSibling != null && this.leftSibling.keys.length > minEntries) this.borrow(this.leftSibling, 0, this.leftSibling.keys.length - 1); //Borrow from right sibling else if (this.rightSibling != null && this.rightSibling.keys.length > minEntries) this.borrow(this.rightSibling, this.keys.length, 0); // Merge left sibling else if (this.leftSibling != null) this.merge(this.leftSibling, this.leftSibling.keys.length); // Merge right sibling else if (this.rightSibling != null) this.merge(this.rightSibling, 0); } /** * Find out if this Node is in an underflow situation. * Invariant Underflow: Every node but the root must always have at least floor(order/ 2) keys. * * @returns true if it is, false otherwise */ isUnderflow() { return this.keys.length < Math.floor(this.order / 2); } /** * Remove the node's child in the given idx * http://sites.fas.harvard.edu/~cs165/papers/comer.pdf * http://www.cburch.com/cs/340/reading/btree/index.html#s3 * * @param idx idx of the interval */ removeChild(idx) { // Removing the interval this.keys.splice(idx, 1); this.maximums.splice(idx, 1); this.getChildren().splice(idx, 1); if (this.isChildNewRoot()) { this.getChildren()[0].setParent(null); //new Root return; } if (this.isUnderflow()) this.handleUnderflow(); else if (!this.isRoot()) this.updateParentValues(); // Update parent keys } } exports.IBplusNode = IBplusNode;