deque-typed
Version:
1,197 lines (1,190 loc) • 37.7 kB
JavaScript
'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