UNPKG

doubly-linked-list-typed

Version:
332 lines (293 loc) 11.3 kB
/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ import { getMSB } from '../../utils'; /** * */ export class BinaryIndexedTree { protected readonly _freq: number; protected readonly _max: number; /** * The constructor initializes the properties of an object, including default frequency, maximum * value, a freqMap data structure, the most significant bit, and the count of negative frequencies. * @param - - `frequency`: The default frequency value. It is optional and has a default * value of 0. */ constructor({ frequency = 0, max }: { frequency?: number; max: number }) { this._freq = frequency; this._max = max; this._freqMap = { 0: 0 }; this._msb = getMSB(max); this._negativeCount = frequency < 0 ? max : 0; } protected _freqMap: Record<number, number>; /** * The function returns the frequency map of numbers. * @returns The `_freqMap` property, which is a record with number keys and number values, is being * returned. */ get freqMap(): Record<number, number> { return this._freqMap; } protected _msb: number; /** * The function returns the value of the _msb property. * @returns The `_msb` property of the object. */ get msb(): number { return this._msb; } protected _negativeCount: number; /** * The function returns the value of the _negativeCount property. * @returns The method is returning the value of the variable `_negativeCount`, which is of type * `number`. */ get negativeCount(): number { return this._negativeCount; } /** * The above function returns the value of the protected variable `_freq`. * @returns The frequency value stored in the protected variable `_freq`. */ get freq(): number { return this._freq; } /** * The above function returns the maximum value. * @returns The maximum value stored in the variable `_max`. */ get max(): number { return this._max; } /** * The function "readSingle" reads a single number from a specified index. * @param {number} index - The `index` parameter is a number that represents the index of an element in a * collection or array. * @returns a number. */ readSingle(index: number): number { this._checkIndex(index); return this._readSingle(index); } /** * The "update" function updates the value at a given index by adding a delta and triggers a callback * to notify of the change. * @param {number} position - The `index` parameter represents the index of the element that needs to be * updated in the data structure. * @param {number} change - The "delta" parameter represents the change in value that needs to be * applied to the frequency at the specified index. */ update(position: number, change: number): void { this._checkIndex(position); const freqCur = this._readSingle(position); this._update(position, change); this._updateNegativeCount(freqCur, freqCur + change); } /** * The function "writeSingle" checks the index and writes a single value with a given frequency. * @param {number} index - The `index` parameter is a number that represents the index of an element. It * is used to identify the specific element that needs to be written. * @param {number} freq - The `freq` parameter represents the frequency value that needs to be * written. */ writeSingle(index: number, freq: number): void { this._checkIndex(index); this._writeSingle(index, freq); } /** * The read function takes a count parameter, checks if it is an integer, and returns the result of * calling the _read function with the count parameter clamped between 0 and the maximum value. * @param {number} count - The `count` parameter is a number that represents the number of items to * read. * @returns a number. */ read(count: number): number { if (!Number.isInteger(count)) { throw new Error('Invalid count'); } return this._read(Math.max(Math.min(count, this.max), 0)); } /** * The function returns the lower bound of a non-descending sequence that sums up to a given number. * @param {number} sum - The `sum` parameter is a number that represents the target sum that we want * to find in the sequence. * @returns The lowerBound function is returning a number. */ lowerBound(sum: number): number { if (this.negativeCount > 0) { throw new Error('Sequence is not non-descending'); } return this._binarySearch(sum, (x, y) => x < y); } /** * The upperBound function returns the index of the first element in a sequence that is greater than * or equal to a given sum. * @param {number} sum - The "sum" parameter is a number that represents the target sum that we want * to find in the sequence. * @returns The upperBound function is returning a number. */ upperBound(sum: number): number { if (this.negativeCount > 0) { throw new Error('Must not be descending'); } return this._binarySearch(sum, (x, y) => x <= y); } /** * The function calculates the prefix sum of an array using a binary indexed tree. * @param {number} i - The parameter "i" in the function "getPrefixSum" represents the index of the element in the * array for which we want to calculate the prefix sum. * @returns The function `getPrefixSum` returns the prefix sum of the elements in the binary indexed tree up to index * `i`. */ getPrefixSum(i: number): number { this._checkIndex(i); i++; // Convert to 1-based index let sum = 0; while (i > 0) { sum += this._getFrequency(i); i -= i & -i; } return sum; } /** * The function returns the value of a specific index in a freqMap data structure, or a default value if * the index is not found. * @param {number} index - The `index` parameter is a number that represents the index of a node in a * freqMap data structure. * @returns a number. */ protected _getFrequency(index: number): number { if (index in this.freqMap) { return this.freqMap[index]; } return this.freq * (index & -index); } /** * The function _updateFrequency adds a delta value to the element at the specified index in the freqMap array. * @param {number} index - The index parameter is a number that represents the index of the freqMap * element that needs to be updated. * @param {number} delta - The `delta` parameter represents the change in value that needs to be * added to the freqMap at the specified `index`. */ protected _updateFrequency(index: number, delta: number): void { this.freqMap[index] = this._getFrequency(index) + delta; } /** * The function checks if the given index is valid and within the range. * @param {number} index - The parameter "index" is of type number and represents the index value * that needs to be checked. */ protected _checkIndex(index: number): void { if (!Number.isInteger(index)) { throw new Error('Invalid index: Index must be an integer.'); } if (index < 0 || index >= this.max) { throw new Error('Index out of range: Index must be within the range [0, this.max).'); } } /** * The function calculates the sum of elements in an array up to a given index using a binary indexed * freqMap. * @param {number} index - The `index` parameter is a number that represents the index of an element in a * data structure. * @returns a number. */ protected _readSingle(index: number): number { index = index + 1; let sum = this._getFrequency(index); const z = index - (index & -index); index--; while (index !== z) { sum -= this._getFrequency(index); index -= index & -index; } return sum; } /** * The function `_updateNegativeCount` updates a counter based on changes in frequency values. * @param {number} freqCur - The current frequency value. * @param {number} freqNew - The freqNew parameter represents the new frequency value. */ protected _updateNegativeCount(freqCur: number, freqNew: number): void { if (freqCur < 0 && freqNew >= 0) { this._negativeCount--; } else if (freqCur >= 0 && freqNew < 0) { this._negativeCount++; } } /** * The `_update` function updates the values in a binary indexed freqMap starting from a given index and * propagating the changes to its parent nodes. * @param {number} index - The `index` parameter is a number that represents the index of the element in * the data structure that needs to be updated. * @param {number} delta - The `delta` parameter represents the change in value that needs to be * applied to the elements in the data structure. */ protected _update(index: number, delta: number): void { index = index + 1; while (index <= this.max) { this._updateFrequency(index, delta); index += index & -index; } } /** * The `_writeSingle` function updates the frequency at a specific index and triggers a callback if * the frequency has changed. * @param {number} index - The `index` parameter is a number that represents the index of the element * being modified or accessed. * @param {number} freq - The `freq` parameter represents the new frequency value that needs to be * written to the specified index `index`. */ protected _writeSingle(index: number, freq: number): void { const freqCur = this._readSingle(index); this._update(index, freq - freqCur); this._updateNegativeCount(freqCur, freq); } /** * The `_read` function calculates the sum of values in a binary freqMap up to a given count. * @param {number} count - The `count` parameter is a number that represents the number of elements * to read from the freqMap. * @returns the sum of the values obtained from calling the `_getFrequency` method for each index in the * range from `count` to 1. */ protected _read(count: number): number { let index = count; let sum = 0; while (index) { sum += this._getFrequency(index); index -= index & -index; } return sum; } /** * The function `_binarySearch` performs a binary search to find the largest number that satisfies a given * condition. * @param {number} sum - The sum parameter is a number that represents the target sum value. * @param before - The `before` parameter is a function that takes two numbers `x` and `y` as * arguments and returns a boolean value. It is used to determine if `x` is less than or equal to * `y`. The purpose of this function is to compare two numbers and determine their order. * @returns the value of the variable "left". */ protected _binarySearch(sum: number, before: (x: number, y: number) => boolean): number { let left = 0; let right = this.msb << 1; let sumT = sum; while (right > left + 1) { const middle = (left + right) >> 1; const sumM = this._getFrequency(middle); if (middle <= this.max && before(sumM, sumT)) { sumT -= sumM; left = middle; } else { right = middle; } } return left; } }