@phosphor/collections
Version:
PhosphorJS - Generic Collections
1,356 lines (1,355 loc) • 45.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2018, PhosphorJS Contributors
|
| Distributed under the terms of the BSD 3-Clause License.
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
var algorithm_1 = require("@phosphor/algorithm");
/**
* A generic B+ tree.
*
* #### Notes
* Most operations have `O(log32 n)` or better complexity.
*/
var BPlusTree = /** @class */ (function () {
/**
* Construct a new B+ tree.
*
* @param cmp - The item comparison function for the tree.
*/
function BPlusTree(cmp) {
this._root = new Private.LeafNode();
this.cmp = cmp;
}
Object.defineProperty(BPlusTree.prototype, "isEmpty", {
/**
* Whether the tree is empty.
*
* #### Complexity
* `O(1)`
*/
get: function () {
return this._root.size === 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BPlusTree.prototype, "size", {
/**
* The size of the tree.
*
* #### Complexity
* `O(1)`
*/
get: function () {
return this._root.size;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BPlusTree.prototype, "first", {
/**
* The first item in the tree.
*
* This is `undefined` if the tree is empty.
*
* #### Complexity
* `O(log32 n)`
*/
get: function () {
var node = Private.firstLeaf(this._root);
return node.size > 0 ? node.items[0] : undefined;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BPlusTree.prototype, "last", {
/**
* The last item in the tree.
*
* This is `undefined` if the tree is empty.
*
* #### Complexity
* `O(log32 n)`
*/
get: function () {
var node = Private.lastLeaf(this._root);
return node.size > 0 ? node.items[node.size - 1] : undefined;
},
enumerable: true,
configurable: true
});
/**
* Create an iterator over the items in the tree.
*
* @returns A new iterator starting with the first item.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.iter = function () {
return Private.iterItems(this._root);
};
/**
* Create a reverse iterator over the items in the tree.
*
* @returns A new iterator starting with the last item.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.retro = function () {
return Private.retroItems(this._root);
};
/**
* Create an iterator for a slice of items in the tree.
*
* @param start - The index of the first item, inclusive. This
* should be `< stop`. Negative values are taken as an offset
* from the end of the tree. The default is `0`.
*
* @param stop - The index of the last item, exclusive. This
* should be `> start`. Negative values are taken as an offset
* from the end of the tree. The default is `size`.
*
* @returns A new iterator starting with the specified item.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.slice = function (start, stop) {
return Private.sliceItems(this._root, start, stop);
};
/**
* Create a reverse iterator for a slice of items in the tree.
*
* @param start - The index of the first item, inclusive. This
* should be `> stop`. Negative values are taken as an offset
* from the end of the tree. The default is `size - 1`.
*
* @param stop - The index of the last item, exclusive. This
* should be `< start`. Negative values are taken as an offset
* from the end of the tree. The default is `-size - 1`.
*
* @returns A new reverse iterator starting with the specified item.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.retroSlice = function (start, stop) {
return Private.retroSliceItems(this._root, start, stop);
};
/**
* Get the item at a particular index.
*
* @param index - The index of the item of interest. Negative
* values are taken as an offset from the end of the tree.
*
* @returns The item at the specified index, or `undefined` if
* the index is out of range.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.at = function (index) {
return Private.itemAt(this._root, index);
};
/**
* Test whether the tree has an item which matches a key.
*
* @param key - The key of interest.
*
* @param cmp - A function which compares an item against the key.
*
* @returns `true` if the tree has an item which matches the given
* key, `false` otherwise.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.has = function (key, cmp) {
return Private.hasItem(this._root, key, cmp);
};
/**
* Get the index of an item which matches a key.
*
* @param key - The key of interest.
*
* @param cmp - A function which compares an item against the key.
*
* @returns The index of the item which matches the given key. A
* negative value means that a matching item does not exist in
* the tree, but if one did it would reside at `-index - 1`.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.indexOf = function (key, cmp) {
return Private.indexOf(this._root, key, cmp);
};
/**
* Get the item which matches a key.
*
* @param item - The key of interest.
*
* @param cmp - A function which compares an item against the key.
*
* @returns The item which matches the given key, or `undefined` if
* the tree does not have a matching item.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.get = function (key, cmp) {
return Private.getItem(this._root, key, cmp);
};
/**
* Assign new items to the tree, replacing all current items.
*
* @param items - The items to assign to the tree.
*
* #### Complexity
* `O(n log32 n)`
*/
BPlusTree.prototype.assign = function (items) {
this.clear();
this.update(items);
};
/**
* Insert an item into the tree.
*
* @param item - The item of interest.
*
* @returns If the given item matches an existing item in the tree,
* the given item will replace it, and the existing item will be
* returned. Otherwise, this method returns `undefined`.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.insert = function (item) {
var existing = Private.insertItem(this._root, item, this.cmp);
this._root = Private.maybeSplitRoot(this._root);
return existing;
};
/**
* Update the tree with multiple items.
*
* @param items - The items to insert into the tree.
*
* #### Complexity
* `O(k log32 n)`
*/
BPlusTree.prototype.update = function (items) {
var _this = this;
algorithm_1.each(items, function (item) { _this.insert(item); });
};
/**
* Delete an item which matches a particular key.
*
* @param key - The key of interest.
*
* @param cmp - A function which compares an item against the key.
*
* @returns The item removed from the tree, or `undefined` if no
* item matched the given key.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.delete = function (key, cmp) {
var item = Private.deleteItem(this._root, key, cmp);
this._root = Private.maybeExtractRoot(this._root);
return item;
};
/**
* Remove an item at a particular index.
*
* @param index - The index of the item to remove. Negative
* values are taken as an offset from the end of the tree.
*
* @returns The item removed from the tree, or `undefined` if
* the given index is out of range.
*
* #### Complexity
* `O(log32 n)`
*/
BPlusTree.prototype.remove = function (index) {
var item = Private.removeItem(this._root, index);
this._root = Private.maybeExtractRoot(this._root);
return item;
};
/**
* Clear the contents of the tree.
*
* #### Complexity
* `O(n)`
*/
BPlusTree.prototype.clear = function () {
Private.clear(this._root);
this._root = new Private.LeafNode();
};
return BPlusTree;
}());
exports.BPlusTree = BPlusTree;
/**
* The namespace for the `BPlusTree` class statics.
*/
(function (BPlusTree) {
/**
* Create a new B+ tree populated with the given items.
*
* @param items - The items to add to the tree.
*
* @param cmp - The item comparison function for the tree.
*
* @returns A new B+ tree populated with the given items.
*
* #### Complexity
* `O(n log32 n)`
*/
function from(items, cmp) {
var tree = new BPlusTree(cmp);
tree.assign(items);
return tree;
}
BPlusTree.from = from;
})(BPlusTree = exports.BPlusTree || (exports.BPlusTree = {}));
exports.BPlusTree = BPlusTree;
/**
* The namespace for the module implementation details.
*/
var Private;
(function (Private) {
/**
* A branch node in a B+ tree.
*/
var BranchNode = /** @class */ (function () {
function BranchNode() {
/**
* The left-most item of each child subtree.
*/
this.items = [];
/**
* The cumulative sizes of each child subtree.
*/
this.sizes = [];
/**
* The child nodes of this branch node.
*/
this.children = [];
}
Object.defineProperty(BranchNode.prototype, "type", {
/**
* The discriminated type of the node.
*/
get: function () {
return 0 /* Branch */;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BranchNode.prototype, "size", {
/**
* The total number of items in the subtree.
*/
get: function () {
return this.sizes[this.sizes.length - 1];
},
enumerable: true,
configurable: true
});
Object.defineProperty(BranchNode.prototype, "width", {
/**
* The tree width of the node.
*/
get: function () {
return this.children.length;
},
enumerable: true,
configurable: true
});
return BranchNode;
}());
Private.BranchNode = BranchNode;
/**
* A leaf node in a B+ tree.
*/
var LeafNode = /** @class */ (function () {
function LeafNode() {
/**
* The next sibling leaf node of this leaf node.
*/
this.next = null;
/**
* The previous sibling leaf node of this leaf node.
*/
this.prev = null;
/**
* The items of the leaf.
*/
this.items = [];
}
Object.defineProperty(LeafNode.prototype, "type", {
/**
* The discriminated type of the node.
*/
get: function () {
return 1 /* Leaf */;
},
enumerable: true,
configurable: true
});
Object.defineProperty(LeafNode.prototype, "size", {
/**
* The total number of items in the leaf.
*/
get: function () {
return this.items.length;
},
enumerable: true,
configurable: true
});
Object.defineProperty(LeafNode.prototype, "width", {
/**
* The tree width of the node.
*/
get: function () {
return this.items.length;
},
enumerable: true,
configurable: true
});
return LeafNode;
}());
Private.LeafNode = LeafNode;
/**
* Get the first leaf node in the tree.
*
* @param node - The root node of interest.
*
* @returns The first leaf node in the tree.
*
* #### Complexity
* `O(log32 n)`
*/
function firstLeaf(node) {
while (node.type === 0 /* Branch */) {
node = node.children[0];
}
return node;
}
Private.firstLeaf = firstLeaf;
/**
* Get the last leaf node in the tree.
*
* @param node - The root node of interest.
*
* @returns The last leaf node in the tree.
*
* #### Complexity
* `O(log32 n)`
*/
function lastLeaf(node) {
while (node.type === 0 /* Branch */) {
node = node.children[node.children.length - 1];
}
return node;
}
Private.lastLeaf = lastLeaf;
/**
* Create a forward iterator for the items in the tree.
*
* @param node - The root node of interest.
*
* @returns A new forward iterator starting with the first item.
*
* #### Complexity
* `O(log32 n)`
*/
function iterItems(node) {
var leaf = firstLeaf(node);
return new ForwardIterator(leaf, 0, -1);
}
Private.iterItems = iterItems;
/**
* Create a reverse iterator for the items in the tree.
*
* @param node - The root node of interest.
*
* @returns A new reverse iterator starting with the last item.
*
* #### Complexity
* `O(log32 n)`
*/
function retroItems(node) {
var leaf = lastLeaf(node);
return new RetroIterator(leaf, leaf.size - 1, -1);
}
Private.retroItems = retroItems;
/**
* Create an iterator for a slice of items in the tree.
*
* @param node - The root node of interest.
*
* @param start - The index of the first item, inclusive. This
* should be `< stop`. Negative values are taken as an offset
* from the end of the tree. The default is `0`.
*
* @param stop - The index of the last item, exclusive. This
* should be `> start`. Negative values are taken as an offset
* from the end of the tree. The default is `size`.
*
* @returns A new iterator starting with the specified item.
*
* #### Complexity
* `O(log32 n)`
*/
function sliceItems(node, start, stop) {
// Normalize the start index.
if (start === undefined) {
start = 0;
}
else if (start < 0) {
start = Math.max(0, start + node.size);
}
else {
start = Math.min(start, node.size);
}
// Normalize the stop index.
if (stop === undefined) {
stop = node.size;
}
else if (stop < 0) {
stop = Math.max(0, stop + node.size);
}
else {
stop = Math.min(stop, node.size);
}
// Compute effective count.
var count = Math.max(0, stop - start);
// Bail early if there is nothing to iterate.
if (count === 0) {
return algorithm_1.empty();
}
// Find the starting leaf node and local index.
while (node.type === 0 /* Branch */) {
var i = findPivotIndexByIndex(node.sizes, start);
if (i > 0)
start -= node.sizes[i - 1];
node = node.children[i];
}
// Return the forward iterator for the range.
return new ForwardIterator(node, start, count);
}
Private.sliceItems = sliceItems;
/**
* Create a reverse iterator for a slice of items in the tree.
*
* @param node - The root node of interest.
*
* @param start - The index of the first item, inclusive. This
* should be `> stop`. Negative values are taken as an offset
* from the end of the tree. The default is `size - 1`.
*
* @param stop - The index of the last item, exclusive. This
* should be `< start`. Negative values are taken as an offset
* from the end of the tree. The default is `-size - 1`.
*
* @returns A new reverse iterator starting with the specified item.
*
* #### Complexity
* `O(log32 n)`
*/
function retroSliceItems(node, start, stop) {
// Normalize the start index.
if (start === undefined) {
start = node.size - 1;
}
else if (start < 0) {
start = Math.max(-1, start + node.size);
}
else {
start = Math.min(start, node.size - 1);
}
// Normalize the stop index.
if (stop === undefined) {
stop = -1;
}
else if (stop < 0) {
stop = Math.max(-1, stop + node.size);
}
else {
stop = Math.min(stop, node.size - 1);
}
// Compute the effective count.
var count = Math.max(0, start - stop);
// Bail early if there is nothing to iterate.
if (count === 0) {
return algorithm_1.empty();
}
// Find the starting leaf node and local index.
while (node.type === 0 /* Branch */) {
var i = findPivotIndexByIndex(node.sizes, start);
if (i > 0)
start -= node.sizes[i - 1];
node = node.children[i];
}
// Return the retro iterator for the range.
return new RetroIterator(node, start, count);
}
Private.retroSliceItems = retroSliceItems;
/**
* Get the item at the specified index.
*
* @param node - The root node of interest.
*
* @param index - The index of the item of interest. Negative
* values are taken as an offset from the end of the tree.
*
* @returns The item at the specified index, or `undefined` if
* the index is out of range.
*
* #### Complexity
* `O(log32 n)`
*/
function itemAt(node, index) {
// Wrap negative indices.
if (index < 0) {
index += node.size;
}
// Bail early if the index is out of range.
if (index < 0 || index >= node.size) {
return undefined;
}
// Find the containing leaf node and local index.
while (node.type === 0 /* Branch */) {
var i = findPivotIndexByIndex(node.sizes, index);
if (i > 0)
index -= node.sizes[i - 1];
node = node.children[i];
}
// Return the item at the specified index.
return node.items[index];
}
Private.itemAt = itemAt;
/**
* Test whether the tree contains an item which matches a key.
*
* @param node - The root node of interest.
*
* @param key - The key of interest.
*
* @param cmp - The key comparison function.
*
* @returns Whether the tree contains a matching item.
*
* #### Complexity
* `O(log32 n)`
*/
function hasItem(node, key, cmp) {
// Find the containing leaf node.
while (node.type === 0 /* Branch */) {
var i_1 = findPivotIndexByKey(node.items, key, cmp);
node = node.children[i_1];
}
// Find the key index.
var i = findKeyIndex(node.items, key, cmp);
// Return whether or not the node contains a matching item.
return i >= 0;
}
Private.hasItem = hasItem;
/**
* Get the index of the item which matches a key.
*
* @param node - The node of interest.
*
* @param key - The key of interest.
*
* @param cmp - The key comparison function.
*
* @returns The index of the item which matches the given key. A
* negative value means that a matching item does not exist in
* the tree, but if one did it would reside at `-index - 1`.
*
* #### Complexity
* `O(log32 n)`
*/
function indexOf(node, key, cmp) {
// Set up the global index.
var index = 0;
// Find the containing leaf node and global index.
while (node.type === 0 /* Branch */) {
var i_2 = findPivotIndexByKey(node.items, key, cmp);
if (i_2 > 0)
index += node.sizes[i_2 - 1];
node = node.children[i_2];
}
// Find the key index.
var i = findKeyIndex(node.items, key, cmp);
// Return the final computed index.
return i >= 0 ? index + i : -index + i;
}
Private.indexOf = indexOf;
/**
* Get the item for a particular key.
*
* @param node - The node of interest.
*
* @param key - The key of interest.
*
* @param cmp - The key comparison function.
*
* @returns The item for the specified key, or `undefined` if
* the tree does not have a matching item for the key.
*
* #### Complexity
* `O(log32 n)`
*/
function getItem(node, key, cmp) {
// Find the containing leaf node.
while (node.type === 0 /* Branch */) {
var i_3 = findPivotIndexByKey(node.items, key, cmp);
node = node.children[i_3];
}
// Find the key index.
var i = findKeyIndex(node.items, key, cmp);
// Return the item for the given key.
return i >= 0 ? node.items[i] : undefined;
}
Private.getItem = getItem;
/**
* Insert an item into the tree.
*
* @param node - The root node of interest.
*
* @param item - The item of interest.
*
* @param cmp - The item comparison function.
*
* @returns If the given item matches an existing item in the tree,
* the given item will replace it, and the existing item will be
* returned. Otherwise, this function returns `undefined`.
*
* #### Complexity
* `O(log32 n)`
*
* #### Notes
* The root may be overfull after calling this function.
*/
function insertItem(node, item, cmp) {
// Handle leaf nodes first.
if (node.type === 1 /* Leaf */) {
// Find the index for the given item.
var i_4 = findKeyIndex(node.items, item, cmp);
// Fetch the existing item and insert the new item.
var existing_1;
if (i_4 >= 0) {
existing_1 = node.items[i_4];
node.items[i_4] = item;
}
else {
existing_1 = undefined;
algorithm_1.ArrayExt.insert(node.items, -i_4 - 1, item);
}
// Return the existing item.
return existing_1;
}
// Find the pivot index for the insert.
var i = findPivotIndexByKey(node.items, item, cmp);
// Fetch the pivot child.
var child = node.children[i];
// Fetch the current size of the child.
var prevSize = child.size;
// Recursively insert the item into the child.
var existing = insertItem(child, item, cmp);
// Fetch the updated size of the child.
var currSize = child.size;
// Update the item state of the branch.
node.items[i] = child.items[0];
// Bail early if the child size did not change.
if (prevSize === currSize) {
return existing;
}
// Split the child if it's overfull.
if (child.width > MAX_NODE_WIDTH) {
var next = splitNode(child);
algorithm_1.ArrayExt.insert(node.children, i + 1, next);
algorithm_1.ArrayExt.insert(node.items, i + 1, next.items[0]);
}
// Update the dirty sizes of the branch.
updateSizes(node, i);
// Return the existing item.
return existing;
}
Private.insertItem = insertItem;
/**
* Delete an item in the tree.
*
* @param node - The node of interest.
*
* @param key - The key of interest.
*
* @param cmp - The key comparison function.
*
* @returns The deleted item or `undefined`.
*
* #### Complexity
* `O(log32 n)`
*
* #### Notes
* The root may be underfull after calling this function.
*/
function deleteItem(node, key, cmp) {
// Handle leaf nodes first.
if (node.type === 1 /* Leaf */) {
// Find the index for the given key.
var i_5 = findKeyIndex(node.items, key, cmp);
// Bail early if the item does not exist.
if (i_5 < 0) {
return undefined;
}
// Remove the item at the computed index.
return algorithm_1.ArrayExt.removeAt(node.items, i_5);
}
// Find the pivot index for the delete.
var i = findPivotIndexByKey(node.items, key, cmp);
// Fetch the pivot child.
var child = node.children[i];
// Fetch the current size of the child.
var prevSize = child.size;
// Recursively remove the item from the child.
var item = deleteItem(child, key, cmp);
// Fetch the updated size of the child.
var currSize = child.size;
// Bail early if the child size did not change.
if (prevSize === currSize) {
return item;
}
// Update the item state of the branch.
node.items[i] = child.items[0];
// Join the child if it's underfull.
if (child.width < MIN_NODE_WIDTH) {
i = joinChild(node, i);
}
// Update the dirty sizes of the branch.
updateSizes(node, i);
// Return the deleted item.
return item;
}
Private.deleteItem = deleteItem;
/**
* Remove an item from the tree.
*
* @param node - The node of interest.
*
* @param index - The index of interest.
*
* @returns The removed item or `undefined`.
*
* #### Complexity
* `O(log32 n)`
*
* #### Notes
* The root may be underfull after calling this function.
*/
function removeItem(node, index) {
// Wrap negative indices.
if (index < 0) {
index += node.size;
}
// Bail early if the index is out of range.
if (index < 0 || index >= node.size) {
return undefined;
}
// Handle leaf nodes first.
if (node.type === 1 /* Leaf */) {
return algorithm_1.ArrayExt.removeAt(node.items, index);
}
// Find the pivot index for the remove.
var i = findPivotIndexByIndex(node.sizes, index);
if (i > 0)
index -= node.sizes[i];
// Fetch the pivot child.
var child = node.children[i];
// Recursively remove the item from the child.
var item = removeItem(child, index);
// Update the item state of the branch.
node.items[i] = child.items[0];
// Join the child if it's underfull.
if (child.width < MIN_NODE_WIDTH) {
i = joinChild(node, i);
}
// Update the dirty sizes of the branch.
updateSizes(node, i);
// Return the removed item.
return item;
}
Private.removeItem = removeItem;
/**
* Recursively clear the contents of a node.
*
* @param node - The node of interest.
*
* #### Complexity
* `O(n)`
*/
function clear(node) {
if (node.type === 0 /* Branch */) {
algorithm_1.each(node.children, clear);
node.children.length = 0;
node.sizes.length = 0;
node.items.length = 0;
}
else {
node.items.length = 0;
node.next = null;
node.prev = null;
}
}
Private.clear = clear;
/**
* Split a root node and create a new root, if needed.
*
* @param node - The root node of interest.
*
* @returns The new root node.
*/
function maybeSplitRoot(node) {
// Bail early if the current root is not overfull.
if (node.width <= MAX_NODE_WIDTH) {
return node;
}
// Create a new root branch node.
var root = new BranchNode();
// Split the node to the right and create a new sibling.
var next = splitNode(node);
// Add the sizes to the root.
root.sizes[0] = node.size;
root.sizes[1] = node.size + next.size;
// Add the children to the root.
root.children[0] = node;
root.children[1] = next;
// Add the items to the root.
root.items[0] = node.items[0];
root.items[1] = next.items[0];
// Return the new root node.
return root;
}
Private.maybeSplitRoot = maybeSplitRoot;
/**
* Extract a single node child as a new root, if needed.
*
* @param node - The root node of interest.
*
* @returns The new root node.
*/
function maybeExtractRoot(node) {
// Bail early if the node it already a leaf.
if (node.type === 1 /* Leaf */) {
return node;
}
// Bail early if the branch has more than one child.
if (node.children.length > 1) {
return node;
}
// Extract the sole remaining child as the new root.
var root = node.children.pop();
// Clear the rest of the node state.
clear(node);
// Return the new root.
return root;
}
Private.maybeExtractRoot = maybeExtractRoot;
/**
* The maximum width for a node in the tree.
*/
var MAX_NODE_WIDTH = 32;
/**
* The minimum width for a node in the tree.
*/
var MIN_NODE_WIDTH = MAX_NODE_WIDTH >> 1;
/**
* A forward iterator for a B+ tree.
*/
var ForwardIterator = /** @class */ (function () {
/**
* Construct a new forward iterator.
*
* @param node - The first leaf node in the chain.
*
* @param index - The local index of the first item.
*
* @param count - The number of items to iterate. A value `< 0`
* will iterate all available items.
*/
function ForwardIterator(node, index, count) {
this._node = node;
this._index = index;
this._count = count;
}
/**
* Get an iterator over the object's values.
*
* @returns An iterator which yields the object's values.
*/
ForwardIterator.prototype.iter = function () {
return this;
};
/**
* Create an independent clone of the iterator.
*
* @returns A new independent clone of the iterator.
*/
ForwardIterator.prototype.clone = function () {
return new ForwardIterator(this._node, this._index, this._count);
};
/**
* Get the next value from the iterator.
*
* @returns The next value from the iterator, or `undefined`.
*/
ForwardIterator.prototype.next = function () {
if (this._node === null || this._count === 0) {
return undefined;
}
if (this._index >= this._node.size) {
this._node = this._node.next;
this._index = 0;
return this.next();
}
if (this._count > 0) {
this._count--;
}
return this._node.items[this._index++];
};
return ForwardIterator;
}());
/**
* A reverse iterator for a B+ tree.
*/
var RetroIterator = /** @class */ (function () {
/**
* Construct a new retro iterator.
*
* @param node - The last leaf node in the chain.
*
* @param index - The local index of the last item.
*
* @param count - The number of items to iterate. A value `< 0`
* will iterate all available items.
*/
function RetroIterator(node, index, count) {
this._node = node;
this._index = index;
this._count = count;
}
/**
* Get an iterator over the object's values.
*
* @returns An iterator which yields the object's values.
*/
RetroIterator.prototype.iter = function () {
return this;
};
/**
* Create an independent clone of the iterator.
*
* @returns A new independent clone of the iterator.
*/
RetroIterator.prototype.clone = function () {
return new RetroIterator(this._node, this._index, this._count);
};
/**
* Get the next value from the iterator.
*
* @returns The next value from the iterator, or `undefined`.
*/
RetroIterator.prototype.next = function () {
if (this._node === null || this._count === 0) {
return undefined;
}
if (this._index >= this._node.size) {
this._index = this._node.size - 1;
}
if (this._index < 0) {
this._node = this._node.prev;
this._index = this._node ? this._node.size - 1 : -1;
return this.next();
}
if (this._count > 0) {
this._count--;
}
return this._node.items[this._index--];
};
return RetroIterator;
}());
/**
* Find the pivot index for a particular local index.
*/
function findPivotIndexByIndex(sizes, index) {
var n = sizes.length;
for (var i = 0; i < n; ++i) {
if (sizes[i] > index) {
return i;
}
}
return n - 1;
}
/**
* Find the pivot index for a particular key.
*/
function findPivotIndexByKey(items, key, cmp) {
var n = items.length;
for (var i = 1; i < n; ++i) {
if (cmp(items[i], key) > 0) {
return i - 1;
}
}
return n - 1;
}
/**
* Find the key index for a particular key.
*/
function findKeyIndex(items, key, cmp) {
var n = items.length;
for (var i = 0; i < n; ++i) {
var c = cmp(items[i], key);
if (c === 0) {
return i;
}
if (c > 0) {
return -i - 1;
}
}
return -n - 1;
}
/**
* Update the sizes of a branch node starting at the given index.
*/
function updateSizes(node, i) {
var sizes = node.sizes, children = node.children;
var last = i > 0 ? sizes[i - 1] : 0;
for (var n = children.length; i < n; ++i) {
last = sizes[i] = last + children[i].size;
}
sizes.length = children.length;
}
/**
* Split a node and return its new next sibling.
*
* @param node - The node of interest.
*
* @returns The new next sibling node.
*/
function splitNode(node) {
// Handle leaf nodes first.
if (node.type === 1 /* Leaf */) {
// Create the new sibling leaf node.
var next_1 = new LeafNode();
// Move the items to the new sibling.
var v1_1 = node.items;
var v2_1 = next_1.items;
for (var i = MIN_NODE_WIDTH, n = v1_1.length; i < n; ++i) {
v2_1.push(v1_1[i]);
}
v1_1.length = MIN_NODE_WIDTH;
// Patch up the sibling links.
if (node.next)
node.next.prev = next_1;
next_1.next = node.next;
next_1.prev = node;
node.next = next_1;
// Return the new next sibling.
return next_1;
}
// Create the new sibling branch node.
var next = new BranchNode();
// Move the children to the new sibling.
var c1 = node.children;
var c2 = next.children;
for (var i = MIN_NODE_WIDTH, n = c1.length; i < n; ++i) {
c2.push(c1[i]);
}
c1.length = MIN_NODE_WIDTH;
// Move the items to the new sibling.
var v1 = node.items;
var v2 = next.items;
for (var i = MIN_NODE_WIDTH, n = v1.length; i < n; ++i) {
v2.push(v1[i]);
}
v1.length = MIN_NODE_WIDTH;
// Update the dirty sizes of the nodes.
updateSizes(node, MIN_NODE_WIDTH);
updateSizes(next, 0);
// Return the new next sibling.
return next;
}
/**
* Join a child node of a branch with one of its siblings.
*
* @param node - The branch node of interest.
*
* @param i - The index of the child node of interest.
*
* @returns The first modified index.
*
* #### Notes
* This may cause the branch to become underfull.
*/
function joinChild(node, i) {
var _a, _b, _c, _d, _e, _f;
// Fetch the child to be joined.
var child = node.children[i];
// Fetch the relevant sibling.
var sibling = i === 0 ? node.children[i + 1] : node.children[i - 1];
// Compute the flags which control the join behavior.
var hasNext = i === 0;
var isLeaf = child.type === 1 /* Leaf */;
var hasExtra = sibling.width > MIN_NODE_WIDTH;
// Join case #1: steal from next sibling leaf
if (isLeaf && hasExtra && hasNext) {
// Cast the children as leaves.
var c = child;
var s = sibling;
// Steal an item.
c.items.push(s.items.shift());
// Update the branch items.
node.items[i + 1] = s.items[0];
// Return the first modified index.
return i;
}
// Join case #2: steal from previous sibling leaf
if (isLeaf && hasExtra && !hasNext) {
// Cast the children as leaves.
var c = child;
var s = sibling;
// Steal an item.
c.items.unshift(s.items.pop());
// Update the branch items.
node.items[i] = c.items[0];
// Return the first modified index.
return i - 1;
}
// Join case #3: merge with next sibling leaf
if (isLeaf && !hasExtra && hasNext) {
// Cast the children as leaves.
var c = child;
var s = sibling;
// Merge items.
(_a = s.items).unshift.apply(_a, c.items);
// Remove the old branch child.
algorithm_1.ArrayExt.removeAt(node.children, i);
// Remove the stale branch item.
algorithm_1.ArrayExt.removeAt(node.items, i + 1);
// Patch up the sibling links.
if (c.prev)
c.prev.next = s;
s.prev = c.prev;
// Clear the original child.
clear(c);
// Return the first modified index.
return i;
}
// Join case #4: merge with previous sibling leaf
if (isLeaf && !hasExtra && !hasNext) {
// Cast the children as leaves.
var c = child;
var s = sibling;
// Merge items.
(_b = s.items).push.apply(_b, c.items);
// Remove the old branch child.
algorithm_1.ArrayExt.removeAt(node.children, i);
// Remove the stale branch item.
algorithm_1.ArrayExt.removeAt(node.items, i);
// Patch up the sibling links.
if (c.next)
c.next.prev = s;
s.next = c.next;
// Clear the original child.
clear(c);
// Return the first modified index.
return i - 1;
}
// Join case #5: steal from next sibling branch
if (!isLeaf && hasExtra && hasNext) {
// Cast the children to branches.
var c = child;
var s = sibling;
// Steal a child from the next sibling.
c.children.push(s.children.shift());
// Steal an item from the next sibling.
c.items.push(s.items.shift());
// Update the branch items.
node.items[i + 1] = s.items[0];
// Update the sibling sizes.
updateSizes(c, c.width - 1);
updateSizes(s, 0);
// Return the first modified index.
return i;
}
// Join case #6: steal from previous sibling branch
if (!isLeaf && hasExtra && !hasNext) {
// Cast the children to branches.
var c = child;
var s = sibling;
// Steal a child from the previous sibling.
c.children.unshift(s.children.pop());
// Steal an item from the previous sibling.
c.items.unshift(s.items.pop());
// Update the branch items.
node.items[i] = c.items[0];
// Update the sibling sizes.
updateSizes(c, 0);
updateSizes(s, s.width - 1);
// Return the first modified index.
return i - 1;
}
// Join case #7: merge with next sibling branch
if (!isLeaf && !hasExtra && hasNext) {
// Cast the children to branches.
var c = child;
var s = sibling;
// Merge the children with the next sibling.
(_c = s.children).unshift.apply(_c, c.children);
// Merge the items with the next sibling.
(_d = s.items).unshift.apply(_d, c.items);
// Remove the old branch child.
algorithm_1.ArrayExt.removeAt(node.children, i);
// Remove the stale branch item.
algorithm_1.ArrayExt.removeAt(node.items, i + 1);
// Update the sibling sizes.
updateSizes(s, 0);
// Clear the original child but, not its children.
c.children.length = 0;
clear(c);
// Return the first modified index.
return i;
}
// Join case #8: merge with previous sibling branch
if (!isLeaf && !hasExtra && !hasNext) {
// Cast the children to branches.
var c = child;
var s = sibling;
// Merge the children with the previous sibling.
(_e = s.children).push.apply(_e, c.children);
// Merge the items with the previous sibling.
(_f = s.items).push.apply(_f, c.items);
// Remove the old branch child.
algorithm_1.ArrayExt.removeAt(node.children, i);
// Remove the stale branch item.
algorithm_1.ArrayExt.removeAt(node.items, i);
// Update the sibling sizes.
updateSizes(s, 0);
// Clear the original child, but not its children.
c.children.length = 0;
clear(c);
// Return the first modified index.
return i - 1;
}
// One of the above cases must match.
throw 'unreachable';
}
})(Private || (Private = {}));