UNPKG

i2bplustree

Version:

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

197 lines (196 loc) 7.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const internal_1 = require("./internal"); class IBplusLeafNode extends internal_1.IBplusNode { constructor(order = 4, parent = null, keys = [], maximums = [], children = []) { super(order, parent, keys, maximums); this.children = children; /** * Sibling Leaf Node that substituted this node, upon the * occurrence of a removal that triggered a merge. */ this.substSibling = null; } getChildren() { return this.children; } /** * Recursively get the node currently replacing this node. * * @returns the substitution sibling if exists, null otherwise */ getSubstituteSibling() { return this.substSibling; } exists(int) { for (const child of this.children) if (child.getOriginalInterval().equals(int)) return true; return false; } search(int) { const intervals = new Set(); for (const child of this.children) { const originInt = child.getOriginalInterval(); if (originInt.equals(int)) intervals.add(originInt); } return intervals; } loneRangeSearch(int) { for (const child of this.children) if (child.intersect(int)) return child.getOriginalInterval(); return null; } allRangeSearch(int) { const intervals = new Set(); for (const child of this.children) if (child.intersect(int)) intervals.add(child.getOriginalInterval()); return intervals; } containedRangeSearch(int) { const intervals = new Set(); for (const child of this.children) { const originInt = child.getOriginalInterval(); if (int.contains(originInt)) intervals.add(originInt); } return intervals; } findInsertNode(int) { return this; } /** * Add the given interval to this leaf data structures. * * @param int The interval to be added */ addInterval(int) { let i = 0; for (; i < this.keys.length; ++i) if (int.getLowerBound() < this.keys[i]) break; this.keys.splice(i, 0, int.getLowerBound()); this.maximums.splice(i, 0, int.getUpperBound()); this.children.splice(i, 0, int); } split(int) { //Need special care on binary trees let divIdx = Math.ceil(this.order / 2); let sibSize = this.order - divIdx; // Divide keys, maximums and children by this node and its sibling let sibling = new IBplusLeafNode(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); // Inserting the interval in the respective node const insertionLeaf = sibling.keys[0] < int.getLowerBound() ? sibling : this; insertionLeaf.addInterval(int); return insertionLeaf; } findInterval(int) { for (let i = 0; i < this.keys.length; ++i) if (this.children[i].getOriginalInterval().equals(int)) return [this, i]; return null; } findIntervalWithCompounds(int) { let res = []; for (let i = 0; i < this.children.length; ++i) if (int.equals(this.children[i].getOriginalInterval())) { res.push([this, this.children[i], i]); } return res; } findIntervalsInRange(int) { let res = []; for (let child of this.children) if (int.contains(child.getOriginalInterval())) res.push([this, child]); return res; } setChildParentOnBorrow(newChild, insertId) { // Child is Interval, it does not store parent information this.children.splice(insertId, 0, newChild); } setChildrenParentOnMerge(newParent) { // Children are Intervals, they do not store parent information } setSubstitutionNode(sibling) { // Sibling on merge is forcefully a IBplusLeafNode this.substSibling = sibling; } isChildNewRoot() { return false; // A Leaf can never be a root } /** * Time split intervals into smaller nodes in order to have better querying performance. * * @param rootNode the node were the insertions will happen * @param alpha parameter to adjust space/ query_time tradeoff. * See more regarding alpha in IBplusTree's constructor's documentation. */ timeSplit(rootNode, alpha) { if (this.isUnderflow()) return; // Array containing the Intervals that will need to be inserted in the end (time split intervals) let newInsertions = []; if (this.getRightSibling() != null) { let splitIdx = this.pickSplitPoint(alpha); if (this.maximums[splitIdx] == this.getMax()) return; else for (let i = 0; i < this.keys.length; ++i) { if (this.maximums[i] > this.maximums[splitIdx]) { // Creating new Interval newInsertions.push(new internal_1.CompoundInterval(this.maximums[splitIdx] + 1, this.maximums[i], this.children[i])); //Updating the split Interval this.children[i] = new internal_1.CompoundInterval(this.children[i].getLowerBound(), this.maximums[splitIdx], this.children[i]); this.maximums[i] = this.maximums[splitIdx]; } } } // Inserting the generated intervals and saving them to the resultant array for (let interval of newInsertions) rootNode.insert(interval, alpha); } /** * Algorithm which takes a leaf node as input and returns the index to the * interval whose end point is the best split point (with the minimum cost). * * @param alpha parameter to adjust space/ query_time tradeoff. * See more regarding alpha in IbplusTree's constructor's documentation. */ pickSplitPoint(alpha) { let maxbegin = Math.max(...this.keys); let endlist = this.maximums.map(el => el < maxbegin ? maxbegin : el); let cutcost = []; for (let i = 0; i < endlist.length; ++i) cutcost.push(endlist.map(el => Math.abs(endlist[i] - el)).reduce((acc, val) => acc + val)); // Doing it like this since formula ain't working // cutcost.push(cutcost[i - 1] + (2 * i - endlist.length) * (endlist[i] - endlist[i - 1])); let mincost = Math.min(...cutcost); let numsplit = []; for (let endval of endlist) numsplit.push(this.maximums.map(el => el > endval ? 1 : 0).reduce((acc, val) => acc + val, 0)); let finalCost = cutcost.map((el, idx) => el + mincost * alpha * numsplit[idx]); let finalCostIdx = Math.min(...finalCost.filter((_, idx) => this.maximums[idx] > maxbegin)); return finalCost.indexOf(finalCostIdx); } asString(acc = "", depth = 0) { // Adding tabs according to depth let tabs = ""; for (let i = 0; i < depth; ++i) tabs += '\t'; acc += `${tabs}Leaf:\n`; for (let interval of this.children) acc += `${tabs}- [${interval.getLowerBound()}, ${interval.getUpperBound()}]\n`; return acc; } } exports.IBplusLeafNode = IBplusLeafNode;