UNPKG

dastal

Version:

Data Structures & Algorithms implementations

151 lines 6.17 kB
"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