UNPKG

i2bplustree

Version:

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

257 lines (256 loc) 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const internal_1 = require("./internal"); class IBplusInternalNode extends internal_1.IBplusNode { constructor(order = 4, parent = null, keys = [], maximums = [], children = []) { super(order, parent, keys, maximums); this.children = children; for (const child of this.children) child.setParent(this); } getChildren() { return this.children; } setChildren(children) { this.children = children; } /** * Updates the current node structures, when a new maximum appears in a child node. * * @param node the child */ updateMax(node) { let newMax = node.getMax(); let prevMax = this.getMax(); this.maximums[this.children.indexOf(node)] = newMax; if (!this.isRoot() && (newMax > prevMax || this.getMax() != prevMax)) this.parent.updateMax(this); } /** * Updates the current node structures, when a new minimum key appears in a child node. * * @param node the child */ updateMin(node) { let newMin = node.getMinKey(); let prevMin = this.getMinKey(); let index = this.children.indexOf(node); this.keys[index] = newMin; if (!this.isRoot() && (newMin < prevMin || index == 0)) this.parent.updateMin(this); } exists(int) { for (let i = 0; i < this.keys.length; ++i) if ((new internal_1.FlatInterval(this.keys[i], this.maximums[i])).contains(int)) if (this.children[i].exists(int)) return true; return false; } search(int) { const intervals = new Set(); for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.containsWithValues([this.keys[i], this.maximums[i]], [int.getLowerBound(), int.getUpperBound()])) { const iterator = this.children[i].search(int).values(); for (let next = iterator.next(); next.done !== true; next = iterator.next()) intervals.add(next.value); } return intervals; } loneRangeSearch(int) { for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.intersectsWithValues([int.getLowerBound(), int.getUpperBound()], [this.keys[i], this.maximums[i]])) return this.children[i].loneRangeSearch(int); return null; } allRangeSearch(int) { const intervals = new Set(); for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.intersectsWithValues([int.getLowerBound(), int.getUpperBound()], [this.keys[i], this.maximums[i]])) { const iterator = this.children[i].allRangeSearch(int).values(); for (let next = iterator.next(); next.done !== true; next = iterator.next()) intervals.add(next.value); } return intervals; } containedRangeSearch(int) { const intervals = new Set(); for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.intersectsWithValues([int.getLowerBound(), int.getUpperBound()], [this.keys[i], this.maximums[i]])) { const iterator = this.children[i].containedRangeSearch(int).values(); for (let next = iterator.next(); next.done !== true; next = iterator.next()) intervals.add(next.value); } return intervals; } findInsertNode(int) { if (this.children.length == 0) return null; for (let i = 0; i < this.keys.length; ++i) if (int.getLowerBound() < this.keys[i]) return this.children[i > 0 ? i - 1 : i].findInsertNode(int); return this.children[this.keys.length - 1].findInsertNode(int); // The biggest } /** * When a split occurred in a child of this node, the node structures must be updated. * Updates maximum and key of previous node and adds the newly created node to this node children. * * @param original The node that was split * @param newNode The node that was created */ updateWithNewNode(original, newNode) { let originalIdx = this.children.indexOf(original); this.maximums[originalIdx] = original.getMax(); this.children.splice(originalIdx + 1, 0, newNode); this.keys.splice(originalIdx + 1, 0, newNode.getMinKey()); this.maximums.splice(originalIdx + 1, 0, newNode.getMax()); // Might be momentarily violating B+-trees order invariant if (this.keys.length > this.order) this.split(); } split() { // If this is root, create a new root if (this.parent == null) this.parent = new IBplusInternalNode(this.order, null, [this.getMinKey()], [this.getMax()], [this]); let divIdx = Math.ceil(this.keys.length / 2); let sibSize = this.order + 1 - divIdx; //Need +1 because of the invariant violation // Divide keys, maximums and children by this node and its sibling let sibling = new IBplusInternalNode(this.order, this.parent, this.keys.splice(divIdx, sibSize), this.maximums.splice(divIdx, sibSize), this.children.splice(divIdx, sibSize)); let rs = this.getRightSibling(); if (rs != null) rs.setLeftSibling(sibling); sibling.setRightSibling(rs); sibling.setLeftSibling(this); this.setRightSibling(sibling); this.parent.updateWithNewNode(this, sibling); // When called in IBplusInternalNode, the split method is not handling direct insertions // hence, its return value is irrelevant return null; } findInterval(int) { let res = null; for (let i = 0; i < this.keys.length && res == null; ++i) if (internal_1.Interval.containsWithValues([this.keys[i], this.maximums[i]], [int.getLowerBound(), int.getUpperBound()])) res = this.children[i].findInterval(int); return res; } findIntervalWithCompounds(int) { let res = []; for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.intersectsWithValues([int.getLowerBound(), int.getUpperBound()], [this.keys[i], this.maximums[i]])) res.push(...this.children[i].findIntervalWithCompounds(int)); return res; } findIntervalsInRange(int) { let res = []; for (let i = 0; i < this.keys.length; ++i) if (internal_1.Interval.intersectsWithValues([this.keys[i], this.maximums[i]], [int.getLowerBound(), int.getUpperBound()])) res.push(...this.children[i].findIntervalsInRange(int)); return res; } /** * Function to be called on successive deletions, * where a previous removal can make an interval change leaf or index * * @param leaf the initial leaf * @param int the initial index * @return pair of leaf and the index of the interval on it */ checkIntervalOnLeaf(leaf, int) { let sibling = leaf.getSubstituteSibling(); while (sibling) { leaf = sibling; sibling = sibling.getSubstituteSibling(); } let childIdx = leaf.getChildren().indexOf(int); if (childIdx < 0) // Previous removals triggered borrows that moved the child if (leaf.getLeftSibling() && int.getLowerBound() <= leaf.getMinKey()) // Sent to left sibling leaf leaf = leaf.getLeftSibling(); else if (leaf.getRightSibling() && int.getLowerBound() > leaf.getMinKey()) // Sent to right sibling leaf leaf = leaf.getRightSibling(); else throw Error('Unable to find child in range remove.'); return [leaf, leaf.getChildren().indexOf(int)]; } /** * Deletes a given interval if it exists in one of the tree's (that have this node as root) leafs. * The tree self-balances on deletion. * * @param int The interval to be deleted */ delete(int, alpha) { if (!alpha || alpha == 0) { let found = this.findInterval(int); if (found != null) found[0].removeChild(found[1]); } else { let foundInts = this.findIntervalWithCompounds(int); // Remove the first found let [leaf, foundInt, idx] = foundInts[0]; leaf.removeChild(idx); // If the eliminated interval is a compound, also eliminate the remaining Compounds referring the same interval\ if (foundInt != foundInt.getOriginalInterval()) { const originInt = foundInt.getOriginalInterval(); for (let i = 1; i < foundInts.length; ++i) { [leaf, int, idx] = foundInts[i]; [leaf, idx] = this.checkIntervalOnLeaf(leaf, int); if (leaf.getChildren()[idx].getOriginalInterval() == originInt) leaf.removeChild(idx); } } } } /** * Deletes all the interval contained in a given range. * The tree self-balances on deletion. * * @param lowerBound the range's lower bound * @param upperBound the range's upper bound */ rangeDelete(lowerBound, upperBound) { let foundInts = this.findIntervalsInRange(new internal_1.FlatInterval(lowerBound, upperBound)); for (let [leaf, int] of foundInts) { let childIdx; [leaf, childIdx] = this.checkIntervalOnLeaf(leaf, int); leaf.removeChild(childIdx); } } setChildParentOnBorrow(newChild, insertId) { this.children.splice(insertId, 0, newChild); newChild.setParent(this); } setChildrenParentOnMerge(newParent) { for (const child of this.children) child.setParent(newParent); } setSubstitutionNode(node) { // No interest in saving substitution node in Internal Nodes } isChildNewRoot() { if (!this.isRoot() || this.children.length > 1) return false; return this.children[0] instanceof IBplusInternalNode && this.children[0].getLeftSibling() == null && this.children[0].getRightSibling() == null; } asString(acc = "", depth = 0) { // Adding tabs according to depth let tabs = ""; for (let i = 0; i < depth; ++i) tabs += '\t'; acc += `${tabs}- Keys |`; for (let key of this.keys) acc += `${key}|`; acc += `\n${tabs} Maxs |`; for (let max of this.maximums) acc += `${max}|`; acc += "\n"; for (let child of this.children) acc = child.asString(acc, depth + 1); return acc; } } exports.IBplusInternalNode = IBplusInternalNode;