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