undirected-graph-typed
Version:
Undirected Graph
2,135 lines (1,449 loc) • 116 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/utils/utils.ts
var uuidV4 = /* @__PURE__ */ __name(function() {
return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".replace(/[x]/g, function(c) {
const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
return v.toString(16);
});
}, "uuidV4");
var arrayRemove = /* @__PURE__ */ __name(function(array, predicate) {
let i = -1, len = array ? array.length : 0;
const result = [];
while (++i < len) {
const value = array[i];
if (predicate(value, i, array)) {
result.push(value);
Array.prototype.splice.call(array, i--, 1);
len--;
}
}
return result;
}, "arrayRemove");
// src/common/error.ts
function raise(ErrorClass, message) {
throw new ErrorClass(message);
}
__name(raise, "raise");
var ERR = {
// Range / index
indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"),
invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"),
// Type / argument
invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"),
comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"),
invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"),
notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"),
invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"),
invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"),
invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"),
reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"),
callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"),
// State / operation
invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"),
// Matrix
matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"),
matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"),
matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"),
matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"),
matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch"),
// Order statistic
orderStatisticNotEnabled: /* @__PURE__ */ __name((method, ctx) => `${ctx ? ctx + ": " : ""}${method}() requires enableOrderStatistic: true.`, "orderStatisticNotEnabled")
};
// src/common/index.ts
var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
return DFSOperation2;
})(DFSOperation || {});
var _Range = class _Range {
constructor(low, high, includeLow = true, includeHigh = true) {
this.low = low;
this.high = high;
this.includeLow = includeLow;
this.includeHigh = includeHigh;
}
// Determine whether a key is within the range
isInRange(key, comparator) {
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
return lowCheck && highCheck;
}
};
__name(_Range, "Range");
var Range = _Range;
// src/data-structures/base/iterable-entry-base.ts
var _IterableEntryBase = class _IterableEntryBase {
/**
* Default iterator yielding `[key, value]` entries.
* @returns Iterator of `[K, V]`.
* @remarks Time O(n) to iterate, Space O(1)
*/
*[Symbol.iterator](...args) {
yield* this._getIterator(...args);
}
/**
* Iterate over `[key, value]` pairs (may yield `undefined` values).
* @returns Iterator of `[K, V | undefined]`.
* @remarks Time O(n), Space O(1)
*/
*entries() {
for (const item of this) {
yield item;
}
}
/**
* Iterate over keys only.
* @returns Iterator of keys.
* @remarks Time O(n), Space O(1)
*/
*keys() {
for (const item of this) {
yield item[0];
}
}
/**
* Iterate over values only.
* @returns Iterator of values.
* @remarks Time O(n), Space O(1)
*/
*values() {
for (const item of this) {
yield item[1];
}
}
/**
* Test whether all entries satisfy the predicate.
* @param predicate - `(key, value, index, self) => boolean`.
* @param thisArg - Optional `this` for callback.
* @returns `true` if all pass; otherwise `false`.
* @remarks Time O(n), Space O(1)
*/
every(predicate, thisArg) {
let index = 0;
for (const item of this) {
if (!predicate.call(thisArg, item[1], item[0], index++, this)) {
return false;
}
}
return true;
}
/**
* Test whether any entry satisfies the predicate.
* @param predicate - `(key, value, index, self) => boolean`.
* @param thisArg - Optional `this` for callback.
* @returns `true` if any passes; otherwise `false`.
* @remarks Time O(n), Space O(1)
*/
some(predicate, thisArg) {
let index = 0;
for (const item of this) {
if (predicate.call(thisArg, item[1], item[0], index++, this)) {
return true;
}
}
return false;
}
/**
* Visit each entry, left-to-right.
* @param callbackfn - `(key, value, index, self) => void`.
* @param thisArg - Optional `this` for callback.
* @remarks Time O(n), Space O(1)
*/
forEach(callbackfn, thisArg) {
let index = 0;
for (const item of this) {
const [key, value] = item;
callbackfn.call(thisArg, value, key, index++, this);
}
}
/**
* Find the first entry that matches a predicate.
* @param callbackfn - `(key, value, index, self) => boolean`.
* @param thisArg - Optional `this` for callback.
* @returns Matching `[key, value]` or `undefined`.
* @remarks Time O(n), Space O(1)
*/
find(callbackfn, thisArg) {
let index = 0;
for (const item of this) {
const [key, value] = item;
if (callbackfn.call(thisArg, value, key, index++, this)) return item;
}
return;
}
/**
* Whether the given key exists.
* @param key - Key to test.
* @returns `true` if found; otherwise `false`.
* @remarks Time O(n) generic, Space O(1)
*/
has(key) {
for (const item of this) {
const [itemKey] = item;
if (itemKey === key) return true;
}
return false;
}
/**
* Whether there exists an entry with the given value.
* @param value - Value to test.
* @returns `true` if found; otherwise `false`.
* @remarks Time O(n), Space O(1)
*/
hasValue(value) {
for (const [, elementValue] of this) {
if (elementValue === value) return true;
}
return false;
}
/**
* Get the value under a key.
* @param key - Key to look up.
* @returns Value or `undefined`.
* @remarks Time O(n) generic, Space O(1)
*/
get(key) {
for (const item of this) {
const [itemKey, value] = item;
if (itemKey === key) return value;
}
return;
}
/**
* Reduce entries into a single accumulator.
* @param callbackfn - `(acc, value, key, index, self) => acc`.
* @param initialValue - Initial accumulator.
* @returns Final accumulator.
* @remarks Time O(n), Space O(1)
*/
reduce(callbackfn, initialValue) {
let accumulator = initialValue;
let index = 0;
for (const item of this) {
const [key, value] = item;
accumulator = callbackfn(accumulator, value, key, index++, this);
}
return accumulator;
}
/**
* Converts data structure to `[key, value]` pairs.
* @returns Array of entries.
* @remarks Time O(n), Space O(n)
*/
toArray() {
return [...this];
}
/**
* Visualize the iterable as an array of `[key, value]` pairs (or a custom string).
* @returns Array of entries (default) or a string.
* @remarks Time O(n), Space O(n)
*/
toVisual() {
return [...this];
}
/**
* Print a human-friendly representation to the console.
* @remarks Time O(n), Space O(n)
*/
print() {
console.log(this.toVisual());
}
};
__name(_IterableEntryBase, "IterableEntryBase");
var IterableEntryBase = _IterableEntryBase;
// src/data-structures/base/iterable-element-base.ts
var _IterableElementBase = class _IterableElementBase {
/**
* Create a new iterable base.
*
* @param options Optional behavior overrides. When provided, a `toElementFn`
* is used to convert a raw element (`R`) into a public element (`E`).
*
* @remarks
* Time O(1), Space O(1).
*/
constructor(options) {
/**
* The converter used to transform a raw element (`R`) into a public element (`E`).
*
* @remarks
* Time O(1), Space O(1).
*/
__publicField(this, "_toElementFn");
if (options) {
const { toElementFn } = options;
if (typeof toElementFn === "function") this._toElementFn = toElementFn;
else if (toElementFn) raise(TypeError, "toElementFn must be a function type");
}
}
/**
* Exposes the current `toElementFn`, if configured.
*
* @returns The converter function or `undefined` when not set.
* @remarks
* Time O(1), Space O(1).
*/
get toElementFn() {
return this._toElementFn;
}
/**
* Returns an iterator over the structure's elements.
*
* @param args Optional iterator arguments forwarded to the internal iterator.
* @returns An `IterableIterator<E>` that yields the elements in traversal order.
*
* @remarks
* Producing the iterator is O(1); consuming the entire iterator is Time O(n) with O(1) extra space.
*/
*[Symbol.iterator](...args) {
yield* this._getIterator(...args);
}
/**
* Returns an iterator over the values (alias of the default iterator).
*
* @returns An `IterableIterator<E>` over all elements.
* @remarks
* Creating the iterator is O(1); full iteration is Time O(n), Space O(1).
*/
*values() {
for (const item of this) yield item;
}
/**
* Tests whether all elements satisfy the predicate.
*
* @template TReturn
* @param predicate Function invoked for each element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the predicate.
* @returns `true` if every element passes; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case; may exit early when the first failure is found. Space O(1).
*/
every(predicate, thisArg) {
let index = 0;
for (const item of this) {
if (thisArg === void 0) {
if (!predicate(item, index++, this)) return false;
} else {
const fn = predicate;
if (!fn.call(thisArg, item, index++, this)) return false;
}
}
return true;
}
/**
* Tests whether at least one element satisfies the predicate.
*
* @param predicate Function invoked for each element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the predicate.
* @returns `true` if any element passes; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case; may exit early on first success. Space O(1).
*/
some(predicate, thisArg) {
let index = 0;
for (const item of this) {
if (thisArg === void 0) {
if (predicate(item, index++, this)) return true;
} else {
const fn = predicate;
if (fn.call(thisArg, item, index++, this)) return true;
}
}
return false;
}
/**
* Invokes a callback for each element in iteration order.
*
* @param callbackfn Function invoked per element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the callback.
* @returns `void`.
*
* @remarks
* Time O(n), Space O(1).
*/
forEach(callbackfn, thisArg) {
let index = 0;
for (const item of this) {
if (thisArg === void 0) {
callbackfn(item, index++, this);
} else {
const fn = callbackfn;
fn.call(thisArg, item, index++, this);
}
}
}
// Implementation signature
find(predicate, thisArg) {
let index = 0;
for (const item of this) {
if (thisArg === void 0) {
if (predicate(item, index++, this)) return item;
} else {
const fn = predicate;
if (fn.call(thisArg, item, index++, this)) return item;
}
}
return;
}
/**
* Checks whether a strictly-equal element exists in the structure.
*
* @param element The element to test with `===` equality.
* @returns `true` if an equal element is found; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case. Space O(1).
*/
has(element) {
for (const ele of this) if (ele === element) return true;
return false;
}
/**
* Check whether a value exists (Array-compatible alias for `has`).
* @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1).
* @param element - Element to search for (uses `===`).
* @returns `true` if found.
*/
includes(element) {
return this.has(element);
}
/**
* Return an iterator of `[index, value]` pairs (Array-compatible).
* @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1) per step.
*/
*entries() {
let index = 0;
for (const value of this) {
yield [index++, value];
}
}
/**
* Return an iterator of numeric indices (Array-compatible).
* @remarks Provided for familiarity when migrating from Array. Time O(n), Space O(1) per step.
*/
*keys() {
let index = 0;
for (const _ of this) {
yield index++;
}
}
/**
* Reduces all elements to a single accumulated value.
*
* @overload
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`. The first element is used as the initial accumulator.
* @returns The final accumulated value typed as `E`.
*
* @overload
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`.
* @param initialValue The initial accumulator value of type `E`.
* @returns The final accumulated value typed as `E`.
*
* @overload
* @template U The accumulator type when it differs from `E`.
* @param callbackfn Reducer of signature `(acc: U, value, index, self) => U`.
* @param initialValue The initial accumulator value of type `U`.
* @returns The final accumulated value typed as `U`.
*
* @remarks
* Time O(n), Space O(1). Throws if called on an empty structure without `initialValue`.
*/
reduce(callbackfn, initialValue) {
let index = 0;
const iter = this[Symbol.iterator]();
let acc;
if (arguments.length >= 2) {
acc = initialValue;
} else {
const first = iter.next();
if (first.done) raise(TypeError, "Reduce of empty structure with no initial value");
acc = first.value;
index = 1;
}
for (const value of iter) {
acc = callbackfn(acc, value, index++, this);
}
return acc;
}
/**
* Materializes the elements into a new array.
*
* @returns A shallow array copy of the iteration order.
* @remarks
* Time O(n), Space O(n).
*/
toArray() {
return [...this];
}
/**
* Returns a representation of the structure suitable for quick visualization.
* Defaults to an array of elements; subclasses may override to provide richer visuals.
*
* @returns A visual representation (array by default).
* @remarks
* Time O(n), Space O(n).
*/
toVisual() {
return [...this];
}
/**
* Prints `toVisual()` to the console. Intended for quick debugging.
*
* @returns `void`.
* @remarks
* Time O(n) due to materialization, Space O(n) for the intermediate representation.
*/
print() {
console.log(this.toVisual());
}
};
__name(_IterableElementBase, "IterableElementBase");
var IterableElementBase = _IterableElementBase;
// src/data-structures/base/linear-base.ts
var _LinearBase = class _LinearBase extends IterableElementBase {
/**
* Construct a linear container with runtime options.
* @param options - `{ maxLen?, ... }` bounds/behavior options.
* @remarks Time O(1), Space O(1)
*/
constructor(options) {
super(options);
__publicField(this, "_maxLen", -1);
if (options) {
const { maxLen } = options;
if (typeof maxLen === "number" && maxLen > 0 && maxLen % 1 === 0) this._maxLen = maxLen;
}
}
/**
* Upper bound for length (if positive), or `-1` when unbounded.
* @returns Maximum allowed length.
* @remarks Time O(1), Space O(1)
*/
get maxLen() {
return this._maxLen;
}
/**
* First index of a value from the left.
* @param searchElement - Value to match.
* @param fromIndex - Start position (supports negative index).
* @returns Index or `-1` if not found.
* @remarks Time O(n), Space O(1)
*/
indexOf(searchElement, fromIndex = 0) {
if (this.length === 0) return -1;
if (fromIndex < 0) fromIndex = this.length + fromIndex;
if (fromIndex < 0) fromIndex = 0;
for (let i = fromIndex; i < this.length; i++) {
const element = this.at(i);
if (element === searchElement) return i;
}
return -1;
}
/**
* Last index of a value from the right.
* @param searchElement - Value to match.
* @param fromIndex - Start position (supports negative index).
* @returns Index or `-1` if not found.
* @remarks Time O(n), Space O(1)
*/
lastIndexOf(searchElement, fromIndex = this.length - 1) {
if (this.length === 0) return -1;
if (fromIndex >= this.length) fromIndex = this.length - 1;
if (fromIndex < 0) fromIndex = this.length + fromIndex;
for (let i = fromIndex; i >= 0; i--) {
const element = this.at(i);
if (element === searchElement) return i;
}
return -1;
}
/**
* Find the first index matching a predicate.
* @param predicate - `(element, index, self) => boolean`.
* @param thisArg - Optional `this` for callback.
* @returns Index or `-1`.
* @remarks Time O(n), Space O(1)
*/
findIndex(predicate, thisArg) {
for (let i = 0; i < this.length; i++) {
const item = this.at(i);
if (item !== void 0 && predicate.call(thisArg, item, i, this)) return i;
}
return -1;
}
/**
* Concatenate elements and/or containers.
* @param items - Elements or other containers.
* @returns New container with combined elements (`this` type).
* @remarks Time O(sum(length)), Space O(sum(length))
*/
concat(...items) {
const newList = this.clone();
for (const item of items) {
if (item instanceof _LinearBase) {
newList.pushMany(item);
} else {
newList.push(item);
}
}
return newList;
}
/**
* In-place stable order via array sort semantics.
* @param compareFn - Comparator `(a, b) => number`.
* @returns This container.
* @remarks Time O(n log n), Space O(n) (materializes to array temporarily)
*/
sort(compareFn) {
const arr = this.toArray();
arr.sort(compareFn);
this.clear();
for (const item of arr) this.push(item);
return this;
}
/**
* Remove and/or insert elements at a position (array-compatible).
* @param start - Start index (supports negative index).
* @param deleteCount - How many to remove.
* @param items - Elements to insert.
* @returns Removed elements as a new list (`this` type).
* @remarks Time O(n + m), Space O(min(n, m)) where `m = items.length`
*/
splice(start, deleteCount = 0, ...items) {
const removedList = this._createInstance();
start = start < 0 ? this.length + start : start;
start = Math.max(0, Math.min(start, this.length));
deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
for (let i = 0; i < deleteCount; i++) {
const removed = this.deleteAt(start);
if (removed !== void 0) {
removedList.push(removed);
}
}
for (let i = 0; i < items.length; i++) {
this.addAt(start + i, items[i]);
}
return removedList;
}
/**
* Join all elements into a string.
* @param separator - Separator string.
* @returns Concatenated string.
* @remarks Time O(n), Space O(n)
*/
join(separator = ",") {
return this.toArray().join(separator);
}
/**
* Snapshot elements into a reversed array.
* @returns New reversed array.
* @remarks Time O(n), Space O(n)
*/
toReversedArray() {
const array = [];
for (let i = this.length - 1; i >= 0; i--) {
array.push(this.at(i));
}
return array;
}
reduceRight(callbackfn, initialValue) {
let accumulator = initialValue != null ? initialValue : 0;
for (let i = this.length - 1; i >= 0; i--) {
accumulator = callbackfn(accumulator, this.at(i), i, this);
}
return accumulator;
}
/**
* Create a shallow copy of a subrange.
* @param start - Inclusive start (supports negative index).
* @param end - Exclusive end (supports negative index).
* @returns New list with the range (`this` type).
* @remarks Time O(n), Space O(n)
*/
slice(start = 0, end = this.length) {
start = start < 0 ? this.length + start : start;
end = end < 0 ? this.length + end : end;
const newList = this._createInstance();
for (let i = start; i < end; i++) {
newList.push(this.at(i));
}
return newList;
}
/**
* Fill a range with a value.
* @param value - Value to set.
* @param start - Inclusive start.
* @param end - Exclusive end.
* @returns This list.
* @remarks Time O(n), Space O(1)
*/
fill(value, start = 0, end = this.length) {
start = start < 0 ? this.length + start : start;
end = end < 0 ? this.length + end : end;
if (start < 0) start = 0;
if (end > this.length) end = this.length;
if (start >= end) return this;
for (let i = start; i < end; i++) {
this.setAt(i, value);
}
return this;
}
/**
* Return a new instance of the same type with elements in reverse order (non-mutating).
* @remarks Provided for familiarity when migrating from Array (ES2023 `toReversed`). Time O(n), Space O(n).
* @returns A new reversed instance.
*/
toReversed() {
const cloned = this.clone();
cloned.reverse();
return cloned;
}
};
__name(_LinearBase, "LinearBase");
var LinearBase = _LinearBase;
// src/data-structures/heap/heap.ts
var _Heap = class _Heap extends IterableElementBase {
/**
* Create a Heap and optionally bulk-insert elements.
* @remarks Time O(N), Space O(N)
* @param [elements] - Iterable of elements (or raw values if toElementFn is set).
* @param [options] - Options such as comparator and toElementFn.
* @returns New Heap instance.
*/
constructor(elements = [], options) {
super(options);
__publicField(this, "_equals", Object.is);
__publicField(this, "_elements", []);
__publicField(this, "_DEFAULT_COMPARATOR", /* @__PURE__ */ __name((a, b) => {
if (typeof a === "object" || typeof b === "object") {
raise(TypeError, ERR.comparatorRequired("Heap"));
}
if (a > b) return 1;
if (a < b) return -1;
return 0;
}, "_DEFAULT_COMPARATOR"));
__publicField(this, "_comparator", this._DEFAULT_COMPARATOR);
if (options) {
const { comparator } = options;
if (comparator) this._comparator = comparator;
}
this.addMany(elements);
}
/**
* Get the backing array of the heap.
* @remarks Time O(1), Space O(1)
* @returns Internal elements array.
*/
get elements() {
return this._elements;
}
/**
* Get the number of elements.
* @remarks Time O(1), Space O(1)
* @returns Heap size.
* @example
* // Track heap capacity
* const heap = new Heap<number>();
* console.log(heap.size); // 0;
* heap.add(10);
* heap.add(20);
* console.log(heap.size); // 2;
* heap.poll();
* console.log(heap.size); // 1;
*/
get size() {
return this.elements.length;
}
/**
* Get the last leaf element.
* @remarks Time O(1), Space O(1)
* @returns Last element or undefined.
*/
get leaf() {
var _a;
return (_a = this.elements[this.size - 1]) != null ? _a : void 0;
}
/**
* Create a heap of the same class from an iterable.
* @remarks Time O(N), Space O(N)
* @template T
* @template R
* @template S
* @param [elements] - Iterable of elements or raw records.
* @param [options] - Heap options including comparator.
* @returns A new heap instance of this class.
*/
static from(elements, options) {
return new this(elements, options);
}
/**
* Build a Heap from an iterable in linear time given a comparator.
* @remarks Time O(N), Space O(N)
* @template EE
* @template RR
* @param elements - Iterable of elements.
* @param options - Heap options including comparator.
* @returns A new Heap built from elements.
*/
static heapify(elements, options) {
return new _Heap(elements, options);
}
/**
* Insert an element.
* @remarks Time O(log N) amortized, Space O(1)
* @param element - Element to insert.
* @returns True.
* @example
* // basic Heap creation and add operation
* // Create a min heap (default)
* const minHeap = new Heap([5, 3, 7, 1, 9, 2]);
*
* // Verify size
* console.log(minHeap.size); // 6;
*
* // Add new element
* minHeap.add(4);
* console.log(minHeap.size); // 7;
*
* // Min heap property: smallest element at root
* const min = minHeap.peek();
* console.log(min); // 1;
*/
add(element) {
this._elements.push(element);
return this._bubbleUp(this.elements.length - 1);
}
/**
* Insert many elements from an iterable.
* @remarks Time O(N log N), Space O(1)
* @param elements - Iterable of elements or raw values.
* @returns Array of per-element success flags.
* @example
* // Add multiple elements
* const heap = new Heap<number>([], { comparator: (a, b) => a - b });
* heap.addMany([5, 3, 7, 1]);
* console.log(heap.peek()); // 1;
* console.log(heap.size); // 4;
*/
addMany(elements) {
const flags = [];
for (const el of elements) {
if (this.toElementFn) {
const ok = this.add(this.toElementFn(el));
flags.push(ok);
} else {
const ok = this.add(el);
flags.push(ok);
}
}
return flags;
}
/**
* Remove and return the top element.
* @remarks Time O(log N), Space O(1)
* @returns Top element or undefined.
* @example
* // Heap with custom comparator (MaxHeap behavior)
* interface Task {
* id: number;
* priority: number;
* name: string;
* }
*
* // Custom comparator for max heap behavior (higher priority first)
* const tasks: Task[] = [
* { id: 1, priority: 5, name: 'Email' },
* { id: 2, priority: 3, name: 'Chat' },
* { id: 3, priority: 8, name: 'Alert' }
* ];
*
* const maxHeap = new Heap(tasks, {
* comparator: (a: Task, b: Task) => b.priority - a.priority
* });
*
* console.log(maxHeap.size); // 3;
*
* // Peek returns highest priority task
* const topTask = maxHeap.peek();
* console.log(topTask?.priority); // 8;
* console.log(topTask?.name); // 'Alert';
*/
/**
* @deprecated Use `pop` instead. Will be removed in a future major version.
* @example
* // Heap with custom comparator (MaxHeap behavior)
* interface Task {
* id: number;
* priority: number;
* name: string;
* }
*
* // Custom comparator for max heap behavior (higher priority first)
* const tasks: Task[] = [
* { id: 1, priority: 5, name: 'Email' },
* { id: 2, priority: 3, name: 'Chat' },
* { id: 3, priority: 8, name: 'Alert' }
* ];
*
* const maxHeap = new Heap(tasks, {
* comparator: (a: Task, b: Task) => b.priority - a.priority
* });
*
* console.log(maxHeap.size); // 3;
*
* // Peek returns highest priority task
* const topTask = maxHeap.peek();
* console.log(topTask?.priority); // 8;
* console.log(topTask?.name); // 'Alert';
*/
poll() {
return this.pop();
}
/**
* Remove and return the top element (min or max depending on comparator).
* @remarks Time O(log N) amortized, Space O(1)
* @returns The removed top element, or undefined if empty.
*/
pop() {
if (this.elements.length === 0) return;
const value = this.elements[0];
const last = this.elements.pop();
if (this.elements.length) {
this.elements[0] = last;
this._sinkDown(0, this.elements.length >> 1);
}
return value;
}
/**
* Get the current top element without removing it.
* @remarks Time O(1), Space O(1)
* @returns Top element or undefined.
* @example
* // Heap for event processing with priority
* interface Event {
* id: number;
* type: 'critical' | 'warning' | 'info';
* timestamp: number;
* message: string;
* }
*
* // Custom priority: critical > warning > info
* const priorityMap = { critical: 3, warning: 2, info: 1 };
*
* const eventHeap = new Heap<Event>([], {
* comparator: (a: Event, b: Event) => {
* const priorityA = priorityMap[a.type];
* const priorityB = priorityMap[b.type];
* return priorityB - priorityA; // Higher priority first
* }
* });
*
* // Add events in random order
* eventHeap.add({ id: 1, type: 'info', timestamp: 100, message: 'User logged in' });
* eventHeap.add({ id: 2, type: 'critical', timestamp: 101, message: 'Server down' });
* eventHeap.add({ id: 3, type: 'warning', timestamp: 102, message: 'High memory' });
* eventHeap.add({ id: 4, type: 'info', timestamp: 103, message: 'Cache cleared' });
* eventHeap.add({ id: 5, type: 'critical', timestamp: 104, message: 'Database error' });
*
* console.log(eventHeap.size); // 5;
*
* // Process events by priority (critical first)
* const processedOrder: Event[] = [];
* while (eventHeap.size > 0) {
* const event = eventHeap.poll();
* if (event) {
* processedOrder.push(event);
* }
* }
*
* // Verify critical events came first
* console.log(processedOrder[0].type); // 'critical';
* console.log(processedOrder[1].type); // 'critical';
* console.log(processedOrder[2].type); // 'warning';
* console.log(processedOrder[3].type); // 'info';
* console.log(processedOrder[4].type); // 'info';
*
* // Verify O(log n) operations
* const newHeap = new Heap<number>([5, 3, 7, 1]);
*
* // Add - O(log n)
* newHeap.add(2);
* console.log(newHeap.size); // 5;
*
* // Poll - O(log n)
* const removed = newHeap.poll();
* console.log(removed); // 1;
*
* // Peek - O(1)
* const top = newHeap.peek();
* console.log(top); // 2;
*/
peek() {
return this.elements[0];
}
/**
* Check whether the heap is empty.
* @remarks Time O(1), Space O(1)
* @returns True if size is 0.
* @example
* // Check if heap is empty
* const heap = new Heap<number>([], { comparator: (a, b) => a - b });
* console.log(heap.isEmpty()); // true;
* heap.add(1);
* console.log(heap.isEmpty()); // false;
*/
isEmpty() {
return this.size === 0;
}
/**
* Remove all elements.
* @remarks Time O(1), Space O(1)
* @returns void
* @example
* // Remove all elements
* const heap = new Heap<number>([1, 2, 3], { comparator: (a, b) => a - b });
* heap.clear();
* console.log(heap.isEmpty()); // true;
*/
clear() {
this._elements = [];
}
/**
* Check if an equal element exists in the heap.
* @remarks Time O(N), Space O(1)
* @param element - Element to search for.
* @returns True if found.
* @example
* // Check element existence
* const heap = new Heap<number>([3, 1, 2], { comparator: (a, b) => a - b });
* console.log(heap.has(1)); // true;
* console.log(heap.has(99)); // false;
*/
has(element) {
for (const el of this.elements) if (this._equals(el, element)) return true;
return false;
}
/**
* Delete one occurrence of an element.
* @remarks Time O(N), Space O(1)
* @param element - Element to delete.
* @returns True if an element was removed.
* @example
* // Remove specific element
* const heap = new Heap<number>([3, 1, 4, 1, 5], { comparator: (a, b) => a - b });
* heap.delete(4);
* console.log(heap.toArray().includes(4)); // false;
*/
delete(element) {
let index = -1;
for (let i = 0; i < this.elements.length; i++) {
if (this._equals(this.elements[i], element)) {
index = i;
break;
}
}
if (index < 0) return false;
if (index === 0) {
this.pop();
} else if (index === this.elements.length - 1) {
this.elements.pop();
} else {
this.elements.splice(index, 1, this.elements.pop());
this._bubbleUp(index);
this._sinkDown(index, this.elements.length >> 1);
}
return true;
}
/**
* @deprecated Use `deleteWhere` instead. Will be removed in a future major version.
*/
deleteBy(predicate) {
return this.deleteWhere(predicate);
}
/**
* Delete the first element that matches a predicate.
* @remarks Time O(N), Space O(1)
* @param predicate - Function (element, index, heap) → boolean.
* @returns True if an element was removed.
*/
deleteWhere(predicate) {
let idx = -1;
for (let i = 0; i < this.elements.length; i++) {
if (predicate(this.elements[i], i, this)) {
idx = i;
break;
}
}
if (idx < 0) return false;
if (idx === 0) {
this.pop();
} else if (idx === this.elements.length - 1) {
this.elements.pop();
} else {
this.elements.splice(idx, 1, this.elements.pop());
this._bubbleUp(idx);
this._sinkDown(idx, this.elements.length >> 1);
}
return true;
}
/**
* Set the equality comparator used by has/delete operations.
* @remarks Time O(1), Space O(1)
* @param equals - Equality predicate (a, b) → boolean.
* @returns This heap.
*/
setEquality(equals) {
this._equals = equals;
return this;
}
/**
* Traverse the binary heap as a complete binary tree and collect elements.
* @remarks Time O(N), Space O(H)
* @param [order] - Traversal order: 'PRE' | 'IN' | 'POST'.
* @returns Array of visited elements.
* @example
* // Depth-first traversal
* const heap = new Heap<number>([3, 1, 2], { comparator: (a, b) => a - b });
* const result = heap.dfs('IN');
* console.log(result.length); // 3;
*/
dfs(order = "PRE") {
const result = [];
const _dfs = /* @__PURE__ */ __name((index) => {
const left = 2 * index + 1, right = left + 1;
if (index < this.size) {
if (order === "IN") {
_dfs(left);
result.push(this.elements[index]);
_dfs(right);
} else if (order === "PRE") {
result.push(this.elements[index]);
_dfs(left);
_dfs(right);
} else if (order === "POST") {
_dfs(left);
_dfs(right);
result.push(this.elements[index]);
}
}
}, "_dfs");
_dfs(0);
return result;
}
/**
* Restore heap order bottom-up (heapify in-place).
* @remarks Time O(N), Space O(1)
* @returns Array of per-node results from fixing steps.
*/
fix() {
const results = [];
for (let i = Math.floor(this.size / 2) - 1; i >= 0; i--) {
results.push(this._sinkDown(i, this.elements.length >> 1));
}
return results;
}
/**
* Return all elements in ascending order by repeatedly polling.
* @remarks Time O(N log N), Space O(N)
* @returns Sorted array of elements.
* @example
* // Sort elements using heap
* const heap = new Heap<number>([5, 1, 3, 2, 4]);
* const sorted = heap.sort();
* console.log(sorted); // [1, 2, 3, 4, 5];
*/
sort() {
const visited = [];
const cloned = this._createInstance();
for (const x of this.elements) cloned.add(x);
while (!cloned.isEmpty()) {
const top = cloned.poll();
if (top !== void 0) visited.push(top);
}
return visited;
}
/**
* Deep clone this heap.
* @remarks Time O(N), Space O(N)
* @returns A new heap with the same elements.
* @example
* // Create independent copy
* const heap = new Heap<number>([3, 1, 4], { comparator: (a, b) => a - b });
* const copy = heap.clone();
* copy.poll();
* console.log(heap.size); // 3;
* console.log(copy.size); // 2;
*/
clone() {
const next = this._createInstance();
for (const x of this.elements) next.add(x);
return next;
}
/**
* Filter elements into a new heap of the same class.
* @remarks Time O(N log N), Space O(N)
* @param callback - Predicate (element, index, heap) → boolean to keep element.
* @param [thisArg] - Value for `this` inside the callback.
* @returns A new heap with the kept elements.
* @example
* // Filter elements
* const heap = new Heap<number>([1, 2, 3, 4, 5], { comparator: (a, b) => a - b });
* const evens = heap.filter(x => x % 2 === 0);
* console.log(evens.size); // 2;
*/
filter(callback, thisArg) {
const out = this._createInstance();
let i = 0;
for (const x of this) {
if (thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this)) {
out.add(x);
} else {
i++;
}
}
return out;
}
/**
* Map elements into a new heap of possibly different element type.
* @remarks Time O(N log N), Space O(N)
* @template EM
* @template RM
* @param callback - Mapping function (element, index, heap) → newElement.
* @param options - Options for the output heap, including comparator for EM.
* @param [thisArg] - Value for `this` inside the callback.
* @returns A new heap with mapped elements.
* @example
* // Transform elements
* const heap = new Heap<number>([1, 2, 3], { comparator: (a, b) => a - b });
* const doubled = heap.map(x => x * 2, { comparator: (a, b) => a - b });
* console.log(doubled.peek()); // 2;
*/
map(callback, options, thisArg) {
const { comparator, toElementFn, ...rest } = options != null ? options : {};
if (!comparator) raise(TypeError, ERR.comparatorRequired("Heap.map"));
const out = this._createLike([], { ...rest, comparator, toElementFn });
let i = 0;
for (const x of this) {
const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
out.add(v);
}
return out;
}
/**
* Map elements into a new heap of the same element type.
* @remarks Time O(N log N), Space O(N)
* @param callback - Mapping function (element, index, heap) → element.
* @param [thisArg] - Value for `this` inside the callback.
* @returns A new heap with mapped elements.
*/
mapSame(callback, thisArg) {
const out = this._createInstance();
let i = 0;
for (const x of this) {
const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
out.add(v);
}
return out;
}
/**
* Get the comparator used to order elements.
* @remarks Time O(1), Space O(1)
* @returns Comparator function.
*/
get comparator() {
return this._comparator;
}
*_getIterator() {
for (const element of this.elements) yield element;
}
_bubbleUp(index) {
const element = this.elements[index];
while (index > 0) {
const parent = index - 1 >> 1;
const parentItem = this.elements[parent];
if (this.comparator(parentItem, element) <= 0) break;
this.elements[index] = parentItem;
index = parent;
}
this.elements[index] = element;
return true;
}
_sinkDown(index, halfLength) {
const element = this.elements[index];
while (index < halfLength) {
let left = index << 1 | 1;
const right = left + 1;
let minItem = this.elements[left];
if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) {
left = right;
minItem = this.elements[right];
}
if (this.comparator(minItem, element) >= 0) break;
this.elements[index] = minItem;
index = left;
}
this.elements[index] = element;
return true;
}
/**
* (Protected) Create an empty instance of the same concrete class.
* @remarks Time O(1), Space O(1)
* @param [options] - Options to override comparator or toElementFn.
* @returns A like-kind empty heap instance.
*/
_createInstance(options) {
const Ctor = this.constructor;
return new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options != null ? options : {} });
}
/**
* (Protected) Create a like-kind instance seeded by elements.
* @remarks Time O(N log N), Space O(N)
* @template EM
* @template RM
* @param [elements] - Iterable of elements or raw values to seed.
* @param [options] - Options forwarded to the constructor.
* @returns A like-kind heap instance.
*/
_createLike(elements = [], options) {
const Ctor = this.constructor;
return new Ctor(elements, options);
}
/**
* (Protected) Spawn an empty like-kind heap instance.
* @remarks Time O(1), Space O(1)
* @template EM
* @template RM
* @param [options] - Options forwarded to the constructor.
* @returns An empty like-kind heap instance.
*/
_spawnLike(options) {
return this._createLike([], options);
}
};
__name(_Heap, "Heap");
var Heap = _Heap;
// src/data-structures/queue/queue.ts
var _Queue = class _Queue extends LinearBase {
/**
* Create a Queue and optionally bulk-insert elements.
* @remarks Time O(N), Space O(N)
* @param [elements] - Iterable of elements (or raw records if toElementFn is set).
* @param [options] - Options such as toElementFn, maxLen, and autoCompactRatio.
* @returns New Queue instance.
*/
constructor(elements = [], options) {
super(options);
__publicField(this, "_elements", []);
__publicField(this, "_offset", 0);
__publicField(this, "_autoCompactRatio", 0.5);
if (options) {
const { autoCompactRatio = 0.5 } = options;
this._autoCompactRatio = autoCompactRatio;
}
this.pushMany(elements);
}
/**
* Get the underlying array buffer.
* @remarks Time O(1), Space O(1)
* @returns Backing array of elements.
*/
get elements() {
return this._elements;
}
/**
* Get the current start offset into the array.
* @remarks Time O(1), Space O(1)
* @returns Zero-based offset.
*/
get offset() {
return this._offset;
}
/**
* Get the compaction threshold (offset/size).
* @remarks Time O(1), Space O(1)
* @returns Auto-compaction ratio in (0,1].
*/
get autoCompactRatio() {
return this._autoCompactRatio;
}
/**
* Set the compaction threshold.
* @remarks Time O(1), Space O(1)
* @param value - New ratio; compacts when offset/size exceeds this value.
* @returns void
*/
set autoCompactRatio(value) {
this._autoCompactRatio = value;
}
/**
* Get the number of elements currently in the queue.
* @remarks Time O(1), Space O(1)
* @returns Current length.
* @example
* // Track queue length
* const q = new Queue<number>();
* console.log(q.length); // 0;
* q.push(1);
* q.push(2);
* console.log(q.length); // 2;
*/
get length() {
return this.elements.length - this._offset;
}
/**
* Get the first element (front) without removing it.
* @remarks Time O(1), Space O(1)
* @returns Front element or undefined.
* @example
* // View the front element
* const q = new Queue<string>(['first', 'second', 'third']);
* console.log(q.first); // 'first';
* console.log(q.length); // 3;
*/
get first() {
return this.length > 0 ? this.elements[this._offset] : void 0;
}
/**
* Peek at the front element without removing it (alias for `first`).
* @remarks Time O(1), Space O(1)
* @returns Front element or undefined.
*/
peek() {
return this.first;
}
/**
* Get the last element (back) without removing it.
* @remarks Time O(1), Space O(1)
* @returns Back element or undefined.
*/
get last() {
return this.length > 0 ? this.elements[this.elements.length - 1] : void 0;
}
/**
* Create a queue from an array of elements.
* @remarks Time O(N), Space O(N)
* @template E
* @param elements - Array of elements to enqueue in order.
* @returns A new queue populated from the array.
*/
static fromArray(elements) {
return new _Queue(elements);
}
/**
* Check whether the queue is empty.
* @remarks Time O(1), Space O(1)
* @returns True if length is 0.
* @example
* // Queue for...of iteration and isEmpty check
* const queue = new Queue<string>(['A', 'B', 'C', 'D']);
*
* const elements: string[] = [];
* for (const item of queue) {
* elements.push(item);
* }
*
* // Verify all elements are ite