UNPKG

undirected-graph-typed

Version:
2,137 lines (1,450 loc) 116 kB
'use strict'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/utils/utils.ts var uuidV4 = /* @__PURE__ */ __name(function() { return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".replace(/[x]/g, function(c) { const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8; return v.toString(16); }); }, "uuidV4"); var arrayRemove = /* @__PURE__ */ __name(function(array, predicate) { let i = -1, len = array ? array.length : 0; const result = []; while (++i < len) { const value = array[i]; if (predicate(value, i, array)) { result.push(value); Array.prototype.splice.call(array, i--, 1); len--; } } return result; }, "arrayRemove"); // 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 _Range { constructor(low, high, includeLow = true, includeHigh = true) { this.low = low; this.high = high; this.includeLow = includeLow; this.includeHigh = includeHigh; } // 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; } }; __name(_Range, "Range"); var Range = _Range; // src/data-structures/base/iterable-entry-base.ts var _IterableEntryBase = class _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()); } }; __name(_IterableEntryBase, "IterableEntryBase"); var IterableEntryBase = _IterableEntryBase; // src/data-structures/base/iterable-element-base.ts var _IterableElementBase = class _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) { /** * The converter used to transform a raw element (`R`) into a public element (`E`). * * @remarks * Time O(1), Space O(1). */ __publicField(this, "_toElementFn"); if (options) { const { toElementFn } = options; if (typeof toElementFn === "function") this._toElementFn = toElementFn; else if (toElementFn) raise(TypeError, "toElementFn must be a function type"); } } /** * 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()); } }; __name(_IterableElementBase, "IterableElementBase"); var IterableElementBase = _IterableElementBase; // src/data-structures/base/linear-base.ts var _LinearBase = class _LinearBase extends IterableElementBase { /** * Construct a linear container with runtime options. * @param options - `{ maxLen?, ... }` bounds/behavior options. * @remarks Time O(1), Space O(1) */ constructor(options) { super(options); __publicField(this, "_maxLen", -1); if (options) { const { maxLen } = options; if (typeof maxLen === "number" && maxLen > 0 && maxLen % 1 === 0) this._maxLen = maxLen; } } /** * 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 != null ? 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; } }; __name(_LinearBase, "LinearBase"); var LinearBase = _LinearBase; // src/data-structures/heap/heap.ts var _Heap = class _Heap extends IterableElementBase { /** * Create a Heap and optionally bulk-insert elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable of elements (or raw values if toElementFn is set). * @param [options] - Options such as comparator and toElementFn. * @returns New Heap instance. */ constructor(elements = [], options) { super(options); __publicField(this, "_equals", Object.is); __publicField(this, "_elements", []); __publicField(this, "_DEFAULT_COMPARATOR", /* @__PURE__ */ __name((a, b) => { if (typeof a === "object" || typeof b === "object") { raise(TypeError, ERR.comparatorRequired("Heap")); } if (a > b) return 1; if (a < b) return -1; return 0; }, "_DEFAULT_COMPARATOR")); __publicField(this, "_comparator", this._DEFAULT_COMPARATOR); if (options) { const { comparator } = options; if (comparator) this._comparator = comparator; } this.addMany(elements); } /** * Get the backing array of the heap. * @remarks Time O(1), Space O(1) * @returns Internal elements array. */ get elements() { return this._elements; } /** * Get the number of elements. * @remarks Time O(1), Space O(1) * @returns Heap size. * @example * // Track heap capacity * const heap = new Heap<number>(); * console.log(heap.size); // 0; * heap.add(10); * heap.add(20); * console.log(heap.size); // 2; * heap.poll(); * console.log(heap.size); // 1; */ get size() { return this.elements.length; } /** * Get the last leaf element. * @remarks Time O(1), Space O(1) * @returns Last element or undefined. */ get leaf() { var _a; return (_a = this.elements[this.size - 1]) != null ? _a : void 0; } /** * Create a heap of the same class from an iterable. * @remarks Time O(N), Space O(N) * @template T * @template R * @template S * @param [elements] - Iterable of elements or raw records. * @param [options] - Heap options including comparator. * @returns A new heap instance of this class. */ static from(elements, options) { return new this(elements, options); } /** * Build a Heap from an iterable in linear time given a comparator. * @remarks Time O(N), Space O(N) * @template EE * @template RR * @param elements - Iterable of elements. * @param options - Heap options including comparator. * @returns A new Heap built from elements. */ static heapify(elements, options) { return new _Heap(elements, options); } /** * Insert an element. * @remarks Time O(log N) amortized, Space O(1) * @param element - Element to insert. * @returns True. * @example * // basic Heap creation and add operation * // Create a min heap (default) * const minHeap = new Heap([5, 3, 7, 1, 9, 2]); * * // Verify size * console.log(minHeap.size); // 6; * * // Add new element * minHeap.add(4); * console.log(minHeap.size); // 7; * * // Min heap property: smallest element at root * const min = minHeap.peek(); * console.log(min); // 1; */ add(element) { this._elements.push(element); return this._bubbleUp(this.elements.length - 1); } /** * Insert many elements from an iterable. * @remarks Time O(N log N), Space O(1) * @param elements - Iterable of elements or raw values. * @returns Array of per-element success flags. * @example * // Add multiple elements * const heap = new Heap<number>([], { comparator: (a, b) => a - b }); * heap.addMany([5, 3, 7, 1]); * console.log(heap.peek()); // 1; * console.log(heap.size); // 4; */ addMany(elements) { const flags = []; for (const el of elements) { if (this.toElementFn) { const ok = this.add(this.toElementFn(el)); flags.push(ok); } else { const ok = this.add(el); flags.push(ok); } } return flags; } /** * Remove and return the top element. * @remarks Time O(log N), Space O(1) * @returns Top element or undefined. * @example * // Heap with custom comparator (MaxHeap behavior) * interface Task { * id: number; * priority: number; * name: string; * } * * // Custom comparator for max heap behavior (higher priority first) * const tasks: Task[] = [ * { id: 1, priority: 5, name: 'Email' }, * { id: 2, priority: 3, name: 'Chat' }, * { id: 3, priority: 8, name: 'Alert' } * ]; * * const maxHeap = new Heap(tasks, { * comparator: (a: Task, b: Task) => b.priority - a.priority * }); * * console.log(maxHeap.size); // 3; * * // Peek returns highest priority task * const topTask = maxHeap.peek(); * console.log(topTask?.priority); // 8; * console.log(topTask?.name); // 'Alert'; */ /** * @deprecated Use `pop` instead. Will be removed in a future major version. * @example * // Heap with custom comparator (MaxHeap behavior) * interface Task { * id: number; * priority: number; * name: string; * } * * // Custom comparator for max heap behavior (higher priority first) * const tasks: Task[] = [ * { id: 1, priority: 5, name: 'Email' }, * { id: 2, priority: 3, name: 'Chat' }, * { id: 3, priority: 8, name: 'Alert' } * ]; * * const maxHeap = new Heap(tasks, { * comparator: (a: Task, b: Task) => b.priority - a.priority * }); * * console.log(maxHeap.size); // 3; * * // Peek returns highest priority task * const topTask = maxHeap.peek(); * console.log(topTask?.priority); // 8; * console.log(topTask?.name); // 'Alert'; */ poll() { return this.pop(); } /** * Remove and return the top element (min or max depending on comparator). * @remarks Time O(log N) amortized, Space O(1) * @returns The removed top element, or undefined if empty. */ pop() { if (this.elements.length === 0) return; const value = this.elements[0]; const last = this.elements.pop(); if (this.elements.length) { this.elements[0] = last; this._sinkDown(0, this.elements.length >> 1); } return value; } /** * Get the current top element without removing it. * @remarks Time O(1), Space O(1) * @returns Top element or undefined. * @example * // Heap for event processing with priority * interface Event { * id: number; * type: 'critical' | 'warning' | 'info'; * timestamp: number; * message: string; * } * * // Custom priority: critical > warning > info * const priorityMap = { critical: 3, warning: 2, info: 1 }; * * const eventHeap = new Heap<Event>([], { * comparator: (a: Event, b: Event) => { * const priorityA = priorityMap[a.type]; * const priorityB = priorityMap[b.type]; * return priorityB - priorityA; // Higher priority first * } * }); * * // Add events in random order * eventHeap.add({ id: 1, type: 'info', timestamp: 100, message: 'User logged in' }); * eventHeap.add({ id: 2, type: 'critical', timestamp: 101, message: 'Server down' }); * eventHeap.add({ id: 3, type: 'warning', timestamp: 102, message: 'High memory' }); * eventHeap.add({ id: 4, type: 'info', timestamp: 103, message: 'Cache cleared' }); * eventHeap.add({ id: 5, type: 'critical', timestamp: 104, message: 'Database error' }); * * console.log(eventHeap.size); // 5; * * // Process events by priority (critical first) * const processedOrder: Event[] = []; * while (eventHeap.size > 0) { * const event = eventHeap.poll(); * if (event) { * processedOrder.push(event); * } * } * * // Verify critical events came first * console.log(processedOrder[0].type); // 'critical'; * console.log(processedOrder[1].type); // 'critical'; * console.log(processedOrder[2].type); // 'warning'; * console.log(processedOrder[3].type); // 'info'; * console.log(processedOrder[4].type); // 'info'; * * // Verify O(log n) operations * const newHeap = new Heap<number>([5, 3, 7, 1]); * * // Add - O(log n) * newHeap.add(2); * console.log(newHeap.size); // 5; * * // Poll - O(log n) * const removed = newHeap.poll(); * console.log(removed); // 1; * * // Peek - O(1) * const top = newHeap.peek(); * console.log(top); // 2; */ peek() { return this.elements[0]; } /** * Check whether the heap is empty. * @remarks Time O(1), Space O(1) * @returns True if size is 0. * @example * // Check if heap is empty * const heap = new Heap<number>([], { comparator: (a, b) => a - b }); * console.log(heap.isEmpty()); // true; * heap.add(1); * console.log(heap.isEmpty()); // false; */ isEmpty() { return this.size === 0; } /** * Remove all elements. * @remarks Time O(1), Space O(1) * @returns void * @example * // Remove all elements * const heap = new Heap<number>([1, 2, 3], { comparator: (a, b) => a - b }); * heap.clear(); * console.log(heap.isEmpty()); // true; */ clear() { this._elements = []; } /** * Check if an equal element exists in the heap. * @remarks Time O(N), Space O(1) * @param element - Element to search for. * @returns True if found. * @example * // Check element existence * const heap = new Heap<number>([3, 1, 2], { comparator: (a, b) => a - b }); * console.log(heap.has(1)); // true; * console.log(heap.has(99)); // false; */ has(element) { for (const el of this.elements) if (this._equals(el, element)) return true; return false; } /** * Delete one occurrence of an element. * @remarks Time O(N), Space O(1) * @param element - Element to delete. * @returns True if an element was removed. * @example * // Remove specific element * const heap = new Heap<number>([3, 1, 4, 1, 5], { comparator: (a, b) => a - b }); * heap.delete(4); * console.log(heap.toArray().includes(4)); // false; */ delete(element) { let index = -1; for (let i = 0; i < this.elements.length; i++) { if (this._equals(this.elements[i], element)) { index = i; break; } } if (index < 0) return false; if (index === 0) { this.pop(); } else if (index === this.elements.length - 1) { this.elements.pop(); } else { this.elements.splice(index, 1, this.elements.pop()); this._bubbleUp(index); this._sinkDown(index, this.elements.length >> 1); } return true; } /** * @deprecated Use `deleteWhere` instead. Will be removed in a future major version. */ deleteBy(predicate) { return this.deleteWhere(predicate); } /** * Delete the first element that matches a predicate. * @remarks Time O(N), Space O(1) * @param predicate - Function (element, index, heap) → boolean. * @returns True if an element was removed. */ deleteWhere(predicate) { let idx = -1; for (let i = 0; i < this.elements.length; i++) { if (predicate(this.elements[i], i, this)) { idx = i; break; } } if (idx < 0) return false; if (idx === 0) { this.pop(); } else if (idx === this.elements.length - 1) { this.elements.pop(); } else { this.elements.splice(idx, 1, this.elements.pop()); this._bubbleUp(idx); this._sinkDown(idx, this.elements.length >> 1); } return true; } /** * Set the equality comparator used by has/delete operations. * @remarks Time O(1), Space O(1) * @param equals - Equality predicate (a, b) → boolean. * @returns This heap. */ setEquality(equals) { this._equals = equals; return this; } /** * Traverse the binary heap as a complete binary tree and collect elements. * @remarks Time O(N), Space O(H) * @param [order] - Traversal order: 'PRE' | 'IN' | 'POST'. * @returns Array of visited elements. * @example * // Depth-first traversal * const heap = new Heap<number>([3, 1, 2], { comparator: (a, b) => a - b }); * const result = heap.dfs('IN'); * console.log(result.length); // 3; */ dfs(order = "PRE") { const result = []; const _dfs = /* @__PURE__ */ __name((index) => { const left = 2 * index + 1, right = left + 1; if (index < this.size) { if (order === "IN") { _dfs(left); result.push(this.elements[index]); _dfs(right); } else if (order === "PRE") { result.push(this.elements[index]); _dfs(left); _dfs(right); } else if (order === "POST") { _dfs(left); _dfs(right); result.push(this.elements[index]); } } }, "_dfs"); _dfs(0); return result; } /** * Restore heap order bottom-up (heapify in-place). * @remarks Time O(N), Space O(1) * @returns Array of per-node results from fixing steps. */ fix() { const results = []; for (let i = Math.floor(this.size / 2) - 1; i >= 0; i--) { results.push(this._sinkDown(i, this.elements.length >> 1)); } return results; } /** * Return all elements in ascending order by repeatedly polling. * @remarks Time O(N log N), Space O(N) * @returns Sorted array of elements. * @example * // Sort elements using heap * const heap = new Heap<number>([5, 1, 3, 2, 4]); * const sorted = heap.sort(); * console.log(sorted); // [1, 2, 3, 4, 5]; */ sort() { const visited = []; const cloned = this._createInstance(); for (const x of this.elements) cloned.add(x); while (!cloned.isEmpty()) { const top = cloned.poll(); if (top !== void 0) visited.push(top); } return visited; } /** * Deep clone this heap. * @remarks Time O(N), Space O(N) * @returns A new heap with the same elements. * @example * // Create independent copy * const heap = new Heap<number>([3, 1, 4], { comparator: (a, b) => a - b }); * const copy = heap.clone(); * copy.poll(); * console.log(heap.size); // 3; * console.log(copy.size); // 2; */ clone() { const next = this._createInstance(); for (const x of this.elements) next.add(x); return next; } /** * Filter elements into a new heap of the same class. * @remarks Time O(N log N), Space O(N) * @param callback - Predicate (element, index, heap) → boolean to keep element. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with the kept elements. * @example * // Filter elements * const heap = new Heap<number>([1, 2, 3, 4, 5], { comparator: (a, b) => a - b }); * const evens = heap.filter(x => x % 2 === 0); * console.log(evens.size); // 2; */ filter(callback, thisArg) { const out = this._createInstance(); let i = 0; for (const x of this) { if (thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this)) { out.add(x); } else { i++; } } return out; } /** * Map elements into a new heap of possibly different element type. * @remarks Time O(N log N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (element, index, heap) → newElement. * @param options - Options for the output heap, including comparator for EM. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with mapped elements. * @example * // Transform elements * const heap = new Heap<number>([1, 2, 3], { comparator: (a, b) => a - b }); * const doubled = heap.map(x => x * 2, { comparator: (a, b) => a - b }); * console.log(doubled.peek()); // 2; */ map(callback, options, thisArg) { const { comparator, toElementFn, ...rest } = options != null ? options : {}; if (!comparator) raise(TypeError, ERR.comparatorRequired("Heap.map")); const out = this._createLike([], { ...rest, comparator, toElementFn }); let i = 0; for (const x of this) { const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this); out.add(v); } return out; } /** * Map elements into a new heap of the same element type. * @remarks Time O(N log N), Space O(N) * @param callback - Mapping function (element, index, heap) → element. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with mapped elements. */ mapSame(callback, thisArg) { const out = this._createInstance(); let i = 0; for (const x of this) { const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this); out.add(v); } return out; } /** * Get the comparator used to order elements. * @remarks Time O(1), Space O(1) * @returns Comparator function. */ get comparator() { return this._comparator; } *_getIterator() { for (const element of this.elements) yield element; } _bubbleUp(index) { const element = this.elements[index]; while (index > 0) { const parent = index - 1 >> 1; const parentItem = this.elements[parent]; if (this.comparator(parentItem, element) <= 0) break; this.elements[index] = parentItem; index = parent; } this.elements[index] = element; return true; } _sinkDown(index, halfLength) { const element = this.elements[index]; while (index < halfLength) { let left = index << 1 | 1; const right = left + 1; let minItem = this.elements[left]; if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) { left = right; minItem = this.elements[right]; } if (this.comparator(minItem, element) >= 0) break; this.elements[index] = minItem; index = left; } this.elements[index] = element; return true; } /** * (Protected) Create an empty instance of the same concrete class. * @remarks Time O(1), Space O(1) * @param [options] - Options to override comparator or toElementFn. * @returns A like-kind empty heap instance. */ _createInstance(options) { const Ctor = this.constructor; return new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options != null ? options : {} }); } /** * (Protected) Create a like-kind instance seeded by elements. * @remarks Time O(N log N), Space O(N) * @template EM * @template RM * @param [elements] - Iterable of elements or raw values to seed. * @param [options] - Options forwarded to the constructor. * @returns A like-kind heap instance. */ _createLike(elements = [], options) { const Ctor = this.constructor; return new Ctor(elements, options); } /** * (Protected) Spawn an empty like-kind heap instance. * @remarks Time O(1), Space O(1) * @template EM * @template RM * @param [options] - Options forwarded to the constructor. * @returns An empty like-kind heap instance. */ _spawnLike(options) { return this._createLike([], options); } }; __name(_Heap, "Heap"); var Heap = _Heap; // src/data-structures/queue/queue.ts var _Queue = class _Queue extends LinearBase { /** * 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); __publicField(this, "_elements", []); __publicField(this, "_offset", 0); __publicField(this, "_autoCompactRatio", 0.5); if (options) { const { autoCompactRatio = 0.5 } = options; this._autoCompactRatio = autoCompactRatio; } this.pushMany(elements); } /** * Get the underlying array buffer. * @remarks Time O(1), Space O(1) * @returns Backing array of elements. */ get elements() { return this._elements; } /** * Get the current start offset into the array. * @remarks Time O(1), Space O(1) * @returns Zero-based offset. */ get offset() { return this._offset; } /** * 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 e