UNPKG

deque-typed

Version:
1,197 lines (1,190 loc) 37.7 kB
'use strict'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/utils/utils.ts var rangeCheck = /* @__PURE__ */ __name((index, min, max, message = "Index out of bounds.") => { if (index < min || index > max) throw new RangeError(message); }, "rangeCheck"); var calcMinUnitsRequired = /* @__PURE__ */ __name((totalQuantity, unitSize) => Math.floor((totalQuantity + unitSize - 1) / unitSize), "calcMinUnitsRequired"); // 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) throw new 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; } /** * 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) throw new 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; } }; // src/data-structures/queue/deque.ts var Deque = class extends LinearBase { static { __name(this, "Deque"); } _equals = Object.is; /** * Create a Deque and optionally bulk-insert elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable (or iterable-like) of elements/records to insert. * @param [options] - Options such as bucketSize, toElementFn, and maxLen. * @returns New Deque instance. */ constructor(elements = [], options) { super(options); if (options) { const { bucketSize } = options; if (typeof bucketSize === "number") this._bucketSize = bucketSize; } let _size; if ("length" in elements) { _size = typeof elements.length === "function" ? elements.length() : elements.length; } else { _size = typeof elements.size === "function" ? elements.size() : elements.size; } this._bucketCount = calcMinUnitsRequired(_size, this._bucketSize) || 1; for (let i = 0; i < this._bucketCount; ++i) { this._buckets.push(new Array(this._bucketSize)); } const needBucketNum = calcMinUnitsRequired(_size, this._bucketSize); this._bucketFirst = this._bucketLast = (this._bucketCount >> 1) - (needBucketNum >> 1); this._firstInBucket = this._lastInBucket = this._bucketSize - _size % this._bucketSize >> 1; this.pushMany(elements); } _bucketSize = 1 << 12; /** * Get the current bucket size. * @remarks Time O(1), Space O(1) * @returns Bucket capacity per bucket. */ get bucketSize() { return this._bucketSize; } _bucketFirst = 0; /** * Get the index of the first bucket in use. * @remarks Time O(1), Space O(1) * @returns Zero-based bucket index. */ get bucketFirst() { return this._bucketFirst; } _firstInBucket = 0; /** * Get the index inside the first bucket. * @remarks Time O(1), Space O(1) * @returns Zero-based index within the first bucket. */ get firstInBucket() { return this._firstInBucket; } _bucketLast = 0; /** * Get the index of the last bucket in use. * @remarks Time O(1), Space O(1) * @returns Zero-based bucket index. */ get bucketLast() { return this._bucketLast; } _lastInBucket = 0; /** * Get the index inside the last bucket. * @remarks Time O(1), Space O(1) * @returns Zero-based index within the last bucket. */ get lastInBucket() { return this._lastInBucket; } _bucketCount = 0; /** * Get the number of buckets allocated. * @remarks Time O(1), Space O(1) * @returns Bucket count. */ get bucketCount() { return this._bucketCount; } _buckets = []; /** * Get the internal buckets array. * @remarks Time O(1), Space O(1) * @returns Array of buckets storing values. */ get buckets() { return this._buckets; } _length = 0; /** * Get the number of elements in the deque. * @remarks Time O(1), Space O(1) * @returns Current length. */ get length() { return this._length; } /** * Get the first element without removing it. * @remarks Time O(1), Space O(1) * @returns First element or undefined. */ get first() { if (this._length === 0) return; return this._buckets[this._bucketFirst][this._firstInBucket]; } /** * Get the last element without removing it. * @remarks Time O(1), Space O(1) * @returns Last element or undefined. */ get last() { if (this._length === 0) return; return this._buckets[this._bucketLast][this._lastInBucket]; } /** * Create a Deque from an array of elements. * @remarks Time O(N), Space O(N) * @template E * @template R * @param this - Constructor (subclass) to instantiate. * @param data - Array of elements to insert in order. * @param [options] - Options forwarded to the constructor. * @returns A new Deque populated from the array. */ static fromArray(data, options) { return new this(data, options); } /** * Append one element at the back. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to append. * @returns True when appended. */ push(element) { if (this._length) { if (this._lastInBucket < this._bucketSize - 1) { this._lastInBucket += 1; } else if (this._bucketLast < this._bucketCount - 1) { this._bucketLast += 1; this._lastInBucket = 0; } else { this._bucketLast = 0; this._lastInBucket = 0; } if (this._bucketLast === this._bucketFirst && this._lastInBucket === this._firstInBucket) this._reallocate(); } this._length += 1; this._buckets[this._bucketLast][this._lastInBucket] = element; if (this._maxLen > 0 && this._length > this._maxLen) this.shift(); return true; } /** * Remove and return the last element. * @remarks Time O(1), Space O(1) * @returns Removed element or undefined. */ pop() { if (this._length === 0) return; const element = this._buckets[this._bucketLast][this._lastInBucket]; if (this._length !== 1) { if (this._lastInBucket > 0) { this._lastInBucket -= 1; } else if (this._bucketLast > 0) { this._bucketLast -= 1; this._lastInBucket = this._bucketSize - 1; } else { this._bucketLast = this._bucketCount - 1; this._lastInBucket = this._bucketSize - 1; } } this._length -= 1; return element; } /** * Remove and return the first element. * @remarks Time O(1) amortized, Space O(1) * @returns Removed element or undefined. */ shift() { if (this._length === 0) return; const element = this._buckets[this._bucketFirst][this._firstInBucket]; if (this._length !== 1) { if (this._firstInBucket < this._bucketSize - 1) { this._firstInBucket += 1; } else if (this._bucketFirst < this._bucketCount - 1) { this._bucketFirst += 1; this._firstInBucket = 0; } else { this._bucketFirst = 0; this._firstInBucket = 0; } } this._length -= 1; return element; } /** * Prepend one element at the front. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to prepend. * @returns True when prepended. */ unshift(element) { if (this._length) { if (this._firstInBucket > 0) { this._firstInBucket -= 1; } else if (this._bucketFirst > 0) { this._bucketFirst -= 1; this._firstInBucket = this._bucketSize - 1; } else { this._bucketFirst = this._bucketCount - 1; this._firstInBucket = this._bucketSize - 1; } if (this._bucketFirst === this._bucketLast && this._firstInBucket === this._lastInBucket) this._reallocate(); } this._length += 1; this._buckets[this._bucketFirst][this._firstInBucket] = element; if (this._maxLen > 0 && this._length > this._maxLen) this.pop(); return true; } /** * Append a sequence of elements. * @remarks Time O(N), Space O(1) * @param elements - Iterable (or iterable-like) of elements/records. * @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; } /** * Prepend a sequence of elements. * @remarks Time O(N), Space O(1) * @param [elements] - Iterable (or iterable-like) of elements/records. * @returns Array of per-element success flags. */ unshiftMany(elements = []) { const ans = []; for (const el of elements) { if (this.toElementFn) { ans.push(this.unshift(this.toElementFn(el))); } else { ans.push(this.unshift(el)); } } return ans; } /** * Check whether the deque is empty. * @remarks Time O(1), Space O(1) * @returns True if length is 0. */ isEmpty() { return this._length === 0; } /** * Remove all elements and reset structure. * @remarks Time O(1), Space O(1) * @returns void */ clear() { this._buckets = [new Array(this._bucketSize)]; this._bucketCount = 1; this._bucketFirst = this._bucketLast = this._length = 0; this._firstInBucket = this._lastInBucket = this._bucketSize >> 1; } /** * Get the element at a given position. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position from the front. * @returns Element or undefined. */ at(pos) { if (pos < 0 || pos >= this._length) return void 0; const { bucketIndex, indexInBucket } = this._getBucketAndPosition(pos); return this._buckets[bucketIndex][indexInBucket]; } /** * Replace the element at a given position. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position from the front. * @param element - New element value. * @returns True if updated. */ setAt(pos, element) { rangeCheck(pos, 0, this._length - 1); const { bucketIndex, indexInBucket } = this._getBucketAndPosition(pos); this._buckets[bucketIndex][indexInBucket] = element; return true; } /** * Insert repeated copies of an element at a position. * @remarks Time O(N), Space O(1) * @param pos - Zero-based position from the front. * @param element - Element to insert. * @param [num] - Number of times to insert (default 1). * @returns True if inserted. */ addAt(pos, element, num = 1) { const length = this._length; rangeCheck(pos, 0, length); if (pos === 0) { while (num--) this.unshift(element); } else if (pos === this._length) { while (num--) this.push(element); } else { const arr = []; for (let i = pos; i < this._length; ++i) { const v = this.at(i); if (v !== void 0) arr.push(v); } this.cut(pos - 1, true); for (let i = 0; i < num; ++i) this.push(element); for (let i = 0; i < arr.length; ++i) this.push(arr[i]); } return true; } /** * Cut the deque to keep items up to index; optionally mutate in-place. * @remarks Time O(N), Space O(1) * @param pos - Last index to keep. * @param [isCutSelf] - When true, mutate this deque; otherwise return a new deque. * @returns This deque if in-place; otherwise a new deque of the prefix. */ cut(pos, isCutSelf = false) { if (isCutSelf) { if (pos < 0) { this.clear(); return this; } const { bucketIndex, indexInBucket } = this._getBucketAndPosition(pos); this._bucketLast = bucketIndex; this._lastInBucket = indexInBucket; this._length = pos + 1; return this; } else { const newDeque = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen }); newDeque._setBucketSize(this._bucketSize); for (let i = 0; i <= pos; i++) { const v = this.at(i); if (v !== void 0) newDeque.push(v); } return newDeque; } } /** * Remove and/or insert elements at a position (array-like behavior). * @remarks Time O(N + M), Space O(M) * @param start - Start index (clamped to [0, length]). * @param [deleteCount] - Number of elements to remove (default: length - start). * @param [items] - Elements to insert after `start`. * @returns A new deque containing the removed elements (typed as `this`). */ splice(start, deleteCount = this._length - start, ...items) { rangeCheck(start, 0, this._length); if (deleteCount < 0) deleteCount = 0; if (start + deleteCount > this._length) deleteCount = this._length - start; const removed = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen }); removed._setBucketSize(this._bucketSize); for (let i = 0; i < deleteCount; i++) { const v = this.at(start + i); if (v !== void 0) removed.push(v); } const tail = []; for (let i = start + deleteCount; i < this._length; i++) { const v = this.at(i); if (v !== void 0) tail.push(v); } this.cut(start - 1, true); for (const it of items) this.push(it); for (const v of tail) this.push(v); return removed; } /** * Cut the deque to keep items from index onward; optionally mutate in-place. * @remarks Time O(N), Space O(1) * @param pos - First index to keep. * @param [isCutSelf] - When true, mutate this deque; otherwise return a new deque. * @returns This deque if in-place; otherwise a new deque of the suffix. */ cutRest(pos, isCutSelf = false) { if (isCutSelf) { if (pos < 0) { return this; } const { bucketIndex, indexInBucket } = this._getBucketAndPosition(pos); this._bucketFirst = bucketIndex; this._firstInBucket = indexInBucket; this._length = this._length - pos; return this; } else { const newDeque = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen }); newDeque._setBucketSize(this._bucketSize); if (pos < 0) pos = 0; for (let i = pos; i < this._length; i++) { const v = this.at(i); if (v !== void 0) newDeque.push(v); } return newDeque; } } /** * Delete the element at a given position. * @remarks Time O(N), Space O(1) * @param pos - Zero-based position from the front. * @returns Removed element or undefined. */ deleteAt(pos) { rangeCheck(pos, 0, this._length - 1); let deleted; if (pos === 0) { return this.shift(); } else if (pos === this._length - 1) { deleted = this.last; this.pop(); return deleted; } else { const length = this._length - 1; const { bucketIndex: targetBucket, indexInBucket: targetPointer } = this._getBucketAndPosition(pos); deleted = this._buckets[targetBucket][targetPointer]; for (let i = pos; i < length; i++) { const { bucketIndex: curBucket, indexInBucket: curPointer } = this._getBucketAndPosition(i); const { bucketIndex: nextBucket, indexInBucket: nextPointer } = this._getBucketAndPosition(i + 1); this._buckets[curBucket][curPointer] = this._buckets[nextBucket][nextPointer]; } this.pop(); return deleted; } } /** * Delete the first occurrence of a value. * @remarks Time O(N), Space O(1) * @param element - Element to remove (using the configured equality). * @returns True if an element was removed. */ delete(element) { const size = this._length; if (size === 0) return false; let i = 0; let index = 0; while (i < size) { const oldElement = this.at(i); if (!this._equals(oldElement, element)) { this.setAt(index, oldElement); index += 1; } i += 1; } this.cut(index - 1, true); return true; } /** * Delete the first element matching a predicate. * @remarks Time O(N), Space O(1) * @param predicate - Function (value, index, deque) → boolean. * @returns True if a match was removed. */ deleteWhere(predicate) { for (let i = 0; i < this._length; i++) { const v = this.at(i); if (predicate(v, i, this)) { this.deleteAt(i); return true; } } return false; } /** * Set the equality comparator used by delete operations. * @remarks Time O(1), Space O(1) * @param equals - Equality predicate (a, b) → boolean. * @returns This deque. */ setEquality(equals) { this._equals = equals; return this; } /** * Reverse the deque by reversing buckets and pointers. * @remarks Time O(N), Space O(N) * @returns This deque. */ reverse() { this._buckets.reverse().forEach(function(bucket) { bucket.reverse(); }); const { _bucketFirst, _bucketLast, _firstInBucket, _lastInBucket } = this; this._bucketFirst = this._bucketCount - _bucketLast - 1; this._bucketLast = this._bucketCount - _bucketFirst - 1; this._firstInBucket = this._bucketSize - _lastInBucket - 1; this._lastInBucket = this._bucketSize - _firstInBucket - 1; return this; } /** * Deduplicate consecutive equal elements in-place. * @remarks Time O(N), Space O(1) * @returns This deque. */ unique() { if (this._length <= 1) { return this; } let index = 1; let prev = this.at(0); for (let i = 1; i < this._length; ++i) { const cur = this.at(i); if (!this._equals(cur, prev)) { prev = cur; this.setAt(index++, cur); } } this.cut(index - 1, true); return this; } /** * Trim unused buckets to fit exactly the active range. * @remarks Time O(N), Space O(1) * @returns void */ shrinkToFit() { if (this._length === 0) return; const newBuckets = []; if (this._bucketFirst === this._bucketLast) return; else if (this._bucketFirst < this._bucketLast) { for (let i = this._bucketFirst; i <= this._bucketLast; ++i) { newBuckets.push(this._buckets[i]); } } else { for (let i = this._bucketFirst; i < this._bucketCount; ++i) { newBuckets.push(this._buckets[i]); } for (let i = 0; i <= this._bucketLast; ++i) { newBuckets.push(this._buckets[i]); } } this._bucketFirst = 0; this._bucketLast = newBuckets.length - 1; this._buckets = newBuckets; } /** * Deep clone this deque, preserving options. * @remarks Time O(N), Space O(N) * @returns A new deque with the same content and options. */ clone() { return this._createLike(this, { bucketSize: this.bucketSize, toElementFn: this.toElementFn, maxLen: this._maxLen }); } /** * Filter elements into a new deque of the same class. * @remarks Time O(N), Space O(N) * @param predicate - Predicate (value, index, deque) → boolean to keep element. * @param [thisArg] - Value for `this` inside the predicate. * @returns A new deque with kept elements. */ filter(predicate, thisArg) { const out = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen }); out._setBucketSize(this._bucketSize); let index = 0; for (const el of this) { if (predicate.call(thisArg, el, index, this)) out.push(el); index++; } return out; } /** * Map elements into a new deque of the same element type. * @remarks Time O(N), Space O(N) * @param callback - Mapping function (value, index, deque) → newValue. * @param [thisArg] - Value for `this` inside the callback. * @returns A new deque with mapped values. */ mapSame(callback, thisArg) { const out = this._createInstance({ toElementFn: this._toElementFn, maxLen: this._maxLen }); out._setBucketSize(this._bucketSize); 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; } /** * Map elements into a new deque (possibly different element type). * @remarks Time O(N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (value, index, deque) → newElement. * @param [options] - Options for the output deque (e.g., bucketSize, toElementFn, maxLen). * @param [thisArg] - Value for `this` inside the callback. * @returns A new Deque with mapped elements. */ map(callback, options, thisArg) { const out = this._createLike([], { ...options ?? {}, bucketSize: this._bucketSize, maxLen: this._maxLen }); let index = 0; for (const el of this) { const mv = thisArg === void 0 ? callback(el, index, this) : callback.call(thisArg, el, index, this); out.push(mv); index++; } return out; } /** * (Protected) Set the internal bucket size. * @remarks Time O(1), Space O(1) * @param size - Bucket capacity to assign. * @returns void */ _setBucketSize(size) { this._bucketSize = size; if (this._length === 0) { this._buckets = [new Array(this._bucketSize)]; this._bucketCount = 1; this._bucketFirst = this._bucketLast = 0; this._firstInBucket = this._lastInBucket = this._bucketSize >> 1; } } /** * (Protected) Iterate elements from front to back. * @remarks Time O(N), Space O(1) * @returns Iterator of elements. */ *_getIterator() { for (let i = 0; i < this._length; ++i) { const v = this.at(i); if (v !== void 0) yield v; } } /** * (Protected) Reallocate buckets to make room near the ends. * @remarks Time O(N), Space O(N) * @param [needBucketNum] - How many extra buckets to add; defaults to half of current. * @returns void */ _reallocate(needBucketNum) { const newBuckets = []; const addBucketNum = needBucketNum || this._bucketCount >> 1 || 1; for (let i = 0; i < addBucketNum; ++i) { newBuckets[i] = new Array(this._bucketSize); } for (let i = this._bucketFirst; i < this._bucketCount; ++i) { newBuckets[newBuckets.length] = this._buckets[i]; } for (let i = 0; i < this._bucketLast; ++i) { newBuckets[newBuckets.length] = this._buckets[i]; } newBuckets[newBuckets.length] = [...this._buckets[this._bucketLast]]; this._bucketFirst = addBucketNum; this._bucketLast = newBuckets.length - 1; for (let i = 0; i < addBucketNum; ++i) { newBuckets[newBuckets.length] = new Array(this._bucketSize); } this._buckets = newBuckets; this._bucketCount = newBuckets.length; } /** * (Protected) Translate a logical position to bucket/offset. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position. * @returns An object containing bucketIndex and indexInBucket. */ _getBucketAndPosition(pos) { let bucketIndex; let indexInBucket; const overallIndex = this._firstInBucket + pos; bucketIndex = this._bucketFirst + Math.floor(overallIndex / this._bucketSize); if (bucketIndex >= this._bucketCount) { bucketIndex -= this._bucketCount; } indexInBucket = (overallIndex + 1) % this._bucketSize - 1; if (indexInBucket < 0) { indexInBucket = this._bucketSize - 1; } return { bucketIndex, indexInBucket }; } /** * (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 deque instance. */ _createInstance(options) { const Ctor = this.constructor; return new Ctor([], options); } /** * (Protected) Create a like-kind deque seeded by elements. * @remarks Time O(N), Space O(N) * @template T * @template RR * @param [elements] - Iterable used to seed the new deque. * @param [options] - Options forwarded to the constructor. * @returns A like-kind Deque instance. */ _createLike(elements = [], options) { const Ctor = this.constructor; return new Ctor(elements, options); } /** * (Protected) Iterate elements from back to front. * @remarks Time O(N), Space O(1) * @returns Iterator of elements. */ *_getReverseIterator() { for (let i = this._length - 1; i > -1; i--) { const v = this.at(i); if (v !== void 0) yield v; } } }; // 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; } }; /** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ exports.DFSOperation = DFSOperation; exports.Deque = Deque; exports.Range = Range; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map