UNPKG

red-black-tree-typed

Version:
2,003 lines (1,509 loc) 207 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/utils/utils.ts function isPrimitiveComparable(value) { const valueType = typeof value; if (valueType === "number") return true; return valueType === "bigint" || valueType === "string" || valueType === "boolean"; } __name(isPrimitiveComparable, "isPrimitiveComparable"); function tryObjectToPrimitive(obj) { if (typeof obj.valueOf === "function") { const valueOfResult = obj.valueOf(); if (valueOfResult !== obj) { if (isPrimitiveComparable(valueOfResult)) return valueOfResult; if (typeof valueOfResult === "object" && valueOfResult !== null) return tryObjectToPrimitive(valueOfResult); } } if (typeof obj.toString === "function") { const stringResult = obj.toString(); if (stringResult !== "[object Object]") return stringResult; } return null; } __name(tryObjectToPrimitive, "tryObjectToPrimitive"); function isComparable(value, isForceObjectComparable = false) { if (value === null || value === void 0) return false; if (isPrimitiveComparable(value)) return true; if (typeof value !== "object") return false; if (value instanceof Date) return true; if (isForceObjectComparable) return true; const comparableValue = tryObjectToPrimitive(value); if (comparableValue === null || comparableValue === void 0) return false; return isPrimitiveComparable(comparableValue); } __name(isComparable, "isComparable"); var makeTrampolineThunk = /* @__PURE__ */ __name((computation) => ({ isThunk: true, // Marker indicating this is a thunk fn: computation // The deferred computation function }), "makeTrampolineThunk"); var isTrampolineThunk = /* @__PURE__ */ __name((value) => typeof value === "object" && // Must be an object value !== null && // Must not be null "isThunk" in value && // Must have the 'isThunk' property value.isThunk, "isTrampolineThunk"); function trampoline(initial) { let current = initial; while (isTrampolineThunk(current)) { current = current.fn(); } return current; } __name(trampoline, "trampoline"); function makeTrampoline(fn) { return (...args) => trampoline(fn(...args)); } __name(makeTrampoline, "makeTrampoline"); // src/common/error.ts function raise(ErrorClass, message) { throw new ErrorClass(message); } __name(raise, "raise"); var ERR = { // Range / index indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"), invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"), // Type / argument invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"), comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"), invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"), notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"), invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"), invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"), invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"), reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"), callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"), // State / operation invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"), // Matrix matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"), matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"), matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"), matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"), matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch"), // Order statistic orderStatisticNotEnabled: /* @__PURE__ */ __name((method, ctx) => `${ctx ? ctx + ": " : ""}${method}() requires enableOrderStatistic: true.`, "orderStatisticNotEnabled") }; // src/common/index.ts var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => { DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT"; DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS"; return DFSOperation2; })(DFSOperation || {}); var Range = class { constructor(low, high, includeLow = true, includeHigh = true) { this.low = low; this.high = high; this.includeLow = includeLow; this.includeHigh = includeHigh; } static { __name(this, "Range"); } // Determine whether a key is within the range isInRange(key, comparator) { const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0; const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0; return lowCheck && highCheck; } }; // src/data-structures/base/iterable-element-base.ts var IterableElementBase = class { static { __name(this, "IterableElementBase"); } /** * Create a new iterable base. * * @param options Optional behavior overrides. When provided, a `toElementFn` * is used to convert a raw element (`R`) into a public element (`E`). * * @remarks * Time O(1), Space O(1). */ constructor(options) { if (options) { const { toElementFn } = options; if (typeof toElementFn === "function") this._toElementFn = toElementFn; else if (toElementFn) raise(TypeError, "toElementFn must be a function type"); } } /** * The converter used to transform a raw element (`R`) into a public element (`E`). * * @remarks * Time O(1), Space O(1). */ _toElementFn; /** * Exposes the current `toElementFn`, if configured. * * @returns The converter function or `undefined` when not set. * @remarks * Time O(1), Space O(1). */ get toElementFn() { return this._toElementFn; } /** * Returns an iterator over the structure's elements. * * @param args Optional iterator arguments forwarded to the internal iterator. * @returns An `IterableIterator<E>` that yields the elements in traversal order. * * @remarks * Producing the iterator is O(1); consuming the entire iterator is Time O(n) with O(1) extra space. */ *[Symbol.iterator](...args) { yield* this._getIterator(...args); } /** * Returns an iterator over the values (alias of the default iterator). * * @returns An `IterableIterator<E>` over all elements. * @remarks * Creating the iterator is O(1); full iteration is Time O(n), Space O(1). */ *values() { for (const item of this) yield item; } /** * Tests whether all elements satisfy the predicate. * * @template TReturn * @param predicate Function invoked for each element with signature `(value, index, self)`. * @param thisArg Optional `this` binding for the predicate. * @returns `true` if every element passes; otherwise `false`. * * @remarks * Time O(n) in the worst case; may exit early when the first failure is found. Space O(1). */ every(predicate, thisArg) { let index = 0; for (const item of this) { if (thisArg === void 0) { if (!predicate(item, index++, this)) return false; } else { const fn = predicate; if (!fn.call(thisArg, item, index++, this)) return false; } } return true; } /** * Tests whether at least one element satisfies the predicate. * * @param predicate Function invoked for each element with signature `(value, index, self)`. * @param thisArg Optional `this` binding for the predicate. * @returns `true` if any element passes; otherwise `false`. * * @remarks * Time O(n) in the worst case; may exit early on first success. Space O(1). */ some(predicate, thisArg) { let index = 0; for (const item of this) { if (thisArg === void 0) { if (predicate(item, index++, this)) return true; } else { const fn = predicate; if (fn.call(thisArg, item, index++, this)) return true; } } return false; } /** * Invokes a callback for each element in iteration order. * * @param callbackfn Function invoked per element with signature `(value, index, self)`. * @param thisArg Optional `this` binding for the callback. * @returns `void`. * * @remarks * Time O(n), Space O(1). */ forEach(callbackfn, thisArg) { let index = 0; for (const item of this) { if (thisArg === void 0) { callbackfn(item, index++, this); } else { const fn = callbackfn; fn.call(thisArg, item, index++, this); } } } // Implementation signature find(predicate, thisArg) { let index = 0; for (const item of this) { if (thisArg === void 0) { if (predicate(item, index++, this)) return item; } else { const fn = predicate; if (fn.call(thisArg, item, index++, this)) return item; } } return; } /** * Checks whether a strictly-equal element exists in the structure. * * @param element The element to test with `===` equality. * @returns `true` if an equal element is found; otherwise `false`. * * @remarks * Time O(n) in the worst case. Space O(1). */ has(element) { for (const ele of this) if (ele === element) return true; return false; } /** * Check whether a value exists (Array-compatible alias for `has`). * @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1). * @param element - Element to search for (uses `===`). * @returns `true` if found. */ includes(element) { return this.has(element); } /** * Return an iterator of `[index, value]` pairs (Array-compatible). * @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1) per step. */ *entries() { let index = 0; for (const value of this) { yield [index++, value]; } } /** * Return an iterator of numeric indices (Array-compatible). * @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1) per step. */ *keys() { let index = 0; for (const _ of this) { yield index++; } } /** * Reduces all elements to a single accumulated value. * * @overload * @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`. The first element is used as the initial accumulator. * @returns The final accumulated value typed as `E`. * * @overload * @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`. * @param initialValue The initial accumulator value of type `E`. * @returns The final accumulated value typed as `E`. * * @overload * @template U The accumulator type when it differs from `E`. * @param callbackfn Reducer of signature `(acc: U, value, index, self) => U`. * @param initialValue The initial accumulator value of type `U`. * @returns The final accumulated value typed as `U`. * * @remarks * Time O(n), Space O(1). Throws if called on an empty structure without `initialValue`. */ reduce(callbackfn, initialValue) { let index = 0; const iter = this[Symbol.iterator](); let acc; if (arguments.length >= 2) { acc = initialValue; } else { const first = iter.next(); if (first.done) raise(TypeError, "Reduce of empty structure with no initial value"); acc = first.value; index = 1; } for (const value of iter) { acc = callbackfn(acc, value, index++, this); } return acc; } /** * Materializes the elements into a new array. * * @returns A shallow array copy of the iteration order. * @remarks * Time O(n), Space O(n). */ toArray() { return [...this]; } /** * Returns a representation of the structure suitable for quick visualization. * Defaults to an array of elements; subclasses may override to provide richer visuals. * * @returns A visual representation (array by default). * @remarks * Time O(n), Space O(n). */ toVisual() { return [...this]; } /** * Prints `toVisual()` to the console. Intended for quick debugging. * * @returns `void`. * @remarks * Time O(n) due to materialization, Space O(n) for the intermediate representation. */ print() { console.log(this.toVisual()); } }; // src/data-structures/base/linear-base.ts var LinearBase = class _LinearBase extends IterableElementBase { static { __name(this, "LinearBase"); } /** * Construct a linear container with runtime options. * @param options - `{ maxLen?, ... }` bounds/behavior options. * @remarks Time O(1), Space O(1) */ constructor(options) { super(options); if (options) { const { maxLen } = options; if (typeof maxLen === "number" && maxLen > 0 && maxLen % 1 === 0) this._maxLen = maxLen; } } _maxLen = -1; /** * Upper bound for length (if positive), or `-1` when unbounded. * @returns Maximum allowed length. * @remarks Time O(1), Space O(1) */ get maxLen() { return this._maxLen; } /** * First index of a value from the left. * @param searchElement - Value to match. * @param fromIndex - Start position (supports negative index). * @returns Index or `-1` if not found. * @remarks Time O(n), Space O(1) */ indexOf(searchElement, fromIndex = 0) { if (this.length === 0) return -1; if (fromIndex < 0) fromIndex = this.length + fromIndex; if (fromIndex < 0) fromIndex = 0; for (let i = fromIndex; i < this.length; i++) { const element = this.at(i); if (element === searchElement) return i; } return -1; } /** * Last index of a value from the right. * @param searchElement - Value to match. * @param fromIndex - Start position (supports negative index). * @returns Index or `-1` if not found. * @remarks Time O(n), Space O(1) */ lastIndexOf(searchElement, fromIndex = this.length - 1) { if (this.length === 0) return -1; if (fromIndex >= this.length) fromIndex = this.length - 1; if (fromIndex < 0) fromIndex = this.length + fromIndex; for (let i = fromIndex; i >= 0; i--) { const element = this.at(i); if (element === searchElement) return i; } return -1; } /** * Find the first index matching a predicate. * @param predicate - `(element, index, self) => boolean`. * @param thisArg - Optional `this` for callback. * @returns Index or `-1`. * @remarks Time O(n), Space O(1) */ findIndex(predicate, thisArg) { for (let i = 0; i < this.length; i++) { const item = this.at(i); if (item !== void 0 && predicate.call(thisArg, item, i, this)) return i; } return -1; } /** * Concatenate elements and/or containers. * @param items - Elements or other containers. * @returns New container with combined elements (`this` type). * @remarks Time O(sum(length)), Space O(sum(length)) */ concat(...items) { const newList = this.clone(); for (const item of items) { if (item instanceof _LinearBase) { newList.pushMany(item); } else { newList.push(item); } } return newList; } /** * In-place stable order via array sort semantics. * @param compareFn - Comparator `(a, b) => number`. * @returns This container. * @remarks Time O(n log n), Space O(n) (materializes to array temporarily) */ sort(compareFn) { const arr = this.toArray(); arr.sort(compareFn); this.clear(); for (const item of arr) this.push(item); return this; } /** * Remove and/or insert elements at a position (array-compatible). * @param start - Start index (supports negative index). * @param deleteCount - How many to remove. * @param items - Elements to insert. * @returns Removed elements as a new list (`this` type). * @remarks Time O(n + m), Space O(min(n, m)) where `m = items.length` */ splice(start, deleteCount = 0, ...items) { const removedList = this._createInstance(); start = start < 0 ? this.length + start : start; start = Math.max(0, Math.min(start, this.length)); deleteCount = Math.max(0, Math.min(deleteCount, this.length - start)); for (let i = 0; i < deleteCount; i++) { const removed = this.deleteAt(start); if (removed !== void 0) { removedList.push(removed); } } for (let i = 0; i < items.length; i++) { this.addAt(start + i, items[i]); } return removedList; } /** * Join all elements into a string. * @param separator - Separator string. * @returns Concatenated string. * @remarks Time O(n), Space O(n) */ join(separator = ",") { return this.toArray().join(separator); } /** * Snapshot elements into a reversed array. * @returns New reversed array. * @remarks Time O(n), Space O(n) */ toReversedArray() { const array = []; for (let i = this.length - 1; i >= 0; i--) { array.push(this.at(i)); } return array; } reduceRight(callbackfn, initialValue) { let accumulator = initialValue ?? 0; for (let i = this.length - 1; i >= 0; i--) { accumulator = callbackfn(accumulator, this.at(i), i, this); } return accumulator; } /** * Create a shallow copy of a subrange. * @param start - Inclusive start (supports negative index). * @param end - Exclusive end (supports negative index). * @returns New list with the range (`this` type). * @remarks Time O(n), Space O(n) */ slice(start = 0, end = this.length) { start = start < 0 ? this.length + start : start; end = end < 0 ? this.length + end : end; const newList = this._createInstance(); for (let i = start; i < end; i++) { newList.push(this.at(i)); } return newList; } /** * Fill a range with a value. * @param value - Value to set. * @param start - Inclusive start. * @param end - Exclusive end. * @returns This list. * @remarks Time O(n), Space O(1) */ fill(value, start = 0, end = this.length) { start = start < 0 ? this.length + start : start; end = end < 0 ? this.length + end : end; if (start < 0) start = 0; if (end > this.length) end = this.length; if (start >= end) return this; for (let i = start; i < end; i++) { this.setAt(i, value); } return this; } /** * Return a new instance of the same type with elements in reverse order (non-mutating). * @remarks Provided for familiarity when migrating from Array (ES2023 `toReversed`). Time O(n), Space O(n). * @returns A new reversed instance. */ toReversed() { const cloned = this.clone(); cloned.reverse(); return cloned; } }; // src/data-structures/base/iterable-entry-base.ts var IterableEntryBase = class { static { __name(this, "IterableEntryBase"); } /** * Default iterator yielding `[key, value]` entries. * @returns Iterator of `[K, V]`. * @remarks Time O(n) to iterate, Space O(1) */ *[Symbol.iterator](...args) { yield* this._getIterator(...args); } /** * Iterate over `[key, value]` pairs (may yield `undefined` values). * @returns Iterator of `[K, V | undefined]`. * @remarks Time O(n), Space O(1) */ *entries() { for (const item of this) { yield item; } } /** * Iterate over keys only. * @returns Iterator of keys. * @remarks Time O(n), Space O(1) */ *keys() { for (const item of this) { yield item[0]; } } /** * Iterate over values only. * @returns Iterator of values. * @remarks Time O(n), Space O(1) */ *values() { for (const item of this) { yield item[1]; } } /** * Test whether all entries satisfy the predicate. * @param predicate - `(key, value, index, self) => boolean`. * @param thisArg - Optional `this` for callback. * @returns `true` if all pass; otherwise `false`. * @remarks Time O(n), Space O(1) */ every(predicate, thisArg) { let index = 0; for (const item of this) { if (!predicate.call(thisArg, item[1], item[0], index++, this)) { return false; } } return true; } /** * Test whether any entry satisfies the predicate. * @param predicate - `(key, value, index, self) => boolean`. * @param thisArg - Optional `this` for callback. * @returns `true` if any passes; otherwise `false`. * @remarks Time O(n), Space O(1) */ some(predicate, thisArg) { let index = 0; for (const item of this) { if (predicate.call(thisArg, item[1], item[0], index++, this)) { return true; } } return false; } /** * Visit each entry, left-to-right. * @param callbackfn - `(key, value, index, self) => void`. * @param thisArg - Optional `this` for callback. * @remarks Time O(n), Space O(1) */ forEach(callbackfn, thisArg) { let index = 0; for (const item of this) { const [key, value] = item; callbackfn.call(thisArg, value, key, index++, this); } } /** * Find the first entry that matches a predicate. * @param callbackfn - `(key, value, index, self) => boolean`. * @param thisArg - Optional `this` for callback. * @returns Matching `[key, value]` or `undefined`. * @remarks Time O(n), Space O(1) */ find(callbackfn, thisArg) { let index = 0; for (const item of this) { const [key, value] = item; if (callbackfn.call(thisArg, value, key, index++, this)) return item; } return; } /** * Whether the given key exists. * @param key - Key to test. * @returns `true` if found; otherwise `false`. * @remarks Time O(n) generic, Space O(1) */ has(key) { for (const item of this) { const [itemKey] = item; if (itemKey === key) return true; } return false; } /** * Whether there exists an entry with the given value. * @param value - Value to test. * @returns `true` if found; otherwise `false`. * @remarks Time O(n), Space O(1) */ hasValue(value) { for (const [, elementValue] of this) { if (elementValue === value) return true; } return false; } /** * Get the value under a key. * @param key - Key to look up. * @returns Value or `undefined`. * @remarks Time O(n) generic, Space O(1) */ get(key) { for (const item of this) { const [itemKey, value] = item; if (itemKey === key) return value; } return; } /** * Reduce entries into a single accumulator. * @param callbackfn - `(acc, value, key, index, self) => acc`. * @param initialValue - Initial accumulator. * @returns Final accumulator. * @remarks Time O(n), Space O(1) */ reduce(callbackfn, initialValue) { let accumulator = initialValue; let index = 0; for (const item of this) { const [key, value] = item; accumulator = callbackfn(accumulator, value, key, index++, this); } return accumulator; } /** * Converts data structure to `[key, value]` pairs. * @returns Array of entries. * @remarks Time O(n), Space O(n) */ toArray() { return [...this]; } /** * Visualize the iterable as an array of `[key, value]` pairs (or a custom string). * @returns Array of entries (default) or a string. * @remarks Time O(n), Space O(n) */ toVisual() { return [...this]; } /** * Print a human-friendly representation to the console. * @remarks Time O(n), Space O(n) */ print() { console.log(this.toVisual()); } }; // src/data-structures/queue/queue.ts var Queue = class _Queue extends LinearBase { static { __name(this, "Queue"); } /** * Create a Queue and optionally bulk-insert elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable of elements (or raw records if toElementFn is set). * @param [options] - Options such as toElementFn, maxLen, and autoCompactRatio. * @returns New Queue instance. */ constructor(elements = [], options) { super(options); if (options) { const { autoCompactRatio = 0.5 } = options; this._autoCompactRatio = autoCompactRatio; } this.pushMany(elements); } _elements = []; /** * Get the underlying array buffer. * @remarks Time O(1), Space O(1) * @returns Backing array of elements. */ get elements() { return this._elements; } _offset = 0; /** * Get the current start offset into the array. * @remarks Time O(1), Space O(1) * @returns Zero-based offset. */ get offset() { return this._offset; } _autoCompactRatio = 0.5; /** * Get the compaction threshold (offset/size). * @remarks Time O(1), Space O(1) * @returns Auto-compaction ratio in (0,1]. */ get autoCompactRatio() { return this._autoCompactRatio; } /** * Set the compaction threshold. * @remarks Time O(1), Space O(1) * @param value - New ratio; compacts when offset/size exceeds this value. * @returns void */ set autoCompactRatio(value) { this._autoCompactRatio = value; } /** * Get the number of elements currently in the queue. * @remarks Time O(1), Space O(1) * @returns Current length. * @example * // Track queue length * const q = new Queue<number>(); * console.log(q.length); // 0; * q.push(1); * q.push(2); * console.log(q.length); // 2; */ get length() { return this.elements.length - this._offset; } /** * Get the first element (front) without removing it. * @remarks Time O(1), Space O(1) * @returns Front element or undefined. * @example * // View the front element * const q = new Queue<string>(['first', 'second', 'third']); * console.log(q.first); // 'first'; * console.log(q.length); // 3; */ get first() { return this.length > 0 ? this.elements[this._offset] : void 0; } /** * Peek at the front element without removing it (alias for `first`). * @remarks Time O(1), Space O(1) * @returns Front element or undefined. */ peek() { return this.first; } /** * Get the last element (back) without removing it. * @remarks Time O(1), Space O(1) * @returns Back element or undefined. */ get last() { return this.length > 0 ? this.elements[this.elements.length - 1] : void 0; } /** * Create a queue from an array of elements. * @remarks Time O(N), Space O(N) * @template E * @param elements - Array of elements to enqueue in order. * @returns A new queue populated from the array. */ static fromArray(elements) { return new _Queue(elements); } /** * Check whether the queue is empty. * @remarks Time O(1), Space O(1) * @returns True if length is 0. * @example * // Queue for...of iteration and isEmpty check * const queue = new Queue<string>(['A', 'B', 'C', 'D']); * * const elements: string[] = []; * for (const item of queue) { * elements.push(item); * } * * // Verify all elements are iterated in order * console.log(elements); // ['A', 'B', 'C', 'D']; * * // Process all elements * while (queue.length > 0) { * queue.shift(); * } * * console.log(queue.length); // 0; */ isEmpty() { return this.length === 0; } /** * Enqueue one element at the back. * @remarks Time O(1), Space O(1) * @param element - Element to enqueue. * @returns True on success. * @example * // basic Queue creation and push operation * // Create a simple Queue with initial values * const queue = new Queue([1, 2, 3, 4, 5]); * * // Verify the queue maintains insertion order * console.log([...queue]); // [1, 2, 3, 4, 5]; * * // Check length * console.log(queue.length); // 5; */ push(element) { this.elements.push(element); if (this._maxLen > 0 && this.length > this._maxLen) this.shift(); return true; } /** * Enqueue many elements from an iterable. * @remarks Time O(N), Space O(1) * @param elements - Iterable of elements (or raw records if toElementFn is set). * @returns Array of per-element success flags. */ pushMany(elements) { const ans = []; for (const el of elements) { if (this.toElementFn) ans.push(this.push(this.toElementFn(el))); else ans.push(this.push(el)); } return ans; } /** * Dequeue one element from the front (amortized via offset). * @remarks Time O(1) amortized, Space O(1) * @returns Removed element or undefined. * @example * // Queue shift and peek operations * const queue = new Queue<number>([10, 20, 30, 40]); * * // Peek at the front element without removing it * console.log(queue.first); // 10; * * // Remove and get the first element (FIFO) * const first = queue.shift(); * console.log(first); // 10; * * // Verify remaining elements and length decreased * console.log([...queue]); // [20, 30, 40]; * console.log(queue.length); // 3; */ shift() { if (this.length === 0) return void 0; const first = this.first; this._offset += 1; if (this.elements.length > 0 && this.offset / this.elements.length > this.autoCompactRatio) this.compact(); return first; } /** * Delete the first occurrence of a specific element. * @remarks Time O(N), Space O(1) * @param element - Element to remove (strict equality via Object.is). * @returns True if an element was removed. * @example * // Remove specific element * const q = new Queue<number>([1, 2, 3, 2]); * q.delete(2); * console.log(q.length); // 3; */ delete(element) { for (let i = this._offset; i < this.elements.length; i++) { if (Object.is(this.elements[i], element)) { this.elements.splice(i, 1); return true; } } return false; } /** * Get the element at a given logical index. * @remarks Time O(1), Space O(1) * @param index - Zero-based index from the front. * @returns Element or undefined. * @example * // Access element by index * const q = new Queue<string>(['a', 'b', 'c']); * console.log(q.at(0)); // 'a'; * console.log(q.at(2)); // 'c'; */ at(index) { if (index < 0 || index >= this.length) return void 0; return this._elements[this._offset + index]; } /** * Delete the element at a given index. * @remarks Time O(N), Space O(1) * @param index - Zero-based index from the front. * @returns Removed element or undefined. */ deleteAt(index) { if (index < 0 || index >= this.length) return void 0; const gi = this._offset + index; const [deleted] = this.elements.splice(gi, 1); return deleted; } /** * Insert a new element at a given index. * @remarks Time O(N), Space O(1) * @param index - Zero-based index from the front. * @param newElement - Element to insert. * @returns True if inserted. */ addAt(index, newElement) { if (index < 0 || index > this.length) return false; this._elements.splice(this._offset + index, 0, newElement); return true; } /** * Replace the element at a given index. * @remarks Time O(1), Space O(1) * @param index - Zero-based index from the front. * @param newElement - New element to set. * @returns True if updated. */ setAt(index, newElement) { if (index < 0 || index >= this.length) return false; this._elements[this._offset + index] = newElement; return true; } /** * Delete the first element that satisfies a predicate. * @remarks Time O(N), Space O(N) * @param predicate - Function (value, index, queue) → boolean to decide deletion. * @returns True if a match was removed. */ deleteWhere(predicate) { for (let i = 0; i < this.length; i++) { if (predicate(this._elements[this._offset + i], i, this)) { this.deleteAt(i); return true; } } return false; } /** * Reverse the queue in-place by compacting then reversing. * @remarks Time O(N), Space O(N) * @returns This queue. */ reverse() { this._elements = this.elements.slice(this._offset).reverse(); this._offset = 0; return this; } /** * Remove all elements and reset offset. * @remarks Time O(1), Space O(1) * @returns void * @example * // Remove all elements * const q = new Queue<number>([1, 2, 3]); * q.clear(); * console.log(q.length); // 0; */ clear() { this._elements = []; this._offset = 0; } /** * Compact storage by discarding consumed head elements. * @remarks Time O(N), Space O(N) * @returns True when compaction performed. * @example * // Reclaim unused memory * const q = new Queue<number>([1, 2, 3, 4, 5]); * q.shift(); * q.shift(); * q.compact(); * console.log(q.length); // 3; */ compact() { this._elements = this.elements.slice(this._offset); this._offset = 0; return true; } /** * Remove and/or insert elements at a position (array-like). * @remarks Time O(N + M), Space O(M) * @param start - Start index (clamped to [0, length]). * @param [deleteCount] - Number of elements to remove (default 0). * @param [items] - Elements to insert after `start`. * @returns A new queue containing the removed elements (typed as `this`). */ splice(start, deleteCount = 0, ...items) { start = Math.max(0, Math.min(start, this.length)); deleteCount = Math.max(0, Math.min(deleteCount, this.length - start)); const gi = this._offset + start; const removedArray = this._elements.splice(gi, deleteCount, ...items); if (this.elements.length > 0 && this.offset / this.elements.length > this.autoCompactRatio) this.compact(); const removed = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen }); removed._setAutoCompactRatio(this._autoCompactRatio); removed.pushMany(removedArray); return removed; } /** * Deep clone this queue and its parameters. * @remarks Time O(N), Space O(N) * @returns A new queue with the same content and options. * @example * // Create independent copy * const q = new Queue<number>([1, 2, 3]); * const copy = q.clone(); * copy.shift(); * console.log(q.length); // 3; * console.log(copy.length); // 2; */ clone() { const out = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen }); out._setAutoCompactRatio(this._autoCompactRatio); for (let i = this._offset; i < this.elements.length; i++) out.push(this.elements[i]); return out; } /** * Filter elements into a new queue of the same class. * @remarks Time O(N), Space O(N) * @param predicate - Predicate (element, index, queue) → boolean to keep element. * @param [thisArg] - Value for `this` inside the predicate. * @returns A new queue with kept elements. * @example * // Filter elements * const q = new Queue<number>([1, 2, 3, 4, 5]); * const evens = q.filter(x => x % 2 === 0); * console.log(evens.length); // 2; */ filter(predicate, thisArg) { const out = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen }); out._setAutoCompactRatio(this._autoCompactRatio); let index = 0; for (const v of this) { if (predicate.call(thisArg, v, index, this)) out.push(v); index++; } return out; } /** * Map each element to a new element in a possibly different-typed queue. * @remarks Time O(N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (element, index, queue) → newElement. * @param [options] - Options for the output queue (e.g., toElementFn, maxLen, autoCompactRatio). * @param [thisArg] - Value for `this` inside the callback. * @returns A new Queue with mapped elements. * @example * // Transform elements * const q = new Queue<number>([1, 2, 3]); * const doubled = q.map(x => x * 2); * console.log(doubled.toArray()); // [2, 4, 6]; */ map(callback, options, thisArg) { const out = new this.constructor([], { toElementFn: options?.toElementFn, maxLen: options?.maxLen ?? this._maxLen, autoCompactRatio: options?.autoCompactRatio ?? this._autoCompactRatio }); let index = 0; for (const v of this) out.push(thisArg === void 0 ? callback(v, index++, this) : callback.call(thisArg, v, index++, this)); return out; } /** * Map each element to a new value of the same type. * @remarks Time O(N), Space O(N) * @param callback - Mapping function (element, index, queue) → element. * @param [thisArg] - Value for `this` inside the callback. * @returns A new queue with mapped elements (same element type). */ mapSame(callback, thisArg) { const Ctor = this.constructor; const out = new Ctor([], { toElementFn: this.toElementFn, maxLen: this._maxLen, autoCompactRatio: this._autoCompactRatio }); out._setAutoCompactRatio?.(this._autoCompactRatio); let index = 0; for (const v of this) { const mv = thisArg === void 0 ? callback(v, index++, this) : callback.call(thisArg, v, index++, this); out.push(mv); } return out; } /** * (Protected) Set the internal auto-compaction ratio. * @remarks Time O(1), Space O(1) * @param value - New ratio to assign. * @returns void */ _setAutoCompactRatio(value) { this._autoCompactRatio = value; } /** * (Protected) Iterate elements from front to back. * @remarks Time O(N), Space O(1) * @returns Iterator of E. */ *_getIterator() { for (let i = this._offset; i < this.elements.length; i++) yield this.elements[i]; } /** * (Protected) Iterate elements from back to front. * @remarks Time O(N), Space O(1) * @returns Iterator of E. */ *_getReverseIterator() { for (let i = this.length - 1; i >= 0; i--) { const cur = this.at(i); if (cur !== void 0) yield cur; } } /** * (Protected) Create an empty instance of the same concrete class. * @remarks Time O(1), Space O(1) * @param [options] - Options forwarded to the constructor. * @returns An empty like-kind queue instance. */ _createInstance(options) { const Ctor = this.constructor; return new Ctor([], options); } /** * (Protected) Create a like-kind queue and seed it from an iterable. * @remarks Time O(N), Space O(N) * @template EM * @template RM * @param [elements] - Iterable used to seed the new queue. * @param [options] - Options forwarded to the constructor. * @returns A like-kind Queue instance. */ _createLike(elements = [], options) { const Ctor = this.constructor; return new Ctor(elements, options); } }; // src/data-structures/binary-tree/binary-tree.ts var BinaryTreeNode = class { static { __name(this, "BinaryTreeNode"); } key; value; parent = void 0; /** * Creates an instance of BinaryTreeNode. * @remarks Time O(1), Space O(1) * * @param key - The key of the node. * @param [value] - The value associated with the key. */ constructor(key, value) { this.key = key; this.value = value; } _left = void 0; /** * Gets the left child of the node. * @remarks Time O(1), Space O(1) * * @returns The left child. */ get left() { return this._left; } /** * Sets the left child of the node and updates its parent reference. * @remarks Time O(1), Space O(1) * * @param v - The node to set as the left child. */ set left(v) { if (v) { v.parent = this; } this._left = v; } _right = void 0; /** * Gets the right child of the node. * @remarks Time O(1), Space O(1) * * @returns The right child. */ get right() { return this._right; } /** * Sets the right child of the node and updates its parent reference. * @remarks Time O(1), Space O(1) * * @param v - The node to set as the right child. */ set right(v) { if (v) { v.parent = this; } this._right = v; } _height = 0; /** * Gets the height of the node (used in self-balancing trees). * @remarks Time O(1), Space O(1) * * @returns The height. */ get height() { return this._height; } /** * Sets the height of the node. * @remarks Time O(1), Space O(1) * * @param value - The new height. */ set height(value) { this._height = value; } _color = "BLACK"; /** * Gets the color of the node (used in Red-Black trees). * @remarks Time O(1), Space O(1) * * @returns The node's color. */ get color() { return this._color; } /** * Sets the color of the node. * @remarks Time O(1), Space O(1) * * @param value - The new color. */ set color(value) { this._color = value; } _count = 1; /** * Gets the count of nodes in the subtree rooted at this node (used in order-statistic trees). * @remarks Time O(1), Space O(1) * * @returns The subtree node count. */ get count() { return this._count; } /** * Sets the count of nodes in the subtree. * @remarks Time O(1), Space O(1) * * @param value - The new count. */ set count(value) { this._count = value; } /** * Gets the position of the node relative to its parent. * @remarks Time O(1), Space O(1) * * @returns The family position (e.g., 'ROOT', 'LEFT', 'RIGHT'). */ get familyPosition() { if (!this.parent) { return this.left || this.right ? "ROOT" : "ISOLATED"; } if (this.parent.left === this) { return this.left || this.right ? "ROOT_LEFT" : "LEFT"; } else if (this.parent.right === this) { return this.left || this.right ? "ROOT_RIGHT" : "RIGHT"; } return "MAL_NODE"; } }; var BinaryTree = class _BinaryTree extends IterableEntryBase { static { __name(this, "BinaryTree"); } iterationType = "ITERATIVE"; /** * Creates an instance of BinaryTree. * @remarks Time O(N * M), where N is the number of items in `keysNodesEntriesOrRaws` and M is the tree size at insertion time (due to O(M) `set` operation). Space O(N) for storing the nodes. * * @param [keysNodesEntriesOrRaws=[]] - An iterable of items to set. * @param [options] - Configuration options for the tree. */ constructor(keysNodesEntriesOrRaws = [], options) { super(); if (options) { const { iterationType, toEntryFn, isMapMode, isDuplicate } = options; if (iterationType) this.iterationType = iterationType; if (isMapMode !== void 0) this._isMapMode = isMapMode; if (isDuplicate !== void 0) this._isDuplicate = isDuplicate; if (typeof toEntryFn === "function") this._toEntryFn = toEntryFn; else if (toEntryFn) raise(TypeError, ERR.notAFunction("toEntryFn", "BinaryTree")); } if (keysNodesEntriesOrRaws) this.setMany(keysNodesEntriesOrRaws); } _isMapMode = true; /** * Gets whether the tree is in Map mode. * @remarks In Map mode (default), values are stored in an external Map, and nodes only hold keys. If false, values are stored directly on the nodes. Time O(1) * * @returns True if in Map mode, false otherwise. */ get isMapMode() { return this._isMapMode; } _isDuplicate = false; /** * Gets whether the tree allows duplicate keys. * @remarks Time O(1) * * @returns True if duplicates are allowed, false otherwise. */ get isDuplicate() { return this._isDuplicate; } // Map mode acceleration store: // - isMapMode=false: unused // - isMapMode=true: key -> node reference (O(1) has/getNode + fast get) _store = /* @__PURE__ */ new Map(); /** * Gets the external value store (used in Map mode). * @remarks Time O(1) * * @returns The map storing key-value pairs. */ get store() { return this._store; } _root; /** * Gets the root node of the tree. * @remarks Time O(1) * * @returns The root node. */ get root() { return this._root; } _size = 0; /** * Gets the number of nodes in the tree. * @remarks Time O(1) * * @returns The size of the tree. */ get size() { return this._size; } _NIL = new BinaryTreeNode(NaN); /** * Gets the sentinel NIL node (used in self-balancing trees like Red-Black Tree). * @remarks Time O(1) * * @returns The NIL node. */ get NIL() { return this._NIL; } _toEntryFn; /** * Gets the function used to convert raw data objects (R) into [key, value] entries. * @remarks Time O(1) * * @returns The conversion function. */ get toEntryFn() { return this._toEntryFn; } /** * (Protected) Creates a new node. * @remarks Time O(1), Space O(1) * * @param key - The key for the new node. * @param [value] - The value for the new node (used if not in Map mode). * @returns The newly created node. */ createNode(key, value) { return new BinaryTreeNode(key, value); } /** * Creates a new, empty tree of the same type and configuration. * @remarks Time O(1) (excluding options cloning), Space O(1) * * @param [options] - Optional overrides for the new tree's options. * @returns A new, empty tree instance. */ createTree(options) { return this._createInstance(options); } /** * Ensures the input is a node. If it's a key or entry, it searches for the node. * @remarks Time O(1) if a node is passed. O(N) if a key or entry is passed (due to `getNode` performing a full search). Space O(1) if iterative search, O(H) if recursive (where H is height, O(N) worst-case). * * @param keyNodeOrEntry - The item to resolve to a node. * @param [iterationType=this.iterationType] - The traversal method to use if searching. * @returns The resolved node, or null/undefined if not found or input is null/undefined. */ ensureNode(keyNodeOrEntry, iterationType = this.iterationType) { if (keyNodeOrEntry === null) return null; if (keyNodeOrEntry === void 0) return; if (keyNodeOrEntry === this._NIL) return; if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry; if (this.isEntry(keyNodeOrEntry)) { const key = keyNodeOrEntry[0]; if (key === null) return null; if (key === void 0) return; return this.getNode(key, this._root, iterationType); } return this.getNode(keyNodeOrEntry, this._root, iterationType); } /** * Checks if the given item is a `BinaryTreeNode` instance. * @remarks Time O(1), Space O(1) * * @param