data-structure-typed 2.4.3 → 2.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/.github/workflows/release.yml +27 -0
  2. package/CHANGELOG.md +24 -1
  3. package/README.md +70 -51
  4. package/dist/cjs/index.cjs +486 -167
  5. package/dist/cjs-legacy/index.cjs +487 -165
  6. package/dist/esm/index.mjs +486 -168
  7. package/dist/esm-legacy/index.mjs +487 -166
  8. package/dist/types/common/error.d.ts +23 -0
  9. package/dist/types/common/index.d.ts +1 -0
  10. package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
  11. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +15 -5
  12. package/dist/types/data-structures/binary-tree/bst.d.ts +1 -1
  13. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +7 -1
  14. package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
  15. package/dist/types/data-structures/graph/directed-graph.d.ts +3 -2
  16. package/dist/types/data-structures/graph/undirected-graph.d.ts +16 -2
  17. package/dist/types/data-structures/hash/hash-map.d.ts +2 -2
  18. package/dist/types/data-structures/heap/heap.d.ts +3 -7
  19. package/dist/types/data-structures/queue/deque.d.ts +41 -1
  20. package/dist/types/types/data-structures/binary-tree/avl-tree.d.ts +1 -1
  21. package/dist/types/types/data-structures/binary-tree/red-black-tree.d.ts +1 -1
  22. package/dist/types/types/data-structures/linked-list/doubly-linked-list.d.ts +1 -1
  23. package/dist/types/types/data-structures/linked-list/singly-linked-list.d.ts +1 -1
  24. package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +1 -1
  25. package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
  26. package/dist/types/types/data-structures/stack/stack.d.ts +1 -1
  27. package/dist/umd/data-structure-typed.js +486 -164
  28. package/dist/umd/data-structure-typed.min.js +6 -4
  29. package/package.json +2 -2
  30. package/src/common/error.ts +60 -0
  31. package/src/common/index.ts +2 -0
  32. package/src/data-structures/base/iterable-element-base.ts +5 -4
  33. package/src/data-structures/binary-tree/binary-indexed-tree.ts +6 -5
  34. package/src/data-structures/binary-tree/binary-tree.ts +121 -49
  35. package/src/data-structures/binary-tree/bst.ts +12 -4
  36. package/src/data-structures/binary-tree/red-black-tree.ts +20 -0
  37. package/src/data-structures/binary-tree/tree-map.ts +8 -7
  38. package/src/data-structures/binary-tree/tree-multi-map.ts +4 -4
  39. package/src/data-structures/binary-tree/tree-multi-set.ts +10 -9
  40. package/src/data-structures/binary-tree/tree-set.ts +7 -6
  41. package/src/data-structures/graph/abstract-graph.ts +124 -19
  42. package/src/data-structures/graph/directed-graph.ts +8 -4
  43. package/src/data-structures/graph/map-graph.ts +1 -1
  44. package/src/data-structures/graph/undirected-graph.ts +99 -4
  45. package/src/data-structures/hash/hash-map.ts +19 -6
  46. package/src/data-structures/heap/heap.ts +21 -17
  47. package/src/data-structures/heap/max-heap.ts +2 -3
  48. package/src/data-structures/linked-list/doubly-linked-list.ts +4 -4
  49. package/src/data-structures/linked-list/singly-linked-list.ts +15 -9
  50. package/src/data-structures/matrix/matrix.ts +9 -10
  51. package/src/data-structures/priority-queue/max-priority-queue.ts +2 -3
  52. package/src/data-structures/queue/deque.ts +72 -4
  53. package/src/data-structures/stack/stack.ts +1 -1
  54. package/src/data-structures/trie/trie.ts +12 -6
  55. package/src/types/data-structures/binary-tree/avl-tree.ts +1 -1
  56. package/src/types/data-structures/binary-tree/red-black-tree.ts +1 -1
  57. package/src/types/data-structures/linked-list/doubly-linked-list.ts +1 -1
  58. package/src/types/data-structures/linked-list/singly-linked-list.ts +1 -1
  59. package/src/types/data-structures/priority-queue/priority-queue.ts +1 -1
  60. package/src/types/data-structures/queue/deque.ts +7 -0
  61. package/src/types/data-structures/stack/stack.ts +1 -1
  62. package/src/utils/utils.ts +4 -2
@@ -183,6 +183,55 @@ var IterableEntryBase = class {
183
183
  }
184
184
  };
185
185
 
186
+ // src/common/error.ts
187
+ var ERR = {
188
+ // Range / index
189
+ indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"),
190
+ invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"),
191
+ // Type / argument
192
+ invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"),
193
+ comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"),
194
+ invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"),
195
+ notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"),
196
+ invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"),
197
+ invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"),
198
+ invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"),
199
+ reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"),
200
+ callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"),
201
+ // State / operation
202
+ invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"),
203
+ // Matrix
204
+ matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"),
205
+ matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"),
206
+ matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"),
207
+ matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"),
208
+ matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch")
209
+ };
210
+
211
+ // src/common/index.ts
212
+ var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
213
+ DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
214
+ DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
215
+ return DFSOperation2;
216
+ })(DFSOperation || {});
217
+ var Range = class {
218
+ constructor(low, high, includeLow = true, includeHigh = true) {
219
+ this.low = low;
220
+ this.high = high;
221
+ this.includeLow = includeLow;
222
+ this.includeHigh = includeHigh;
223
+ }
224
+ static {
225
+ __name(this, "Range");
226
+ }
227
+ // Determine whether a key is within the range
228
+ isInRange(key, comparator) {
229
+ const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
230
+ const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
231
+ return lowCheck && highCheck;
232
+ }
233
+ };
234
+
186
235
  // src/data-structures/base/iterable-element-base.ts
187
236
  var IterableElementBase = class {
188
237
  static {
@@ -201,7 +250,7 @@ var IterableElementBase = class {
201
250
  if (options) {
202
251
  const { toElementFn } = options;
203
252
  if (typeof toElementFn === "function") this._toElementFn = toElementFn;
204
- else if (toElementFn) throw new TypeError("toElementFn must be a function type");
253
+ else if (toElementFn) throw new TypeError(ERR.notAFunction("toElementFn"));
205
254
  }
206
255
  }
207
256
  /**
@@ -364,7 +413,7 @@ var IterableElementBase = class {
364
413
  acc = initialValue;
365
414
  } else {
366
415
  const first = iter.next();
367
- if (first.done) throw new TypeError("Reduce of empty structure with no initial value");
416
+ if (first.done) throw new TypeError(ERR.reduceEmpty());
368
417
  acc = first.value;
369
418
  index = 1;
370
419
  }
@@ -432,8 +481,10 @@ var getMSB = /* @__PURE__ */ __name((value) => {
432
481
  }
433
482
  return 1 << 31 - Math.clz32(value);
434
483
  }, "getMSB");
435
- var rangeCheck = /* @__PURE__ */ __name((index, min, max, message = "Index out of bounds.") => {
436
- if (index < min || index > max) throw new RangeError(message);
484
+ var rangeCheck = /* @__PURE__ */ __name((index, min, max, message) => {
485
+ if (index < min || index > max) {
486
+ throw new RangeError(message ?? `Index ${index} is out of range [${min}, ${max}].`);
487
+ }
437
488
  }, "rangeCheck");
438
489
  var throwRangeError = /* @__PURE__ */ __name((message = "The value is off-limits.") => {
439
490
  throw new RangeError(message);
@@ -856,8 +907,8 @@ var LinkedHashMap = class extends IterableEntryBase {
856
907
  if (this.isEntry(rawElement)) {
857
908
  return rawElement;
858
909
  }
859
- throw new Error(
860
- "If `entryOrRawElements` does not adhere to [key,value], provide `options.toEntryFn` to transform raw records."
910
+ throw new TypeError(
911
+ ERR.invalidArgument("If elements do not adhere to [key, value], provide options.toEntryFn to transform raw records.", "HashMap")
861
912
  );
862
913
  }, "_toEntryFn");
863
914
  get toEntryFn() {
@@ -1019,8 +1070,9 @@ var LinkedHashMap = class extends IterableEntryBase {
1019
1070
  const cur = node;
1020
1071
  node = node.next;
1021
1072
  if (predicate(cur.key, cur.value, i++, this)) {
1022
- if (isWeakKey(cur.key)) {
1023
- this._objMap.delete(cur.key);
1073
+ const keyToCheck = cur.key;
1074
+ if (isWeakKey(keyToCheck)) {
1075
+ this._objMap.delete(keyToCheck);
1024
1076
  } else {
1025
1077
  const hash = this._hashFn(cur.key);
1026
1078
  delete this._noObjMap[hash];
@@ -1093,6 +1145,13 @@ var LinkedHashMap = class extends IterableEntryBase {
1093
1145
  }
1094
1146
  }
1095
1147
  _deleteNode(node) {
1148
+ const key = node.key;
1149
+ if (isWeakKey(key)) {
1150
+ this._objMap.delete(key);
1151
+ } else {
1152
+ const hash = this._hashFn(key);
1153
+ delete this._noObjMap[hash];
1154
+ }
1096
1155
  const { prev, next } = node;
1097
1156
  prev.next = next;
1098
1157
  next.prev = prev;
@@ -1534,7 +1593,7 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1534
1593
  static {
1535
1594
  __name(this, "SinglyLinkedList");
1536
1595
  }
1537
- _equals = Object.is;
1596
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
1538
1597
  /**
1539
1598
  * Create a SinglyLinkedList and optionally bulk-insert elements.
1540
1599
  * @remarks Time O(N), Space O(N)
@@ -1638,8 +1697,8 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1638
1697
  return value2;
1639
1698
  }
1640
1699
  let current = this.head;
1641
- while (current.next !== this.tail) current = current.next;
1642
- const value = this.tail.value;
1700
+ while (current.next && current.next !== this.tail) current = current.next;
1701
+ const value = this.tail?.value;
1643
1702
  current.next = void 0;
1644
1703
  this._tail = current;
1645
1704
  this._length--;
@@ -1727,8 +1786,8 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1727
1786
  at(index) {
1728
1787
  if (index < 0 || index >= this._length) return void 0;
1729
1788
  let current = this.head;
1730
- for (let i = 0; i < index; i++) current = current.next;
1731
- return current.value;
1789
+ for (let i = 0; i < index && current; i++) current = current.next;
1790
+ return current?.value;
1732
1791
  }
1733
1792
  /**
1734
1793
  * Type guard: check whether the input is a SinglyLinkedListNode.
@@ -1748,7 +1807,7 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1748
1807
  getNodeAt(index) {
1749
1808
  if (index < 0 || index >= this._length) return void 0;
1750
1809
  let current = this.head;
1751
- for (let i = 0; i < index; i++) current = current.next;
1810
+ for (let i = 0; i < index && current; i++) current = current.next;
1752
1811
  return current;
1753
1812
  }
1754
1813
  /**
@@ -2257,7 +2316,7 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2257
2316
  static {
2258
2317
  __name(this, "DoublyLinkedList");
2259
2318
  }
2260
- _equals = Object.is;
2319
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
2261
2320
  /**
2262
2321
  * Create a DoublyLinkedList and optionally bulk-insert elements.
2263
2322
  * @remarks Time O(N), Space O(N)
@@ -2452,8 +2511,8 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2452
2511
  at(index) {
2453
2512
  if (index < 0 || index >= this._length) return void 0;
2454
2513
  let current = this.head;
2455
- for (let i = 0; i < index; i++) current = current.next;
2456
- return current.value;
2514
+ for (let i = 0; i < index && current; i++) current = current.next;
2515
+ return current?.value;
2457
2516
  }
2458
2517
  /**
2459
2518
  * Get the node reference at a given index.
@@ -2464,7 +2523,7 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2464
2523
  getNodeAt(index) {
2465
2524
  if (index < 0 || index >= this._length) return void 0;
2466
2525
  let current = this.head;
2467
- for (let i = 0; i < index; i++) current = current.next;
2526
+ for (let i = 0; i < index && current; i++) current = current.next;
2468
2527
  return current;
2469
2528
  }
2470
2529
  /**
@@ -2970,7 +3029,7 @@ var Stack = class extends IterableElementBase {
2970
3029
  static {
2971
3030
  __name(this, "Stack");
2972
3031
  }
2973
- _equals = Object.is;
3032
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
2974
3033
  /**
2975
3034
  * Create a Stack and optionally bulk-push elements.
2976
3035
  * @remarks Time O(N), Space O(N)
@@ -3606,19 +3665,13 @@ var Deque = class extends LinearBase {
3606
3665
  static {
3607
3666
  __name(this, "Deque");
3608
3667
  }
3609
- _equals = Object.is;
3610
- /**
3611
- * Create a Deque and optionally bulk-insert elements.
3612
- * @remarks Time O(N), Space O(N)
3613
- * @param [elements] - Iterable (or iterable-like) of elements/records to insert.
3614
- * @param [options] - Options such as bucketSize, toElementFn, and maxLen.
3615
- * @returns New Deque instance.
3616
- */
3668
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
3617
3669
  constructor(elements = [], options) {
3618
3670
  super(options);
3619
3671
  if (options) {
3620
- const { bucketSize } = options;
3672
+ const { bucketSize, autoCompactRatio } = options;
3621
3673
  if (typeof bucketSize === "number") this._bucketSize = bucketSize;
3674
+ if (typeof autoCompactRatio === "number") this._autoCompactRatio = autoCompactRatio;
3622
3675
  }
3623
3676
  let _size;
3624
3677
  if ("length" in elements) {
@@ -3644,6 +3697,30 @@ var Deque = class extends LinearBase {
3644
3697
  get bucketSize() {
3645
3698
  return this._bucketSize;
3646
3699
  }
3700
+ _autoCompactRatio = 0.5;
3701
+ /**
3702
+ * Get the auto-compaction ratio.
3703
+ * When `elements / (bucketCount * bucketSize)` drops below this ratio after
3704
+ * enough shift/pop operations, the deque auto-compacts.
3705
+ * @remarks Time O(1), Space O(1)
3706
+ * @returns Current ratio threshold. 0 means auto-compact is disabled.
3707
+ */
3708
+ get autoCompactRatio() {
3709
+ return this._autoCompactRatio;
3710
+ }
3711
+ /**
3712
+ * Set the auto-compaction ratio.
3713
+ * @remarks Time O(1), Space O(1)
3714
+ * @param value - Ratio in [0,1]. 0 disables auto-compact.
3715
+ */
3716
+ set autoCompactRatio(value) {
3717
+ this._autoCompactRatio = value;
3718
+ }
3719
+ /**
3720
+ * Counter for shift/pop operations since last compaction check.
3721
+ * Only checks ratio every `_bucketSize` operations to minimize overhead.
3722
+ */
3723
+ _compactCounter = 0;
3647
3724
  _bucketFirst = 0;
3648
3725
  /**
3649
3726
  * Get the index of the first bucket in use.
@@ -3782,6 +3859,7 @@ var Deque = class extends LinearBase {
3782
3859
  }
3783
3860
  }
3784
3861
  this._length -= 1;
3862
+ this._autoCompact();
3785
3863
  return element;
3786
3864
  }
3787
3865
  /**
@@ -3804,6 +3882,7 @@ var Deque = class extends LinearBase {
3804
3882
  }
3805
3883
  }
3806
3884
  this._length -= 1;
3885
+ this._autoCompact();
3807
3886
  return element;
3808
3887
  }
3809
3888
  /**
@@ -4136,11 +4215,40 @@ var Deque = class extends LinearBase {
4136
4215
  * @remarks Time O(N), Space O(1)
4137
4216
  * @returns void
4138
4217
  */
4218
+ /**
4219
+ * (Protected) Trigger auto-compaction if space utilization drops below threshold.
4220
+ * Only checks every `_bucketSize` operations to minimize hot-path overhead.
4221
+ * Uses element-based ratio: `elements / (bucketCount * bucketSize)`.
4222
+ */
4223
+ _autoCompact() {
4224
+ if (this._autoCompactRatio <= 0 || this._bucketCount <= 1) return;
4225
+ this._compactCounter++;
4226
+ if (this._compactCounter < this._bucketSize) return;
4227
+ this._compactCounter = 0;
4228
+ const utilization = this._length / (this._bucketCount * this._bucketSize);
4229
+ if (utilization < this._autoCompactRatio) {
4230
+ this.shrinkToFit();
4231
+ }
4232
+ }
4233
+ /**
4234
+ * Compact the deque by removing unused buckets.
4235
+ * @remarks Time O(N), Space O(1)
4236
+ * @returns True if compaction was performed (bucket count reduced).
4237
+ */
4238
+ /**
4239
+ * Compact the deque by removing unused buckets.
4240
+ * @remarks Time O(N), Space O(1)
4241
+ * @returns True if compaction was performed (bucket count reduced).
4242
+ */
4243
+ compact() {
4244
+ const before = this._bucketCount;
4245
+ this.shrinkToFit();
4246
+ return this._bucketCount < before;
4247
+ }
4139
4248
  shrinkToFit() {
4140
4249
  if (this._length === 0) return;
4141
4250
  const newBuckets = [];
4142
- if (this._bucketFirst === this._bucketLast) return;
4143
- else if (this._bucketFirst < this._bucketLast) {
4251
+ if (this._bucketFirst <= this._bucketLast) {
4144
4252
  for (let i = this._bucketFirst; i <= this._bucketLast; ++i) {
4145
4253
  newBuckets.push(this._buckets[i]);
4146
4254
  }
@@ -4155,6 +4263,8 @@ var Deque = class extends LinearBase {
4155
4263
  this._bucketFirst = 0;
4156
4264
  this._bucketLast = newBuckets.length - 1;
4157
4265
  this._buckets = newBuckets;
4266
+ this._bucketCount = newBuckets.length;
4267
+ this._compactCounter = 0;
4158
4268
  }
4159
4269
  /**
4160
4270
  * Deep clone this deque, preserving options.
@@ -4653,7 +4763,7 @@ var Heap = class _Heap extends IterableElementBase {
4653
4763
  */
4654
4764
  map(callback, options, thisArg) {
4655
4765
  const { comparator, toElementFn, ...rest } = options ?? {};
4656
- if (!comparator) throw new TypeError("Heap.map requires options.comparator for EM");
4766
+ if (!comparator) throw new TypeError(ERR.comparatorRequired("Heap.map"));
4657
4767
  const out = this._createLike([], { ...rest, comparator, toElementFn });
4658
4768
  let i = 0;
4659
4769
  for (const x of this) {
@@ -4680,18 +4790,13 @@ var Heap = class _Heap extends IterableElementBase {
4680
4790
  }
4681
4791
  _DEFAULT_COMPARATOR = /* @__PURE__ */ __name((a, b) => {
4682
4792
  if (typeof a === "object" || typeof b === "object") {
4683
- throw TypeError("When comparing object types, define a custom comparator in options.");
4793
+ throw new TypeError(ERR.comparatorRequired("Heap"));
4684
4794
  }
4685
4795
  if (a > b) return 1;
4686
4796
  if (a < b) return -1;
4687
4797
  return 0;
4688
4798
  }, "_DEFAULT_COMPARATOR");
4689
4799
  _comparator = this._DEFAULT_COMPARATOR;
4690
- /**
4691
- * Get the comparator used to order elements.
4692
- * @remarks Time O(1), Space O(1)
4693
- * @returns Comparator function.
4694
- */
4695
4800
  /**
4696
4801
  * Get the comparator used to order elements.
4697
4802
  * @remarks Time O(1), Space O(1)
@@ -4740,8 +4845,7 @@ var Heap = class _Heap extends IterableElementBase {
4740
4845
  */
4741
4846
  _createInstance(options) {
4742
4847
  const Ctor = this.constructor;
4743
- const next = new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options ?? {} });
4744
- return next;
4848
+ return new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options ?? {} });
4745
4849
  }
4746
4850
  /**
4747
4851
  * (Protected) Create a like-kind instance seeded by elements.
@@ -4798,7 +4902,7 @@ var FibonacciHeap = class {
4798
4902
  constructor(comparator) {
4799
4903
  this.clear();
4800
4904
  this._comparator = comparator || this._defaultComparator;
4801
- if (typeof this.comparator !== "function") throw new Error("FibonacciHeap: comparator must be a function.");
4905
+ if (typeof this.comparator !== "function") throw new TypeError(ERR.notAFunction("comparator", "FibonacciHeap"));
4802
4906
  }
4803
4907
  _root;
4804
4908
  /**
@@ -5014,9 +5118,7 @@ var MaxHeap = class extends Heap {
5014
5118
  super(elements, {
5015
5119
  comparator: /* @__PURE__ */ __name((a, b) => {
5016
5120
  if (typeof a === "object" || typeof b === "object") {
5017
- throw TypeError(
5018
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
5019
- );
5121
+ throw new TypeError(ERR.comparatorRequired("MaxHeap"));
5020
5122
  }
5021
5123
  if (a < b) return 1;
5022
5124
  if (a > b) return -1;
@@ -5185,7 +5287,7 @@ var AbstractGraph = class extends IterableEntryBase {
5185
5287
  const newEdge = this.createEdge(srcOrEdge, dest, weight, value);
5186
5288
  return this._addEdge(newEdge);
5187
5289
  } else {
5188
- throw new Error("dest must be a Vertex or vertex key while srcOrEdge is an Edge");
5290
+ throw new TypeError(ERR.invalidArgument("dest must be a Vertex or vertex key when srcOrEdge is an Edge.", "Graph"));
5189
5291
  }
5190
5292
  }
5191
5293
  }
@@ -5790,8 +5892,8 @@ var AbstractGraph = class extends IterableEntryBase {
5790
5892
  const Ctor = this.constructor;
5791
5893
  const instance = new Ctor();
5792
5894
  const graph = _options?.graph;
5793
- if (graph) instance._options = { ...instance._options, ...graph };
5794
- else instance._options = { ...instance._options, ...this._options };
5895
+ if (graph) instance["_options"] = { ...instance["_options"], ...graph };
5896
+ else instance["_options"] = { ...instance["_options"], ...this._options };
5795
5897
  return instance;
5796
5898
  }
5797
5899
  /**
@@ -5824,12 +5926,10 @@ var AbstractGraph = class extends IterableEntryBase {
5824
5926
  const [va, vb] = ends;
5825
5927
  const ka = va.key;
5826
5928
  const kb = vb.key;
5827
- const hasA = g.hasVertex ? g.hasVertex(ka) : false;
5828
- const hasB = g.hasVertex ? g.hasVertex(kb) : false;
5929
+ const hasA = typeof g.hasVertex === "function" ? g.hasVertex(ka) : false;
5930
+ const hasB = typeof g.hasVertex === "function" ? g.hasVertex(kb) : false;
5829
5931
  if (hasA && hasB) {
5830
- const w = e.weight;
5831
- const val = e.value;
5832
- const newEdge = g.createEdge(ka, kb, w, val);
5932
+ const newEdge = g.createEdge(ka, kb, e.weight, e.value);
5833
5933
  g._addEdge(newEdge);
5834
5934
  }
5835
5935
  }
@@ -5867,6 +5967,92 @@ var AbstractGraph = class extends IterableEntryBase {
5867
5967
  _getVertexKey(vertexOrKey) {
5868
5968
  return vertexOrKey instanceof AbstractVertex ? vertexOrKey.key : vertexOrKey;
5869
5969
  }
5970
+ /**
5971
+ * The edge connector string used in visual output.
5972
+ * Override in subclasses (e.g., '--' for undirected, '->' for directed).
5973
+ */
5974
+ get _edgeConnector() {
5975
+ return "--";
5976
+ }
5977
+ /**
5978
+ * Generate a text-based visual representation of the graph.
5979
+ *
5980
+ * **Adjacency list format:**
5981
+ * ```
5982
+ * Graph (5 vertices, 6 edges):
5983
+ * A -> B (1), C (2)
5984
+ * B -> D (3)
5985
+ * C -> (no outgoing edges)
5986
+ * D -> A (1)
5987
+ * E (isolated)
5988
+ * ```
5989
+ *
5990
+ * @param options - Optional display settings.
5991
+ * @param options.showWeight - Whether to show edge weights (default: true).
5992
+ * @returns The visual string.
5993
+ */
5994
+ toVisual(options) {
5995
+ const showWeight = options?.showWeight ?? true;
5996
+ const vertices = [...this._vertexMap.values()];
5997
+ const vertexCount = vertices.length;
5998
+ const edgeCount = this.edgeSet().length;
5999
+ const lines = [`Graph (${vertexCount} vertices, ${edgeCount} edges):`];
6000
+ for (const vertex of vertices) {
6001
+ const neighbors = this.getNeighbors(vertex);
6002
+ if (neighbors.length === 0) {
6003
+ lines.push(` ${vertex.key} (isolated)`);
6004
+ } else {
6005
+ const edgeStrs = neighbors.map((neighbor) => {
6006
+ const edge = this.getEdge(vertex, neighbor);
6007
+ if (edge && showWeight && edge.weight !== void 0 && edge.weight !== 1) {
6008
+ return `${neighbor.key} (${edge.weight})`;
6009
+ }
6010
+ return `${neighbor.key}`;
6011
+ });
6012
+ lines.push(` ${vertex.key} ${this._edgeConnector} ${edgeStrs.join(", ")}`);
6013
+ }
6014
+ }
6015
+ return lines.join("\n");
6016
+ }
6017
+ /**
6018
+ * Generate DOT language representation for Graphviz.
6019
+ *
6020
+ * @param options - Optional display settings.
6021
+ * @param options.name - Graph name (default: 'G').
6022
+ * @param options.showWeight - Whether to label edges with weight (default: true).
6023
+ * @returns DOT format string.
6024
+ */
6025
+ toDot(options) {
6026
+ const name = options?.name ?? "G";
6027
+ const showWeight = options?.showWeight ?? true;
6028
+ const isDirected = this._edgeConnector === "->";
6029
+ const graphType = isDirected ? "digraph" : "graph";
6030
+ const edgeOp = isDirected ? "->" : "--";
6031
+ const lines = [`${graphType} ${name} {`];
6032
+ for (const vertex of this._vertexMap.values()) {
6033
+ lines.push(` "${vertex.key}";`);
6034
+ }
6035
+ const visited = /* @__PURE__ */ new Set();
6036
+ for (const vertex of this._vertexMap.values()) {
6037
+ for (const neighbor of this.getNeighbors(vertex)) {
6038
+ const edgeId = isDirected ? `${vertex.key}->${neighbor.key}` : [vertex.key, neighbor.key].sort().join("--");
6039
+ if (visited.has(edgeId)) continue;
6040
+ visited.add(edgeId);
6041
+ const edge = this.getEdge(vertex, neighbor);
6042
+ const label = edge && showWeight && edge.weight !== void 0 && edge.weight !== 1 ? ` [label="${edge.weight}"]` : "";
6043
+ lines.push(` "${vertex.key}" ${edgeOp} "${neighbor.key}"${label};`);
6044
+ }
6045
+ }
6046
+ lines.push("}");
6047
+ return lines.join("\n");
6048
+ }
6049
+ /**
6050
+ * Print the graph to console.
6051
+ * @param options - Display settings passed to `toVisual`.
6052
+ */
6053
+ print(options) {
6054
+ console.log(this.toVisual(options));
6055
+ }
5870
6056
  };
5871
6057
 
5872
6058
  // src/data-structures/graph/directed-graph.ts
@@ -5902,6 +6088,9 @@ var DirectedGraph = class _DirectedGraph extends AbstractGraph {
5902
6088
  constructor(options) {
5903
6089
  super(options);
5904
6090
  }
6091
+ get _edgeConnector() {
6092
+ return "->";
6093
+ }
5905
6094
  _outEdgeMap = /* @__PURE__ */ new Map();
5906
6095
  get outEdgeMap() {
5907
6096
  return this._outEdgeMap;
@@ -6683,6 +6872,84 @@ var UndirectedGraph = class _UndirectedGraph extends AbstractGraph {
6683
6872
  cutVertices
6684
6873
  };
6685
6874
  }
6875
+ /**
6876
+ * Find biconnected components using edge-stack Tarjan variant.
6877
+ * A biconnected component is a maximal biconnected subgraph.
6878
+ * @returns Array of edge arrays, each representing a biconnected component.
6879
+ * @remarks Time O(V + E), Space O(V + E)
6880
+ */
6881
+ getBiconnectedComponents() {
6882
+ const dfn = /* @__PURE__ */ new Map();
6883
+ const low = /* @__PURE__ */ new Map();
6884
+ const edgeStack = [];
6885
+ const components = [];
6886
+ let time = 0;
6887
+ const dfs = /* @__PURE__ */ __name((vertex, parent) => {
6888
+ dfn.set(vertex, time);
6889
+ low.set(vertex, time);
6890
+ time++;
6891
+ const neighbors = this.getNeighbors(vertex);
6892
+ let childCount = 0;
6893
+ for (const neighbor of neighbors) {
6894
+ const edge = this.getEdge(vertex, neighbor);
6895
+ if (!edge) continue;
6896
+ if (!dfn.has(neighbor)) {
6897
+ childCount++;
6898
+ edgeStack.push(edge);
6899
+ dfs(neighbor, vertex);
6900
+ low.set(vertex, Math.min(low.get(vertex), low.get(neighbor)));
6901
+ if (parent === void 0 && childCount > 1 || parent !== void 0 && low.get(neighbor) >= dfn.get(vertex)) {
6902
+ const component = [];
6903
+ let e;
6904
+ do {
6905
+ e = edgeStack.pop();
6906
+ if (e) component.push(e);
6907
+ } while (e && e !== edge);
6908
+ if (component.length > 0) components.push(component);
6909
+ }
6910
+ } else if (neighbor !== parent && dfn.get(neighbor) < dfn.get(vertex)) {
6911
+ edgeStack.push(edge);
6912
+ low.set(vertex, Math.min(low.get(vertex), dfn.get(neighbor)));
6913
+ }
6914
+ }
6915
+ }, "dfs");
6916
+ for (const vertex of this.vertexMap.values()) {
6917
+ if (!dfn.has(vertex)) {
6918
+ dfs(vertex, void 0);
6919
+ if (edgeStack.length > 0) {
6920
+ components.push([...edgeStack]);
6921
+ edgeStack.length = 0;
6922
+ }
6923
+ }
6924
+ }
6925
+ return components;
6926
+ }
6927
+ /**
6928
+ * Detect whether the graph contains a cycle.
6929
+ * Uses DFS with parent tracking.
6930
+ * @returns `true` if a cycle exists, `false` otherwise.
6931
+ * @remarks Time O(V + E), Space O(V)
6932
+ */
6933
+ hasCycle() {
6934
+ const visited = /* @__PURE__ */ new Set();
6935
+ const dfs = /* @__PURE__ */ __name((vertex, parent) => {
6936
+ visited.add(vertex);
6937
+ for (const neighbor of this.getNeighbors(vertex)) {
6938
+ if (!visited.has(neighbor)) {
6939
+ if (dfs(neighbor, vertex)) return true;
6940
+ } else if (neighbor !== parent) {
6941
+ return true;
6942
+ }
6943
+ }
6944
+ return false;
6945
+ }, "dfs");
6946
+ for (const vertex of this.vertexMap.values()) {
6947
+ if (!visited.has(vertex)) {
6948
+ if (dfs(vertex, void 0)) return true;
6949
+ }
6950
+ }
6951
+ return false;
6952
+ }
6686
6953
  /**
6687
6954
  * Get bridges discovered by `tarjan()`.
6688
6955
  * @returns Array of edges that are bridges.
@@ -6836,30 +7103,6 @@ var MapGraph = class _MapGraph extends DirectedGraph {
6836
7103
  }
6837
7104
  };
6838
7105
 
6839
- // src/common/index.ts
6840
- var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
6841
- DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
6842
- DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
6843
- return DFSOperation2;
6844
- })(DFSOperation || {});
6845
- var Range = class {
6846
- constructor(low, high, includeLow = true, includeHigh = true) {
6847
- this.low = low;
6848
- this.high = high;
6849
- this.includeLow = includeLow;
6850
- this.includeHigh = includeHigh;
6851
- }
6852
- static {
6853
- __name(this, "Range");
6854
- }
6855
- // Determine whether a key is within the range
6856
- isInRange(key, comparator) {
6857
- const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
6858
- const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
6859
- return lowCheck && highCheck;
6860
- }
6861
- };
6862
-
6863
7106
  // src/data-structures/binary-tree/binary-tree.ts
6864
7107
  var BinaryTreeNode = class {
6865
7108
  static {
@@ -6998,7 +7241,7 @@ var BinaryTreeNode = class {
6998
7241
  return "MAL_NODE";
6999
7242
  }
7000
7243
  };
7001
- var BinaryTree = class extends IterableEntryBase {
7244
+ var BinaryTree = class _BinaryTree extends IterableEntryBase {
7002
7245
  static {
7003
7246
  __name(this, "BinaryTree");
7004
7247
  }
@@ -7018,7 +7261,7 @@ var BinaryTree = class extends IterableEntryBase {
7018
7261
  if (isMapMode !== void 0) this._isMapMode = isMapMode;
7019
7262
  if (isDuplicate !== void 0) this._isDuplicate = isDuplicate;
7020
7263
  if (typeof toEntryFn === "function") this._toEntryFn = toEntryFn;
7021
- else if (toEntryFn) throw TypeError("toEntryFn must be a function type");
7264
+ else if (toEntryFn) throw new TypeError(ERR.notAFunction("toEntryFn", "BinaryTree"));
7022
7265
  }
7023
7266
  if (keysNodesEntriesOrRaws) this.setMany(keysNodesEntriesOrRaws);
7024
7267
  }
@@ -7256,7 +7499,7 @@ var BinaryTree = class extends IterableEntryBase {
7256
7499
  if (!this._root) {
7257
7500
  this._setRoot(newNode);
7258
7501
  if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
7259
- this._size = 1;
7502
+ if (newNode !== null) this._size = 1;
7260
7503
  return true;
7261
7504
  }
7262
7505
  const queue = new Queue([this._root]);
@@ -7288,7 +7531,7 @@ var BinaryTree = class extends IterableEntryBase {
7288
7531
  potentialParent.right = newNode;
7289
7532
  }
7290
7533
  if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
7291
- this._size++;
7534
+ if (newNode !== null) this._size++;
7292
7535
  return true;
7293
7536
  }
7294
7537
  return false;
@@ -7856,7 +8099,7 @@ var BinaryTree = class extends IterableEntryBase {
7856
8099
  }
7857
8100
  /**
7858
8101
  * Finds all leaf nodes in the tree.
7859
- * @remarks Time O(N), visits every node. Space O(H) for recursive stack or O(N) for iterative queue.
8102
+ * @remarks Time O(N), visits every node. Space O(H) for recursive or iterative stack.
7860
8103
  *
7861
8104
  * @template C - The type of the callback function.
7862
8105
  * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each leaf node.
@@ -7879,15 +8122,15 @@ var BinaryTree = class extends IterableEntryBase {
7879
8122
  }, "dfs");
7880
8123
  dfs(startNode);
7881
8124
  } else {
7882
- const queue = new Queue([startNode]);
7883
- while (queue.length > 0) {
7884
- const cur = queue.shift();
8125
+ const stack = [startNode];
8126
+ while (stack.length > 0) {
8127
+ const cur = stack.pop();
7885
8128
  if (this.isRealNode(cur)) {
7886
8129
  if (this.isLeaf(cur)) {
7887
8130
  leaves.push(callback(cur));
7888
8131
  }
7889
- if (this.isRealNode(cur.left)) queue.push(cur.left);
7890
- if (this.isRealNode(cur.right)) queue.push(cur.right);
8132
+ if (this.isRealNode(cur.right)) stack.push(cur.right);
8133
+ if (this.isRealNode(cur.left)) stack.push(cur.left);
7891
8134
  }
7892
8135
  }
7893
8136
  }
@@ -8256,7 +8499,7 @@ var BinaryTree = class extends IterableEntryBase {
8256
8499
  * @param node - The node.
8257
8500
  * @returns The node's key or undefined.
8258
8501
  */
8259
- _DEFAULT_NODE_CALLBACK = /* @__PURE__ */ __name((node) => node ? node.key : void 0, "_DEFAULT_NODE_CALLBACK");
8502
+ _DEFAULT_NODE_CALLBACK = /* @__PURE__ */ __name((node) => node?.key, "_DEFAULT_NODE_CALLBACK");
8260
8503
  /**
8261
8504
  * (Protected) Snapshots the current tree's configuration options.
8262
8505
  * @remarks Time O(1)
@@ -8349,42 +8592,98 @@ var BinaryTree = class extends IterableEntryBase {
8349
8592
  _displayAux(node, options) {
8350
8593
  const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8351
8594
  const emptyDisplayLayout = [["\u2500"], 1, 0, 0];
8352
- if (node === null && !isShowNull) {
8353
- return emptyDisplayLayout;
8354
- } else if (node === void 0 && !isShowUndefined) {
8355
- return emptyDisplayLayout;
8356
- } else if (this.isNIL(node) && !isShowRedBlackNIL) {
8357
- return emptyDisplayLayout;
8358
- } else if (node !== null && node !== void 0) {
8359
- const key = node.key, line = this.isNIL(node) ? "S" : String(key), width = line.length;
8360
- return _buildNodeDisplay(
8361
- line,
8362
- width,
8363
- this._displayAux(node.left, options),
8364
- this._displayAux(node.right, options)
8365
- );
8366
- } else {
8367
- const line = node === void 0 ? "U" : "N", width = line.length;
8368
- return _buildNodeDisplay(line, width, [[""], 1, 0, 0], [[""], 1, 0, 0]);
8369
- }
8370
- function _buildNodeDisplay(line, width, left, right) {
8371
- const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
8372
- const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
8373
- const firstLine = " ".repeat(Math.max(0, leftMiddle + 1)) + "_".repeat(Math.max(0, leftWidth - leftMiddle - 1)) + line + "_".repeat(Math.max(0, rightMiddle)) + " ".repeat(Math.max(0, rightWidth - rightMiddle));
8374
- const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
8375
- const mergedLines = [firstLine, secondLine];
8376
- for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
8377
- const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
8378
- const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
8379
- mergedLines.push(leftLine + " ".repeat(width) + rightLine);
8595
+ const newFrame = /* @__PURE__ */ __name((n) => ({
8596
+ node: n,
8597
+ stage: 0,
8598
+ leftLayout: emptyDisplayLayout,
8599
+ rightLayout: emptyDisplayLayout
8600
+ }), "newFrame");
8601
+ const stack = [newFrame(node)];
8602
+ let result = emptyDisplayLayout;
8603
+ const setChildResult = /* @__PURE__ */ __name((layout) => {
8604
+ if (stack.length === 0) {
8605
+ result = layout;
8606
+ return;
8380
8607
  }
8381
- return [
8382
- mergedLines,
8383
- leftWidth + width + rightWidth,
8384
- Math.max(leftHeight, rightHeight) + 2,
8385
- leftWidth + Math.floor(width / 2)
8386
- ];
8608
+ const parent = stack[stack.length - 1];
8609
+ if (parent.stage === 1) parent.leftLayout = layout;
8610
+ else parent.rightLayout = layout;
8611
+ }, "setChildResult");
8612
+ while (stack.length > 0) {
8613
+ const frame = stack[stack.length - 1];
8614
+ const cur = frame.node;
8615
+ if (frame.stage === 0) {
8616
+ if (this._isDisplayLeaf(cur, options)) {
8617
+ stack.pop();
8618
+ const layout = this._resolveDisplayLeaf(cur, options, emptyDisplayLayout);
8619
+ setChildResult(layout);
8620
+ continue;
8621
+ }
8622
+ frame.stage = 1;
8623
+ stack.push(newFrame(cur.left));
8624
+ } else if (frame.stage === 1) {
8625
+ frame.stage = 2;
8626
+ stack.push(newFrame(cur.right));
8627
+ } else {
8628
+ stack.pop();
8629
+ const line = this.isNIL(cur) ? "S" : String(cur.key);
8630
+ const layout = _BinaryTree._buildNodeDisplay(line, line.length, frame.leftLayout, frame.rightLayout);
8631
+ setChildResult(layout);
8632
+ }
8633
+ }
8634
+ return result;
8635
+ }
8636
+ static _buildNodeDisplay(line, width, left, right) {
8637
+ const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
8638
+ const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
8639
+ const firstLine = " ".repeat(Math.max(0, leftMiddle + 1)) + "_".repeat(Math.max(0, leftWidth - leftMiddle - 1)) + line + "_".repeat(Math.max(0, rightMiddle)) + " ".repeat(Math.max(0, rightWidth - rightMiddle));
8640
+ const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
8641
+ const mergedLines = [firstLine, secondLine];
8642
+ for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
8643
+ const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
8644
+ const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
8645
+ mergedLines.push(leftLine + " ".repeat(width) + rightLine);
8646
+ }
8647
+ return [
8648
+ mergedLines,
8649
+ leftWidth + width + rightWidth,
8650
+ Math.max(leftHeight, rightHeight) + 2,
8651
+ leftWidth + Math.floor(width / 2)
8652
+ ];
8653
+ }
8654
+ /**
8655
+ * Check if a node is a display leaf (empty, null, undefined, NIL, or real leaf).
8656
+ */
8657
+ _isDisplayLeaf(node, options) {
8658
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8659
+ if (node === null && !isShowNull) return true;
8660
+ if (node === void 0 && !isShowUndefined) return true;
8661
+ if (this.isNIL(node) && !isShowRedBlackNIL) return true;
8662
+ if (node === null || node === void 0) return true;
8663
+ const hasDisplayableLeft = this._hasDisplayableChild(node.left, options);
8664
+ const hasDisplayableRight = this._hasDisplayableChild(node.right, options);
8665
+ return !hasDisplayableLeft && !hasDisplayableRight;
8666
+ }
8667
+ _hasDisplayableChild(child, options) {
8668
+ if (child === null) return !!options.isShowNull;
8669
+ if (child === void 0) return !!options.isShowUndefined;
8670
+ if (this.isNIL(child)) return !!options.isShowRedBlackNIL;
8671
+ return true;
8672
+ }
8673
+ /**
8674
+ * Resolve a display leaf node to its layout.
8675
+ */
8676
+ _resolveDisplayLeaf(node, options, emptyDisplayLayout) {
8677
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8678
+ if (node === null && !isShowNull) return emptyDisplayLayout;
8679
+ if (node === void 0 && !isShowUndefined) return emptyDisplayLayout;
8680
+ if (this.isNIL(node) && !isShowRedBlackNIL) return emptyDisplayLayout;
8681
+ if (node !== null && node !== void 0) {
8682
+ const line2 = this.isNIL(node) ? "S" : String(node.key);
8683
+ return _BinaryTree._buildNodeDisplay(line2, line2.length, emptyDisplayLayout, emptyDisplayLayout);
8387
8684
  }
8685
+ const line = node === void 0 ? "U" : "N";
8686
+ return _BinaryTree._buildNodeDisplay(line, line.length, [[""], 1, 0, 0], [[""], 1, 0, 0]);
8388
8687
  }
8389
8688
  /**
8390
8689
  * (Protected) Swaps the key/value properties of two nodes.
@@ -9390,9 +9689,15 @@ var BST = class extends BinaryTree {
9390
9689
  if (a < b) return -1;
9391
9690
  return 0;
9392
9691
  }
9692
+ if (a instanceof Date && b instanceof Date) {
9693
+ const ta = a.getTime();
9694
+ const tb = b.getTime();
9695
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("BST"));
9696
+ return ta > tb ? 1 : ta < tb ? -1 : 0;
9697
+ }
9393
9698
  if (typeof a === "object" || typeof b === "object") {
9394
- throw TypeError(
9395
- `When comparing object type keys, a custom comparator must be provided in the constructor's options!`
9699
+ throw new TypeError(
9700
+ ERR.comparatorRequired("BST")
9396
9701
  );
9397
9702
  }
9398
9703
  return 0;
@@ -9904,7 +10209,7 @@ var BinaryIndexedTree = class {
9904
10209
  */
9905
10210
  read(count) {
9906
10211
  if (!Number.isInteger(count)) {
9907
- throw new Error("Invalid count");
10212
+ throw new Error(ERR.invalidArgument("count must be an integer", "BinaryIndexedTree"));
9908
10213
  }
9909
10214
  return this._read(Math.max(Math.min(count, this.max), 0));
9910
10215
  }
@@ -9916,7 +10221,7 @@ var BinaryIndexedTree = class {
9916
10221
  */
9917
10222
  lowerBound(sum) {
9918
10223
  if (this.negativeCount > 0) {
9919
- throw new Error("Sequence is not non-descending");
10224
+ throw new Error(ERR.invalidOperation("Sequence is not non-descending.", "BinaryIndexedTree"));
9920
10225
  }
9921
10226
  return this._binarySearch(sum, (x, y) => x < y);
9922
10227
  }
@@ -9929,7 +10234,7 @@ var BinaryIndexedTree = class {
9929
10234
  */
9930
10235
  upperBound(sum) {
9931
10236
  if (this.negativeCount > 0) {
9932
- throw new Error("Must not be descending");
10237
+ throw new Error(ERR.invalidOperation("Sequence must not be descending.", "BinaryIndexedTree"));
9933
10238
  }
9934
10239
  return this._binarySearch(sum, (x, y) => x <= y);
9935
10240
  }
@@ -9980,10 +10285,10 @@ var BinaryIndexedTree = class {
9980
10285
  */
9981
10286
  _checkIndex(index) {
9982
10287
  if (!Number.isInteger(index)) {
9983
- throw new Error("Invalid index: Index must be an integer.");
10288
+ throw new TypeError(ERR.invalidIndex("BinaryIndexedTree"));
9984
10289
  }
9985
10290
  if (index < 0 || index >= this.max) {
9986
- throw new Error("Index out of range: Index must be within the range [0, this.max).");
10291
+ throw new RangeError(ERR.indexOutOfRange(index, 0, this.max - 1, "BinaryIndexedTree"));
9987
10292
  }
9988
10293
  }
9989
10294
  /**
@@ -11575,6 +11880,24 @@ var RedBlackTree = class extends BST {
11575
11880
  * @param [thisArg] - See parameter type for details.
11576
11881
  * @returns A new RedBlackTree with mapped entries.
11577
11882
  */
11883
+ /**
11884
+ * Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
11885
+ * sorted bulk insert, which naturally produces a balanced RBT.
11886
+ * @remarks Time O(N), Space O(N)
11887
+ */
11888
+ perfectlyBalance(iterationType) {
11889
+ const entries = [];
11890
+ for (const [key, value] of this) entries.push([key, value]);
11891
+ if (entries.length <= 1) return true;
11892
+ this.clear();
11893
+ this.setMany(
11894
+ entries.map(([k]) => k),
11895
+ entries.map(([, v]) => v),
11896
+ true
11897
+ // isBalanceAdd
11898
+ );
11899
+ return true;
11900
+ }
11578
11901
  map(callback, options, thisArg) {
11579
11902
  const out = this._createLike([], options);
11580
11903
  let index = 0;
@@ -11896,7 +12219,7 @@ var TreeSet = class _TreeSet {
11896
12219
  static createDefaultComparator() {
11897
12220
  return (a, b) => {
11898
12221
  if (typeof a === "number" && typeof b === "number") {
11899
- if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError("TreeSet: NaN is not a valid key");
12222
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN("TreeSet"));
11900
12223
  const aa = Object.is(a, -0) ? 0 : a;
11901
12224
  const bb = Object.is(b, -0) ? 0 : b;
11902
12225
  return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -11907,10 +12230,10 @@ var TreeSet = class _TreeSet {
11907
12230
  if (a instanceof Date && b instanceof Date) {
11908
12231
  const ta = a.getTime();
11909
12232
  const tb = b.getTime();
11910
- if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError("TreeSet: invalid Date key");
12233
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("TreeSet"));
11911
12234
  return ta > tb ? 1 : ta < tb ? -1 : 0;
11912
12235
  }
11913
- throw new TypeError("TreeSet: comparator is required for non-number/non-string/non-Date keys");
12236
+ throw new TypeError(ERR.comparatorRequired("TreeSet"));
11914
12237
  };
11915
12238
  }
11916
12239
  /**
@@ -11928,15 +12251,15 @@ var TreeSet = class _TreeSet {
11928
12251
  _validateKey(key) {
11929
12252
  if (!this.#isDefaultComparator) return;
11930
12253
  if (typeof key === "number") {
11931
- if (Number.isNaN(key)) throw new TypeError("TreeSet: NaN is not a valid key");
12254
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeSet"));
11932
12255
  return;
11933
12256
  }
11934
12257
  if (typeof key === "string") return;
11935
12258
  if (key instanceof Date) {
11936
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeSet: invalid Date key");
12259
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeSet"));
11937
12260
  return;
11938
12261
  }
11939
- throw new TypeError("TreeSet: comparator is required for non-number/non-string/non-Date keys");
12262
+ throw new TypeError(ERR.comparatorRequired("TreeSet"));
11940
12263
  }
11941
12264
  /**
11942
12265
  * Add a key to the set (no-op if already present).
@@ -12255,15 +12578,15 @@ var TreeMultiMap = class _TreeMultiMap {
12255
12578
  _validateKey(key) {
12256
12579
  if (!this.#isDefaultComparator) return;
12257
12580
  if (typeof key === "number") {
12258
- if (Number.isNaN(key)) throw new TypeError("TreeMultiMap: NaN is not a valid key");
12581
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMultiMap"));
12259
12582
  return;
12260
12583
  }
12261
12584
  if (typeof key === "string") return;
12262
12585
  if (key instanceof Date) {
12263
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMultiMap: invalid Date key");
12586
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMultiMap"));
12264
12587
  return;
12265
12588
  }
12266
- throw new TypeError("TreeMultiMap: comparator is required for non-number/non-string/non-Date keys");
12589
+ throw new TypeError(ERR.comparatorRequired("TreeMultiMap"));
12267
12590
  }
12268
12591
  /**
12269
12592
  * Number of distinct keys.
@@ -12685,7 +13008,7 @@ var TreeMap = class _TreeMap {
12685
13008
  [k, v] = toEntryFn(item);
12686
13009
  } else {
12687
13010
  if (!Array.isArray(item) || item.length < 2) {
12688
- throw new TypeError("TreeMap: each entry must be a [key, value] tuple");
13011
+ throw new TypeError(ERR.invalidEntry("TreeMap"));
12689
13012
  }
12690
13013
  k = item[0];
12691
13014
  v = item[1];
@@ -12706,7 +13029,7 @@ var TreeMap = class _TreeMap {
12706
13029
  static createDefaultComparator() {
12707
13030
  return (a, b) => {
12708
13031
  if (typeof a === "number" && typeof b === "number") {
12709
- if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError("TreeMap: NaN is not a valid key");
13032
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN("TreeMap"));
12710
13033
  const aa = Object.is(a, -0) ? 0 : a;
12711
13034
  const bb = Object.is(b, -0) ? 0 : b;
12712
13035
  return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -12717,24 +13040,24 @@ var TreeMap = class _TreeMap {
12717
13040
  if (a instanceof Date && b instanceof Date) {
12718
13041
  const ta = a.getTime();
12719
13042
  const tb = b.getTime();
12720
- if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError("TreeMap: invalid Date key");
13043
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("TreeMap"));
12721
13044
  return ta > tb ? 1 : ta < tb ? -1 : 0;
12722
13045
  }
12723
- throw new TypeError("TreeMap: comparator is required for non-number/non-string/non-Date keys");
13046
+ throw new TypeError(ERR.comparatorRequired("TreeMap"));
12724
13047
  };
12725
13048
  }
12726
13049
  _validateKey(key) {
12727
13050
  if (!this.#isDefaultComparator) return;
12728
13051
  if (typeof key === "number") {
12729
- if (Number.isNaN(key)) throw new TypeError("TreeMap: NaN is not a valid key");
13052
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMap"));
12730
13053
  return;
12731
13054
  }
12732
13055
  if (typeof key === "string") return;
12733
13056
  if (key instanceof Date) {
12734
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMap: invalid Date key");
13057
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMap"));
12735
13058
  return;
12736
13059
  }
12737
- throw new TypeError("TreeMap: comparator is required for non-number/non-string/non-Date keys");
13060
+ throw new TypeError(ERR.comparatorRequired("TreeMap"));
12738
13061
  }
12739
13062
  /**
12740
13063
  * Number of entries in the map.
@@ -13058,22 +13381,22 @@ var TreeMultiSet = class _TreeMultiSet {
13058
13381
  _validateKey(key) {
13059
13382
  if (!this.#isDefaultComparator) return;
13060
13383
  if (typeof key === "number") {
13061
- if (Number.isNaN(key)) throw new TypeError("TreeMultiSet: NaN is not a valid key");
13384
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMultiSet"));
13062
13385
  return;
13063
13386
  }
13064
13387
  if (typeof key === "string") return;
13065
13388
  if (key instanceof Date) {
13066
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMultiSet: invalid Date key");
13389
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMultiSet"));
13067
13390
  return;
13068
13391
  }
13069
- throw new TypeError("TreeMultiSet: comparator is required for non-number/non-string/non-Date keys");
13392
+ throw new TypeError(ERR.comparatorRequired("TreeMultiSet"));
13070
13393
  }
13071
13394
  /**
13072
13395
  * Validates that count is a non-negative safe integer.
13073
13396
  * @remarks Time O(1), Space O(1)
13074
13397
  */
13075
13398
  _validateCount(n) {
13076
- if (!Number.isSafeInteger(n) || n < 0) throw new RangeError("TreeMultiSet: count must be a safe integer >= 0");
13399
+ if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(ERR.invalidArgument("count must be a safe integer >= 0.", "TreeMultiSet"));
13077
13400
  }
13078
13401
  /**
13079
13402
  * Total occurrences (sumCounts).
@@ -13227,7 +13550,7 @@ var TreeMultiSet = class _TreeMultiSet {
13227
13550
  * @remarks Time O(1), Space O(1)
13228
13551
  */
13229
13552
  get comparator() {
13230
- return this.#core._comparator;
13553
+ return this.#core.comparator;
13231
13554
  }
13232
13555
  // ━━━ clear ━━━
13233
13556
  /**
@@ -13364,7 +13687,7 @@ var TreeMultiSet = class _TreeMultiSet {
13364
13687
  filter(predicate) {
13365
13688
  const result = new _TreeMultiSet([], {
13366
13689
  comparator: this.#isDefaultComparator ? void 0 : this.comparator,
13367
- isMapMode: this.#core._isMapMode
13690
+ isMapMode: this.#core.isMapMode
13368
13691
  });
13369
13692
  for (const [k, c] of this.entries()) {
13370
13693
  if (predicate(k, c)) {
@@ -13404,7 +13727,7 @@ var TreeMultiSet = class _TreeMultiSet {
13404
13727
  map(mapper, options) {
13405
13728
  const result = new _TreeMultiSet([], {
13406
13729
  comparator: options?.comparator,
13407
- isMapMode: this.#core._isMapMode
13730
+ isMapMode: this.#core.isMapMode
13408
13731
  });
13409
13732
  for (const [k, c] of this.entries()) {
13410
13733
  const [newKey, newCount] = mapper(k, c);
@@ -13424,7 +13747,7 @@ var TreeMultiSet = class _TreeMultiSet {
13424
13747
  clone() {
13425
13748
  const result = new _TreeMultiSet([], {
13426
13749
  comparator: this.#isDefaultComparator ? void 0 : this.comparator,
13427
- isMapMode: this.#core._isMapMode
13750
+ isMapMode: this.#core.isMapMode
13428
13751
  });
13429
13752
  for (const [k, c] of this.entries()) {
13430
13753
  result.add(k, c);
@@ -13497,9 +13820,7 @@ var MaxPriorityQueue = class extends PriorityQueue {
13497
13820
  super(elements, {
13498
13821
  comparator: /* @__PURE__ */ __name((a, b) => {
13499
13822
  if (typeof a === "object" || typeof b === "object") {
13500
- throw TypeError(
13501
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
13502
- );
13823
+ throw new TypeError(ERR.comparatorRequired("MaxPriorityQueue"));
13503
13824
  }
13504
13825
  if (a < b) return 1;
13505
13826
  if (a > b) return -1;
@@ -13640,7 +13961,7 @@ var Matrix = class _Matrix {
13640
13961
  */
13641
13962
  add(matrix) {
13642
13963
  if (!this.isMatchForCalculate(matrix)) {
13643
- throw new Error("Matrix dimensions must match for addition.");
13964
+ throw new Error(ERR.matrixDimensionMismatch("addition"));
13644
13965
  }
13645
13966
  const resultData = [];
13646
13967
  for (let i = 0; i < this.rows; i++) {
@@ -13672,7 +13993,7 @@ var Matrix = class _Matrix {
13672
13993
  */
13673
13994
  subtract(matrix) {
13674
13995
  if (!this.isMatchForCalculate(matrix)) {
13675
- throw new Error("Matrix dimensions must match for subtraction.");
13996
+ throw new Error(ERR.matrixDimensionMismatch("subtraction"));
13676
13997
  }
13677
13998
  const resultData = [];
13678
13999
  for (let i = 0; i < this.rows; i++) {
@@ -13703,7 +14024,7 @@ var Matrix = class _Matrix {
13703
14024
  */
13704
14025
  multiply(matrix) {
13705
14026
  if (this.cols !== matrix.rows) {
13706
- throw new Error("Matrix dimensions must be compatible for multiplication (A.cols = B.rows).");
14027
+ throw new Error(ERR.matrixDimensionMismatch("multiplication (A.cols must equal B.rows)"));
13707
14028
  }
13708
14029
  const resultData = [];
13709
14030
  for (let i = 0; i < this.rows; i++) {
@@ -13737,7 +14058,7 @@ var Matrix = class _Matrix {
13737
14058
  */
13738
14059
  transpose() {
13739
14060
  if (this.data.some((row) => row.length !== this.rows)) {
13740
- throw new Error("Matrix must be rectangular for transposition.");
14061
+ throw new Error(ERR.matrixNotRectangular());
13741
14062
  }
13742
14063
  const resultData = [];
13743
14064
  for (let j = 0; j < this.cols; j++) {
@@ -13761,7 +14082,7 @@ var Matrix = class _Matrix {
13761
14082
  */
13762
14083
  inverse() {
13763
14084
  if (this.rows !== this.cols) {
13764
- throw new Error("Matrix must be square for inversion.");
14085
+ throw new Error(ERR.matrixNotSquare());
13765
14086
  }
13766
14087
  const augmentedMatrixData = [];
13767
14088
  for (let i = 0; i < this.rows; i++) {
@@ -13783,12 +14104,12 @@ var Matrix = class _Matrix {
13783
14104
  pivotRow++;
13784
14105
  }
13785
14106
  if (pivotRow === this.rows) {
13786
- throw new Error("Matrix is singular, and its inverse does not exist.");
14107
+ throw new Error(ERR.matrixSingular());
13787
14108
  }
13788
14109
  augmentedMatrix._swapRows(i, pivotRow);
13789
14110
  const pivotElement = augmentedMatrix.get(i, i) ?? 1;
13790
14111
  if (pivotElement === 0) {
13791
- throw new Error("Matrix is singular, and its inverse does not exist (division by zero).");
14112
+ throw new Error(ERR.matrixSingular());
13792
14113
  }
13793
14114
  augmentedMatrix._scaleRow(i, 1 / pivotElement);
13794
14115
  for (let j = 0; j < this.rows; j++) {
@@ -13818,9 +14139,7 @@ var Matrix = class _Matrix {
13818
14139
  */
13819
14140
  dot(matrix) {
13820
14141
  if (this.cols !== matrix.rows) {
13821
- throw new Error(
13822
- "Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product."
13823
- );
14142
+ throw new Error(ERR.matrixDimensionMismatch("dot product (A.cols must equal B.rows)"));
13824
14143
  }
13825
14144
  const resultData = [];
13826
14145
  for (let i = 0; i < this.rows; i++) {
@@ -14446,7 +14765,7 @@ var Trie = class extends IterableElementBase {
14446
14765
  for (const x of this) {
14447
14766
  const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
14448
14767
  if (typeof v !== "string") {
14449
- throw new TypeError(`Trie.map callback must return string; got ${typeof v}`);
14768
+ throw new TypeError(ERR.callbackReturnType("string", typeof v, "Trie.map"));
14450
14769
  }
14451
14770
  newTrie.add(v);
14452
14771
  }
@@ -14476,12 +14795,11 @@ var Trie = class extends IterableElementBase {
14476
14795
  */
14477
14796
  _createInstance(options) {
14478
14797
  const Ctor = this.constructor;
14479
- const next = new Ctor([], {
14798
+ return new Ctor([], {
14480
14799
  toElementFn: this.toElementFn,
14481
14800
  caseSensitive: this.caseSensitive,
14482
14801
  ...options ?? {}
14483
14802
  });
14484
- return next;
14485
14803
  }
14486
14804
  /**
14487
14805
  * (Protected) Create a like-kind trie and seed it from an iterable.
@@ -14679,6 +14997,6 @@ var TreeNode = class _TreeNode {
14679
14997
  * @license MIT License
14680
14998
  */
14681
14999
 
14682
- export { AVLTree, AVLTreeNode, AbstractEdge, AbstractGraph, AbstractVertex, BST, BSTNode, BinaryIndexedTree, BinaryTree, BinaryTreeNode, Character, DFSOperation, Deque, DirectedEdge, DirectedGraph, DirectedVertex, DoublyLinkedList, DoublyLinkedListNode, FibonacciHeap, FibonacciHeapNode, HashMap, Heap, IterableElementBase, IterableEntryBase, LinkedHashMap, LinkedListQueue, MapEdge, MapGraph, MapVertex, Matrix, MaxHeap, MaxPriorityQueue, MinHeap, MinPriorityQueue, Navigator, PriorityQueue, Queue, Range, RedBlackTree, RedBlackTreeNode, SegmentTree, SegmentTreeNode, SinglyLinkedList, SinglyLinkedListNode, SkipList, SkipListNode, Stack, TreeMap, TreeMultiMap, TreeMultiMapNode, TreeMultiSet, TreeNode, TreeSet, Trie, TrieNode, UndirectedEdge, UndirectedGraph, UndirectedVertex, arrayRemove, asyncTrampoline, calcMinUnitsRequired, getMSB, isComparable, isTrampolineThunk, isWeakKey, makeAsyncTrampoline, makeTrampoline, makeTrampolineThunk, rangeCheck, roundFixed, throwRangeError, toBinaryString, trampoline, uuidV4 };
15000
+ export { AVLTree, AVLTreeNode, AbstractEdge, AbstractGraph, AbstractVertex, BST, BSTNode, BinaryIndexedTree, BinaryTree, BinaryTreeNode, Character, DFSOperation, Deque, DirectedEdge, DirectedGraph, DirectedVertex, DoublyLinkedList, DoublyLinkedListNode, ERR, FibonacciHeap, FibonacciHeapNode, HashMap, Heap, IterableElementBase, IterableEntryBase, LinkedHashMap, LinkedListQueue, MapEdge, MapGraph, MapVertex, Matrix, MaxHeap, MaxPriorityQueue, MinHeap, MinPriorityQueue, Navigator, PriorityQueue, Queue, Range, RedBlackTree, RedBlackTreeNode, SegmentTree, SegmentTreeNode, SinglyLinkedList, SinglyLinkedListNode, SkipList, SkipListNode, Stack, TreeMap, TreeMultiMap, TreeMultiMapNode, TreeMultiSet, TreeNode, TreeSet, Trie, TrieNode, UndirectedEdge, UndirectedGraph, UndirectedVertex, arrayRemove, asyncTrampoline, calcMinUnitsRequired, getMSB, isComparable, isTrampolineThunk, isWeakKey, makeAsyncTrampoline, makeTrampoline, makeTrampolineThunk, rangeCheck, roundFixed, throwRangeError, toBinaryString, trampoline, uuidV4 };
14683
15001
  //# sourceMappingURL=index.mjs.map
14684
15002
  //# sourceMappingURL=index.mjs.map