dastal
Version:
Data Structures & Algorithms implementations
151 lines • 6.17 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InOrderSegmentTree = void 0;
/**
* Inspired by [Tristan Hume's IForestIndex](https://thume.ca/2021/03/14/iforests) ([github](https://github.com/trishume/gigatrace))
*
* +
* /''''''''''''''''''''''''''''''+''''''''''''''''''''''''''''''\ |
* /''''''''''''''+''''''''''''''\ | /'''''''''''''''+''''''''''''''\ |
* /''''''+''''''\ | /''''''+'''''\ | /''''''+'''''\ | /''''''+'''''\ |
* /''+''\ | /''+''\ | /''+''\ | /''+''\ | /''+''\ | /''+''\ | /''+''\ | /''+''\ |
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
* --------------------------------------------------------------------------------------------------------------------------------------
* 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
* 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0
* 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0
* 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0
* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
*
* @module
*/
const u32_1 = require("../math/u32");
const env_1 = require("src/env");
/**
* A {@link SegmentTree} internally represented by a binary tree array, with nodes stored in in-order traversal.
*
* Memory usage: n elements require 2n - 1 space.
*
*/
class InOrderSegmentTree {
/**
* Construct a new segment tree
*
* @param combine - The function used to aggregate segment information
* @param elements - A set of elements to add into the initial tree
*/
constructor(combine, elements = []) {
this.array = [];
this.combine = combine;
for (const element of elements) {
this.push(element);
}
}
clear() {
this.array.length = 0;
}
pop() {
// Sanitize range
if (this.array.length < 2) {
return this.array.pop();
}
// Return element
const out = this.array[this.array.length - 1];
this.array.length -= 2;
return out;
}
push(element) {
// Sanitize range
if (this.array.length < 1) {
this.array[0] = element;
return 1;
}
if (this.array.length + 2 > env_1.MAX_ARRAY_LENGTH) {
throw new RangeError(`Invalid length`);
}
// Add element
this.array.push(element, element);
this.aggregate(this.array.length - 1);
return this.size;
}
query(min, max) {
// Sanitize range
if (min >= max) {
throw new RangeError(`Range [${min}..${max}) is empty`);
}
if (min < 0 || max > this.size) {
throw new RangeError(`Range [${min}..${max}) not in [0..${this.size})`);
}
// Translate range to interior indices
min *= 2;
max *= 2;
// Jump to min's highest aggregation node that is fully within the range
let offset = u32_1.lsp(min | u32_1.msp(max - min));
let value = this.array[min - 1 + (offset >>> 1)];
// Continue jumping top aggregation nodes until max is reached
for (min += offset; min < max; min += offset) {
offset = u32_1.lsp(min | u32_1.msp(max - min));
value = this.combine(value, this.array[min - 1 + (offset >>> 1)]);
}
return value;
}
get size() {
return (this.array.length + 1) >>> 1;
}
/**
* Return an iterator through the tree's elements
*/
*[Symbol.iterator]() {
for (let i = 0; i < this.array.length; i += 2) {
yield this.array[i];
}
}
update(min, max, operation) {
// Sanitize range
if (min >= max) {
return;
}
if (min < 0 || max > this.size) {
throw new RangeError(`Range [${min}..${max}) not in [0..${this.size})`);
}
// Translate range to interior indices
min *= 2;
max *= 2;
// Update the values
let value;
do {
this.array[min] = operation(this.array[min], min >>> 1);
value = this.aggregate(min);
min += 2;
} while (min < max);
// Update remaining aggregation nodes
let dc = 0;
let dp = u32_1.lsp(min);
max = u32_1.msb(min ^ (this.array.length + 1)) - u32_1.lsb(min);
for (--min; max > 0; --max) {
value = this.combine(value, this.array[min + (dp >>> 1) - dc]);
this.array[min] = value;
dc = (min & (2 * dp)) >>> 0;
min += dp - dc;
dp *= 2;
}
}
/**
* A helper method to update complete aggregation nodes for an index.
*
* @params index - The index whose aggregation nodes will be updated.
*
* @returns - The top aggregation node's new value.
*/
aggregate(index) {
let element = this.array[index++];
for (let mask = 2; index & mask; mask *= 2) {
element = this.combine(this.array[index - mask - (mask >>> 1)], element);
this.array[index - mask] = element;
}
return element;
}
}
exports.InOrderSegmentTree = InOrderSegmentTree;
//# sourceMappingURL=inOrderSegmentTree.js.map