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
@@ -185,6 +185,55 @@ var IterableEntryBase = class {
185
185
  }
186
186
  };
187
187
 
188
+ // src/common/error.ts
189
+ var ERR = {
190
+ // Range / index
191
+ indexOutOfRange: /* @__PURE__ */ __name((index, min, max, ctx) => `${ctx ? ctx + ": " : ""}Index ${index} is out of range [${min}, ${max}].`, "indexOutOfRange"),
192
+ invalidIndex: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Index must be an integer.`, "invalidIndex"),
193
+ // Type / argument
194
+ invalidArgument: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidArgument"),
195
+ comparatorRequired: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Comparator is required for non-number/non-string/non-Date keys.`, "comparatorRequired"),
196
+ invalidKey: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidKey"),
197
+ notAFunction: /* @__PURE__ */ __name((name, ctx) => `${ctx ? ctx + ": " : ""}${name} must be a function.`, "notAFunction"),
198
+ invalidEntry: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Each entry must be a [key, value] tuple.`, "invalidEntry"),
199
+ invalidNaN: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}NaN is not a valid key.`, "invalidNaN"),
200
+ invalidDate: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Invalid Date key.`, "invalidDate"),
201
+ reduceEmpty: /* @__PURE__ */ __name((ctx) => `${ctx ? ctx + ": " : ""}Reduce of empty structure with no initial value.`, "reduceEmpty"),
202
+ callbackReturnType: /* @__PURE__ */ __name((expected, got, ctx) => `${ctx ? ctx + ": " : ""}Callback must return ${expected}; got ${got}.`, "callbackReturnType"),
203
+ // State / operation
204
+ invalidOperation: /* @__PURE__ */ __name((reason, ctx) => `${ctx ? ctx + ": " : ""}${reason}`, "invalidOperation"),
205
+ // Matrix
206
+ matrixDimensionMismatch: /* @__PURE__ */ __name((op) => `Matrix: Dimensions must be compatible for ${op}.`, "matrixDimensionMismatch"),
207
+ matrixSingular: /* @__PURE__ */ __name(() => "Matrix: Singular matrix, inverse does not exist.", "matrixSingular"),
208
+ matrixNotSquare: /* @__PURE__ */ __name(() => "Matrix: Must be square for inversion.", "matrixNotSquare"),
209
+ matrixNotRectangular: /* @__PURE__ */ __name(() => "Matrix: Must be rectangular for transposition.", "matrixNotRectangular"),
210
+ matrixRowMismatch: /* @__PURE__ */ __name((expected, got) => `Matrix: Expected row length ${expected}, but got ${got}.`, "matrixRowMismatch")
211
+ };
212
+
213
+ // src/common/index.ts
214
+ var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
215
+ DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
216
+ DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
217
+ return DFSOperation2;
218
+ })(DFSOperation || {});
219
+ var Range = class {
220
+ constructor(low, high, includeLow = true, includeHigh = true) {
221
+ this.low = low;
222
+ this.high = high;
223
+ this.includeLow = includeLow;
224
+ this.includeHigh = includeHigh;
225
+ }
226
+ static {
227
+ __name(this, "Range");
228
+ }
229
+ // Determine whether a key is within the range
230
+ isInRange(key, comparator) {
231
+ const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
232
+ const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
233
+ return lowCheck && highCheck;
234
+ }
235
+ };
236
+
188
237
  // src/data-structures/base/iterable-element-base.ts
189
238
  var IterableElementBase = class {
190
239
  static {
@@ -203,7 +252,7 @@ var IterableElementBase = class {
203
252
  if (options) {
204
253
  const { toElementFn } = options;
205
254
  if (typeof toElementFn === "function") this._toElementFn = toElementFn;
206
- else if (toElementFn) throw new TypeError("toElementFn must be a function type");
255
+ else if (toElementFn) throw new TypeError(ERR.notAFunction("toElementFn"));
207
256
  }
208
257
  }
209
258
  /**
@@ -366,7 +415,7 @@ var IterableElementBase = class {
366
415
  acc = initialValue;
367
416
  } else {
368
417
  const first = iter.next();
369
- if (first.done) throw new TypeError("Reduce of empty structure with no initial value");
418
+ if (first.done) throw new TypeError(ERR.reduceEmpty());
370
419
  acc = first.value;
371
420
  index = 1;
372
421
  }
@@ -434,8 +483,10 @@ var getMSB = /* @__PURE__ */ __name((value) => {
434
483
  }
435
484
  return 1 << 31 - Math.clz32(value);
436
485
  }, "getMSB");
437
- var rangeCheck = /* @__PURE__ */ __name((index, min, max, message = "Index out of bounds.") => {
438
- if (index < min || index > max) throw new RangeError(message);
486
+ var rangeCheck = /* @__PURE__ */ __name((index, min, max, message) => {
487
+ if (index < min || index > max) {
488
+ throw new RangeError(message ?? `Index ${index} is out of range [${min}, ${max}].`);
489
+ }
439
490
  }, "rangeCheck");
440
491
  var throwRangeError = /* @__PURE__ */ __name((message = "The value is off-limits.") => {
441
492
  throw new RangeError(message);
@@ -858,8 +909,8 @@ var LinkedHashMap = class extends IterableEntryBase {
858
909
  if (this.isEntry(rawElement)) {
859
910
  return rawElement;
860
911
  }
861
- throw new Error(
862
- "If `entryOrRawElements` does not adhere to [key,value], provide `options.toEntryFn` to transform raw records."
912
+ throw new TypeError(
913
+ ERR.invalidArgument("If elements do not adhere to [key, value], provide options.toEntryFn to transform raw records.", "HashMap")
863
914
  );
864
915
  }, "_toEntryFn");
865
916
  get toEntryFn() {
@@ -1021,8 +1072,9 @@ var LinkedHashMap = class extends IterableEntryBase {
1021
1072
  const cur = node;
1022
1073
  node = node.next;
1023
1074
  if (predicate(cur.key, cur.value, i++, this)) {
1024
- if (isWeakKey(cur.key)) {
1025
- this._objMap.delete(cur.key);
1075
+ const keyToCheck = cur.key;
1076
+ if (isWeakKey(keyToCheck)) {
1077
+ this._objMap.delete(keyToCheck);
1026
1078
  } else {
1027
1079
  const hash = this._hashFn(cur.key);
1028
1080
  delete this._noObjMap[hash];
@@ -1095,6 +1147,13 @@ var LinkedHashMap = class extends IterableEntryBase {
1095
1147
  }
1096
1148
  }
1097
1149
  _deleteNode(node) {
1150
+ const key = node.key;
1151
+ if (isWeakKey(key)) {
1152
+ this._objMap.delete(key);
1153
+ } else {
1154
+ const hash = this._hashFn(key);
1155
+ delete this._noObjMap[hash];
1156
+ }
1098
1157
  const { prev, next } = node;
1099
1158
  prev.next = next;
1100
1159
  next.prev = prev;
@@ -1536,7 +1595,7 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1536
1595
  static {
1537
1596
  __name(this, "SinglyLinkedList");
1538
1597
  }
1539
- _equals = Object.is;
1598
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
1540
1599
  /**
1541
1600
  * Create a SinglyLinkedList and optionally bulk-insert elements.
1542
1601
  * @remarks Time O(N), Space O(N)
@@ -1640,8 +1699,8 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1640
1699
  return value2;
1641
1700
  }
1642
1701
  let current = this.head;
1643
- while (current.next !== this.tail) current = current.next;
1644
- const value = this.tail.value;
1702
+ while (current.next && current.next !== this.tail) current = current.next;
1703
+ const value = this.tail?.value;
1645
1704
  current.next = void 0;
1646
1705
  this._tail = current;
1647
1706
  this._length--;
@@ -1729,8 +1788,8 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1729
1788
  at(index) {
1730
1789
  if (index < 0 || index >= this._length) return void 0;
1731
1790
  let current = this.head;
1732
- for (let i = 0; i < index; i++) current = current.next;
1733
- return current.value;
1791
+ for (let i = 0; i < index && current; i++) current = current.next;
1792
+ return current?.value;
1734
1793
  }
1735
1794
  /**
1736
1795
  * Type guard: check whether the input is a SinglyLinkedListNode.
@@ -1750,7 +1809,7 @@ var SinglyLinkedList = class extends LinearLinkedBase {
1750
1809
  getNodeAt(index) {
1751
1810
  if (index < 0 || index >= this._length) return void 0;
1752
1811
  let current = this.head;
1753
- for (let i = 0; i < index; i++) current = current.next;
1812
+ for (let i = 0; i < index && current; i++) current = current.next;
1754
1813
  return current;
1755
1814
  }
1756
1815
  /**
@@ -2259,7 +2318,7 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2259
2318
  static {
2260
2319
  __name(this, "DoublyLinkedList");
2261
2320
  }
2262
- _equals = Object.is;
2321
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
2263
2322
  /**
2264
2323
  * Create a DoublyLinkedList and optionally bulk-insert elements.
2265
2324
  * @remarks Time O(N), Space O(N)
@@ -2454,8 +2513,8 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2454
2513
  at(index) {
2455
2514
  if (index < 0 || index >= this._length) return void 0;
2456
2515
  let current = this.head;
2457
- for (let i = 0; i < index; i++) current = current.next;
2458
- return current.value;
2516
+ for (let i = 0; i < index && current; i++) current = current.next;
2517
+ return current?.value;
2459
2518
  }
2460
2519
  /**
2461
2520
  * Get the node reference at a given index.
@@ -2466,7 +2525,7 @@ var DoublyLinkedList = class extends LinearLinkedBase {
2466
2525
  getNodeAt(index) {
2467
2526
  if (index < 0 || index >= this._length) return void 0;
2468
2527
  let current = this.head;
2469
- for (let i = 0; i < index; i++) current = current.next;
2528
+ for (let i = 0; i < index && current; i++) current = current.next;
2470
2529
  return current;
2471
2530
  }
2472
2531
  /**
@@ -2972,7 +3031,7 @@ var Stack = class extends IterableElementBase {
2972
3031
  static {
2973
3032
  __name(this, "Stack");
2974
3033
  }
2975
- _equals = Object.is;
3034
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
2976
3035
  /**
2977
3036
  * Create a Stack and optionally bulk-push elements.
2978
3037
  * @remarks Time O(N), Space O(N)
@@ -3608,19 +3667,13 @@ var Deque = class extends LinearBase {
3608
3667
  static {
3609
3668
  __name(this, "Deque");
3610
3669
  }
3611
- _equals = Object.is;
3612
- /**
3613
- * Create a Deque and optionally bulk-insert elements.
3614
- * @remarks Time O(N), Space O(N)
3615
- * @param [elements] - Iterable (or iterable-like) of elements/records to insert.
3616
- * @param [options] - Options such as bucketSize, toElementFn, and maxLen.
3617
- * @returns New Deque instance.
3618
- */
3670
+ _equals = /* @__PURE__ */ __name((a, b) => Object.is(a, b), "_equals");
3619
3671
  constructor(elements = [], options) {
3620
3672
  super(options);
3621
3673
  if (options) {
3622
- const { bucketSize } = options;
3674
+ const { bucketSize, autoCompactRatio } = options;
3623
3675
  if (typeof bucketSize === "number") this._bucketSize = bucketSize;
3676
+ if (typeof autoCompactRatio === "number") this._autoCompactRatio = autoCompactRatio;
3624
3677
  }
3625
3678
  let _size;
3626
3679
  if ("length" in elements) {
@@ -3646,6 +3699,30 @@ var Deque = class extends LinearBase {
3646
3699
  get bucketSize() {
3647
3700
  return this._bucketSize;
3648
3701
  }
3702
+ _autoCompactRatio = 0.5;
3703
+ /**
3704
+ * Get the auto-compaction ratio.
3705
+ * When `elements / (bucketCount * bucketSize)` drops below this ratio after
3706
+ * enough shift/pop operations, the deque auto-compacts.
3707
+ * @remarks Time O(1), Space O(1)
3708
+ * @returns Current ratio threshold. 0 means auto-compact is disabled.
3709
+ */
3710
+ get autoCompactRatio() {
3711
+ return this._autoCompactRatio;
3712
+ }
3713
+ /**
3714
+ * Set the auto-compaction ratio.
3715
+ * @remarks Time O(1), Space O(1)
3716
+ * @param value - Ratio in [0,1]. 0 disables auto-compact.
3717
+ */
3718
+ set autoCompactRatio(value) {
3719
+ this._autoCompactRatio = value;
3720
+ }
3721
+ /**
3722
+ * Counter for shift/pop operations since last compaction check.
3723
+ * Only checks ratio every `_bucketSize` operations to minimize overhead.
3724
+ */
3725
+ _compactCounter = 0;
3649
3726
  _bucketFirst = 0;
3650
3727
  /**
3651
3728
  * Get the index of the first bucket in use.
@@ -3784,6 +3861,7 @@ var Deque = class extends LinearBase {
3784
3861
  }
3785
3862
  }
3786
3863
  this._length -= 1;
3864
+ this._autoCompact();
3787
3865
  return element;
3788
3866
  }
3789
3867
  /**
@@ -3806,6 +3884,7 @@ var Deque = class extends LinearBase {
3806
3884
  }
3807
3885
  }
3808
3886
  this._length -= 1;
3887
+ this._autoCompact();
3809
3888
  return element;
3810
3889
  }
3811
3890
  /**
@@ -4138,11 +4217,40 @@ var Deque = class extends LinearBase {
4138
4217
  * @remarks Time O(N), Space O(1)
4139
4218
  * @returns void
4140
4219
  */
4220
+ /**
4221
+ * (Protected) Trigger auto-compaction if space utilization drops below threshold.
4222
+ * Only checks every `_bucketSize` operations to minimize hot-path overhead.
4223
+ * Uses element-based ratio: `elements / (bucketCount * bucketSize)`.
4224
+ */
4225
+ _autoCompact() {
4226
+ if (this._autoCompactRatio <= 0 || this._bucketCount <= 1) return;
4227
+ this._compactCounter++;
4228
+ if (this._compactCounter < this._bucketSize) return;
4229
+ this._compactCounter = 0;
4230
+ const utilization = this._length / (this._bucketCount * this._bucketSize);
4231
+ if (utilization < this._autoCompactRatio) {
4232
+ this.shrinkToFit();
4233
+ }
4234
+ }
4235
+ /**
4236
+ * Compact the deque by removing unused buckets.
4237
+ * @remarks Time O(N), Space O(1)
4238
+ * @returns True if compaction was performed (bucket count reduced).
4239
+ */
4240
+ /**
4241
+ * Compact the deque by removing unused buckets.
4242
+ * @remarks Time O(N), Space O(1)
4243
+ * @returns True if compaction was performed (bucket count reduced).
4244
+ */
4245
+ compact() {
4246
+ const before = this._bucketCount;
4247
+ this.shrinkToFit();
4248
+ return this._bucketCount < before;
4249
+ }
4141
4250
  shrinkToFit() {
4142
4251
  if (this._length === 0) return;
4143
4252
  const newBuckets = [];
4144
- if (this._bucketFirst === this._bucketLast) return;
4145
- else if (this._bucketFirst < this._bucketLast) {
4253
+ if (this._bucketFirst <= this._bucketLast) {
4146
4254
  for (let i = this._bucketFirst; i <= this._bucketLast; ++i) {
4147
4255
  newBuckets.push(this._buckets[i]);
4148
4256
  }
@@ -4157,6 +4265,8 @@ var Deque = class extends LinearBase {
4157
4265
  this._bucketFirst = 0;
4158
4266
  this._bucketLast = newBuckets.length - 1;
4159
4267
  this._buckets = newBuckets;
4268
+ this._bucketCount = newBuckets.length;
4269
+ this._compactCounter = 0;
4160
4270
  }
4161
4271
  /**
4162
4272
  * Deep clone this deque, preserving options.
@@ -4655,7 +4765,7 @@ var Heap = class _Heap extends IterableElementBase {
4655
4765
  */
4656
4766
  map(callback, options, thisArg) {
4657
4767
  const { comparator, toElementFn, ...rest } = options ?? {};
4658
- if (!comparator) throw new TypeError("Heap.map requires options.comparator for EM");
4768
+ if (!comparator) throw new TypeError(ERR.comparatorRequired("Heap.map"));
4659
4769
  const out = this._createLike([], { ...rest, comparator, toElementFn });
4660
4770
  let i = 0;
4661
4771
  for (const x of this) {
@@ -4682,18 +4792,13 @@ var Heap = class _Heap extends IterableElementBase {
4682
4792
  }
4683
4793
  _DEFAULT_COMPARATOR = /* @__PURE__ */ __name((a, b) => {
4684
4794
  if (typeof a === "object" || typeof b === "object") {
4685
- throw TypeError("When comparing object types, define a custom comparator in options.");
4795
+ throw new TypeError(ERR.comparatorRequired("Heap"));
4686
4796
  }
4687
4797
  if (a > b) return 1;
4688
4798
  if (a < b) return -1;
4689
4799
  return 0;
4690
4800
  }, "_DEFAULT_COMPARATOR");
4691
4801
  _comparator = this._DEFAULT_COMPARATOR;
4692
- /**
4693
- * Get the comparator used to order elements.
4694
- * @remarks Time O(1), Space O(1)
4695
- * @returns Comparator function.
4696
- */
4697
4802
  /**
4698
4803
  * Get the comparator used to order elements.
4699
4804
  * @remarks Time O(1), Space O(1)
@@ -4742,8 +4847,7 @@ var Heap = class _Heap extends IterableElementBase {
4742
4847
  */
4743
4848
  _createInstance(options) {
4744
4849
  const Ctor = this.constructor;
4745
- const next = new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options ?? {} });
4746
- return next;
4850
+ return new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...options ?? {} });
4747
4851
  }
4748
4852
  /**
4749
4853
  * (Protected) Create a like-kind instance seeded by elements.
@@ -4800,7 +4904,7 @@ var FibonacciHeap = class {
4800
4904
  constructor(comparator) {
4801
4905
  this.clear();
4802
4906
  this._comparator = comparator || this._defaultComparator;
4803
- if (typeof this.comparator !== "function") throw new Error("FibonacciHeap: comparator must be a function.");
4907
+ if (typeof this.comparator !== "function") throw new TypeError(ERR.notAFunction("comparator", "FibonacciHeap"));
4804
4908
  }
4805
4909
  _root;
4806
4910
  /**
@@ -5016,9 +5120,7 @@ var MaxHeap = class extends Heap {
5016
5120
  super(elements, {
5017
5121
  comparator: /* @__PURE__ */ __name((a, b) => {
5018
5122
  if (typeof a === "object" || typeof b === "object") {
5019
- throw TypeError(
5020
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
5021
- );
5123
+ throw new TypeError(ERR.comparatorRequired("MaxHeap"));
5022
5124
  }
5023
5125
  if (a < b) return 1;
5024
5126
  if (a > b) return -1;
@@ -5187,7 +5289,7 @@ var AbstractGraph = class extends IterableEntryBase {
5187
5289
  const newEdge = this.createEdge(srcOrEdge, dest, weight, value);
5188
5290
  return this._addEdge(newEdge);
5189
5291
  } else {
5190
- throw new Error("dest must be a Vertex or vertex key while srcOrEdge is an Edge");
5292
+ throw new TypeError(ERR.invalidArgument("dest must be a Vertex or vertex key when srcOrEdge is an Edge.", "Graph"));
5191
5293
  }
5192
5294
  }
5193
5295
  }
@@ -5792,8 +5894,8 @@ var AbstractGraph = class extends IterableEntryBase {
5792
5894
  const Ctor = this.constructor;
5793
5895
  const instance = new Ctor();
5794
5896
  const graph = _options?.graph;
5795
- if (graph) instance._options = { ...instance._options, ...graph };
5796
- else instance._options = { ...instance._options, ...this._options };
5897
+ if (graph) instance["_options"] = { ...instance["_options"], ...graph };
5898
+ else instance["_options"] = { ...instance["_options"], ...this._options };
5797
5899
  return instance;
5798
5900
  }
5799
5901
  /**
@@ -5826,12 +5928,10 @@ var AbstractGraph = class extends IterableEntryBase {
5826
5928
  const [va, vb] = ends;
5827
5929
  const ka = va.key;
5828
5930
  const kb = vb.key;
5829
- const hasA = g.hasVertex ? g.hasVertex(ka) : false;
5830
- const hasB = g.hasVertex ? g.hasVertex(kb) : false;
5931
+ const hasA = typeof g.hasVertex === "function" ? g.hasVertex(ka) : false;
5932
+ const hasB = typeof g.hasVertex === "function" ? g.hasVertex(kb) : false;
5831
5933
  if (hasA && hasB) {
5832
- const w = e.weight;
5833
- const val = e.value;
5834
- const newEdge = g.createEdge(ka, kb, w, val);
5934
+ const newEdge = g.createEdge(ka, kb, e.weight, e.value);
5835
5935
  g._addEdge(newEdge);
5836
5936
  }
5837
5937
  }
@@ -5869,6 +5969,92 @@ var AbstractGraph = class extends IterableEntryBase {
5869
5969
  _getVertexKey(vertexOrKey) {
5870
5970
  return vertexOrKey instanceof AbstractVertex ? vertexOrKey.key : vertexOrKey;
5871
5971
  }
5972
+ /**
5973
+ * The edge connector string used in visual output.
5974
+ * Override in subclasses (e.g., '--' for undirected, '->' for directed).
5975
+ */
5976
+ get _edgeConnector() {
5977
+ return "--";
5978
+ }
5979
+ /**
5980
+ * Generate a text-based visual representation of the graph.
5981
+ *
5982
+ * **Adjacency list format:**
5983
+ * ```
5984
+ * Graph (5 vertices, 6 edges):
5985
+ * A -> B (1), C (2)
5986
+ * B -> D (3)
5987
+ * C -> (no outgoing edges)
5988
+ * D -> A (1)
5989
+ * E (isolated)
5990
+ * ```
5991
+ *
5992
+ * @param options - Optional display settings.
5993
+ * @param options.showWeight - Whether to show edge weights (default: true).
5994
+ * @returns The visual string.
5995
+ */
5996
+ toVisual(options) {
5997
+ const showWeight = options?.showWeight ?? true;
5998
+ const vertices = [...this._vertexMap.values()];
5999
+ const vertexCount = vertices.length;
6000
+ const edgeCount = this.edgeSet().length;
6001
+ const lines = [`Graph (${vertexCount} vertices, ${edgeCount} edges):`];
6002
+ for (const vertex of vertices) {
6003
+ const neighbors = this.getNeighbors(vertex);
6004
+ if (neighbors.length === 0) {
6005
+ lines.push(` ${vertex.key} (isolated)`);
6006
+ } else {
6007
+ const edgeStrs = neighbors.map((neighbor) => {
6008
+ const edge = this.getEdge(vertex, neighbor);
6009
+ if (edge && showWeight && edge.weight !== void 0 && edge.weight !== 1) {
6010
+ return `${neighbor.key} (${edge.weight})`;
6011
+ }
6012
+ return `${neighbor.key}`;
6013
+ });
6014
+ lines.push(` ${vertex.key} ${this._edgeConnector} ${edgeStrs.join(", ")}`);
6015
+ }
6016
+ }
6017
+ return lines.join("\n");
6018
+ }
6019
+ /**
6020
+ * Generate DOT language representation for Graphviz.
6021
+ *
6022
+ * @param options - Optional display settings.
6023
+ * @param options.name - Graph name (default: 'G').
6024
+ * @param options.showWeight - Whether to label edges with weight (default: true).
6025
+ * @returns DOT format string.
6026
+ */
6027
+ toDot(options) {
6028
+ const name = options?.name ?? "G";
6029
+ const showWeight = options?.showWeight ?? true;
6030
+ const isDirected = this._edgeConnector === "->";
6031
+ const graphType = isDirected ? "digraph" : "graph";
6032
+ const edgeOp = isDirected ? "->" : "--";
6033
+ const lines = [`${graphType} ${name} {`];
6034
+ for (const vertex of this._vertexMap.values()) {
6035
+ lines.push(` "${vertex.key}";`);
6036
+ }
6037
+ const visited = /* @__PURE__ */ new Set();
6038
+ for (const vertex of this._vertexMap.values()) {
6039
+ for (const neighbor of this.getNeighbors(vertex)) {
6040
+ const edgeId = isDirected ? `${vertex.key}->${neighbor.key}` : [vertex.key, neighbor.key].sort().join("--");
6041
+ if (visited.has(edgeId)) continue;
6042
+ visited.add(edgeId);
6043
+ const edge = this.getEdge(vertex, neighbor);
6044
+ const label = edge && showWeight && edge.weight !== void 0 && edge.weight !== 1 ? ` [label="${edge.weight}"]` : "";
6045
+ lines.push(` "${vertex.key}" ${edgeOp} "${neighbor.key}"${label};`);
6046
+ }
6047
+ }
6048
+ lines.push("}");
6049
+ return lines.join("\n");
6050
+ }
6051
+ /**
6052
+ * Print the graph to console.
6053
+ * @param options - Display settings passed to `toVisual`.
6054
+ */
6055
+ print(options) {
6056
+ console.log(this.toVisual(options));
6057
+ }
5872
6058
  };
5873
6059
 
5874
6060
  // src/data-structures/graph/directed-graph.ts
@@ -5904,6 +6090,9 @@ var DirectedGraph = class _DirectedGraph extends AbstractGraph {
5904
6090
  constructor(options) {
5905
6091
  super(options);
5906
6092
  }
6093
+ get _edgeConnector() {
6094
+ return "->";
6095
+ }
5907
6096
  _outEdgeMap = /* @__PURE__ */ new Map();
5908
6097
  get outEdgeMap() {
5909
6098
  return this._outEdgeMap;
@@ -6685,6 +6874,84 @@ var UndirectedGraph = class _UndirectedGraph extends AbstractGraph {
6685
6874
  cutVertices
6686
6875
  };
6687
6876
  }
6877
+ /**
6878
+ * Find biconnected components using edge-stack Tarjan variant.
6879
+ * A biconnected component is a maximal biconnected subgraph.
6880
+ * @returns Array of edge arrays, each representing a biconnected component.
6881
+ * @remarks Time O(V + E), Space O(V + E)
6882
+ */
6883
+ getBiconnectedComponents() {
6884
+ const dfn = /* @__PURE__ */ new Map();
6885
+ const low = /* @__PURE__ */ new Map();
6886
+ const edgeStack = [];
6887
+ const components = [];
6888
+ let time = 0;
6889
+ const dfs = /* @__PURE__ */ __name((vertex, parent) => {
6890
+ dfn.set(vertex, time);
6891
+ low.set(vertex, time);
6892
+ time++;
6893
+ const neighbors = this.getNeighbors(vertex);
6894
+ let childCount = 0;
6895
+ for (const neighbor of neighbors) {
6896
+ const edge = this.getEdge(vertex, neighbor);
6897
+ if (!edge) continue;
6898
+ if (!dfn.has(neighbor)) {
6899
+ childCount++;
6900
+ edgeStack.push(edge);
6901
+ dfs(neighbor, vertex);
6902
+ low.set(vertex, Math.min(low.get(vertex), low.get(neighbor)));
6903
+ if (parent === void 0 && childCount > 1 || parent !== void 0 && low.get(neighbor) >= dfn.get(vertex)) {
6904
+ const component = [];
6905
+ let e;
6906
+ do {
6907
+ e = edgeStack.pop();
6908
+ if (e) component.push(e);
6909
+ } while (e && e !== edge);
6910
+ if (component.length > 0) components.push(component);
6911
+ }
6912
+ } else if (neighbor !== parent && dfn.get(neighbor) < dfn.get(vertex)) {
6913
+ edgeStack.push(edge);
6914
+ low.set(vertex, Math.min(low.get(vertex), dfn.get(neighbor)));
6915
+ }
6916
+ }
6917
+ }, "dfs");
6918
+ for (const vertex of this.vertexMap.values()) {
6919
+ if (!dfn.has(vertex)) {
6920
+ dfs(vertex, void 0);
6921
+ if (edgeStack.length > 0) {
6922
+ components.push([...edgeStack]);
6923
+ edgeStack.length = 0;
6924
+ }
6925
+ }
6926
+ }
6927
+ return components;
6928
+ }
6929
+ /**
6930
+ * Detect whether the graph contains a cycle.
6931
+ * Uses DFS with parent tracking.
6932
+ * @returns `true` if a cycle exists, `false` otherwise.
6933
+ * @remarks Time O(V + E), Space O(V)
6934
+ */
6935
+ hasCycle() {
6936
+ const visited = /* @__PURE__ */ new Set();
6937
+ const dfs = /* @__PURE__ */ __name((vertex, parent) => {
6938
+ visited.add(vertex);
6939
+ for (const neighbor of this.getNeighbors(vertex)) {
6940
+ if (!visited.has(neighbor)) {
6941
+ if (dfs(neighbor, vertex)) return true;
6942
+ } else if (neighbor !== parent) {
6943
+ return true;
6944
+ }
6945
+ }
6946
+ return false;
6947
+ }, "dfs");
6948
+ for (const vertex of this.vertexMap.values()) {
6949
+ if (!visited.has(vertex)) {
6950
+ if (dfs(vertex, void 0)) return true;
6951
+ }
6952
+ }
6953
+ return false;
6954
+ }
6688
6955
  /**
6689
6956
  * Get bridges discovered by `tarjan()`.
6690
6957
  * @returns Array of edges that are bridges.
@@ -6838,30 +7105,6 @@ var MapGraph = class _MapGraph extends DirectedGraph {
6838
7105
  }
6839
7106
  };
6840
7107
 
6841
- // src/common/index.ts
6842
- var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
6843
- DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
6844
- DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
6845
- return DFSOperation2;
6846
- })(DFSOperation || {});
6847
- var Range = class {
6848
- constructor(low, high, includeLow = true, includeHigh = true) {
6849
- this.low = low;
6850
- this.high = high;
6851
- this.includeLow = includeLow;
6852
- this.includeHigh = includeHigh;
6853
- }
6854
- static {
6855
- __name(this, "Range");
6856
- }
6857
- // Determine whether a key is within the range
6858
- isInRange(key, comparator) {
6859
- const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
6860
- const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
6861
- return lowCheck && highCheck;
6862
- }
6863
- };
6864
-
6865
7108
  // src/data-structures/binary-tree/binary-tree.ts
6866
7109
  var BinaryTreeNode = class {
6867
7110
  static {
@@ -7000,7 +7243,7 @@ var BinaryTreeNode = class {
7000
7243
  return "MAL_NODE";
7001
7244
  }
7002
7245
  };
7003
- var BinaryTree = class extends IterableEntryBase {
7246
+ var BinaryTree = class _BinaryTree extends IterableEntryBase {
7004
7247
  static {
7005
7248
  __name(this, "BinaryTree");
7006
7249
  }
@@ -7020,7 +7263,7 @@ var BinaryTree = class extends IterableEntryBase {
7020
7263
  if (isMapMode !== void 0) this._isMapMode = isMapMode;
7021
7264
  if (isDuplicate !== void 0) this._isDuplicate = isDuplicate;
7022
7265
  if (typeof toEntryFn === "function") this._toEntryFn = toEntryFn;
7023
- else if (toEntryFn) throw TypeError("toEntryFn must be a function type");
7266
+ else if (toEntryFn) throw new TypeError(ERR.notAFunction("toEntryFn", "BinaryTree"));
7024
7267
  }
7025
7268
  if (keysNodesEntriesOrRaws) this.setMany(keysNodesEntriesOrRaws);
7026
7269
  }
@@ -7258,7 +7501,7 @@ var BinaryTree = class extends IterableEntryBase {
7258
7501
  if (!this._root) {
7259
7502
  this._setRoot(newNode);
7260
7503
  if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
7261
- this._size = 1;
7504
+ if (newNode !== null) this._size = 1;
7262
7505
  return true;
7263
7506
  }
7264
7507
  const queue = new Queue([this._root]);
@@ -7290,7 +7533,7 @@ var BinaryTree = class extends IterableEntryBase {
7290
7533
  potentialParent.right = newNode;
7291
7534
  }
7292
7535
  if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
7293
- this._size++;
7536
+ if (newNode !== null) this._size++;
7294
7537
  return true;
7295
7538
  }
7296
7539
  return false;
@@ -7858,7 +8101,7 @@ var BinaryTree = class extends IterableEntryBase {
7858
8101
  }
7859
8102
  /**
7860
8103
  * Finds all leaf nodes in the tree.
7861
- * @remarks Time O(N), visits every node. Space O(H) for recursive stack or O(N) for iterative queue.
8104
+ * @remarks Time O(N), visits every node. Space O(H) for recursive or iterative stack.
7862
8105
  *
7863
8106
  * @template C - The type of the callback function.
7864
8107
  * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each leaf node.
@@ -7881,15 +8124,15 @@ var BinaryTree = class extends IterableEntryBase {
7881
8124
  }, "dfs");
7882
8125
  dfs(startNode);
7883
8126
  } else {
7884
- const queue = new Queue([startNode]);
7885
- while (queue.length > 0) {
7886
- const cur = queue.shift();
8127
+ const stack = [startNode];
8128
+ while (stack.length > 0) {
8129
+ const cur = stack.pop();
7887
8130
  if (this.isRealNode(cur)) {
7888
8131
  if (this.isLeaf(cur)) {
7889
8132
  leaves.push(callback(cur));
7890
8133
  }
7891
- if (this.isRealNode(cur.left)) queue.push(cur.left);
7892
- if (this.isRealNode(cur.right)) queue.push(cur.right);
8134
+ if (this.isRealNode(cur.right)) stack.push(cur.right);
8135
+ if (this.isRealNode(cur.left)) stack.push(cur.left);
7893
8136
  }
7894
8137
  }
7895
8138
  }
@@ -8258,7 +8501,7 @@ var BinaryTree = class extends IterableEntryBase {
8258
8501
  * @param node - The node.
8259
8502
  * @returns The node's key or undefined.
8260
8503
  */
8261
- _DEFAULT_NODE_CALLBACK = /* @__PURE__ */ __name((node) => node ? node.key : void 0, "_DEFAULT_NODE_CALLBACK");
8504
+ _DEFAULT_NODE_CALLBACK = /* @__PURE__ */ __name((node) => node?.key, "_DEFAULT_NODE_CALLBACK");
8262
8505
  /**
8263
8506
  * (Protected) Snapshots the current tree's configuration options.
8264
8507
  * @remarks Time O(1)
@@ -8351,42 +8594,98 @@ var BinaryTree = class extends IterableEntryBase {
8351
8594
  _displayAux(node, options) {
8352
8595
  const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8353
8596
  const emptyDisplayLayout = [["\u2500"], 1, 0, 0];
8354
- if (node === null && !isShowNull) {
8355
- return emptyDisplayLayout;
8356
- } else if (node === void 0 && !isShowUndefined) {
8357
- return emptyDisplayLayout;
8358
- } else if (this.isNIL(node) && !isShowRedBlackNIL) {
8359
- return emptyDisplayLayout;
8360
- } else if (node !== null && node !== void 0) {
8361
- const key = node.key, line = this.isNIL(node) ? "S" : String(key), width = line.length;
8362
- return _buildNodeDisplay(
8363
- line,
8364
- width,
8365
- this._displayAux(node.left, options),
8366
- this._displayAux(node.right, options)
8367
- );
8368
- } else {
8369
- const line = node === void 0 ? "U" : "N", width = line.length;
8370
- return _buildNodeDisplay(line, width, [[""], 1, 0, 0], [[""], 1, 0, 0]);
8371
- }
8372
- function _buildNodeDisplay(line, width, left, right) {
8373
- const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
8374
- const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
8375
- 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));
8376
- const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
8377
- const mergedLines = [firstLine, secondLine];
8378
- for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
8379
- const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
8380
- const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
8381
- mergedLines.push(leftLine + " ".repeat(width) + rightLine);
8597
+ const newFrame = /* @__PURE__ */ __name((n) => ({
8598
+ node: n,
8599
+ stage: 0,
8600
+ leftLayout: emptyDisplayLayout,
8601
+ rightLayout: emptyDisplayLayout
8602
+ }), "newFrame");
8603
+ const stack = [newFrame(node)];
8604
+ let result = emptyDisplayLayout;
8605
+ const setChildResult = /* @__PURE__ */ __name((layout) => {
8606
+ if (stack.length === 0) {
8607
+ result = layout;
8608
+ return;
8382
8609
  }
8383
- return [
8384
- mergedLines,
8385
- leftWidth + width + rightWidth,
8386
- Math.max(leftHeight, rightHeight) + 2,
8387
- leftWidth + Math.floor(width / 2)
8388
- ];
8610
+ const parent = stack[stack.length - 1];
8611
+ if (parent.stage === 1) parent.leftLayout = layout;
8612
+ else parent.rightLayout = layout;
8613
+ }, "setChildResult");
8614
+ while (stack.length > 0) {
8615
+ const frame = stack[stack.length - 1];
8616
+ const cur = frame.node;
8617
+ if (frame.stage === 0) {
8618
+ if (this._isDisplayLeaf(cur, options)) {
8619
+ stack.pop();
8620
+ const layout = this._resolveDisplayLeaf(cur, options, emptyDisplayLayout);
8621
+ setChildResult(layout);
8622
+ continue;
8623
+ }
8624
+ frame.stage = 1;
8625
+ stack.push(newFrame(cur.left));
8626
+ } else if (frame.stage === 1) {
8627
+ frame.stage = 2;
8628
+ stack.push(newFrame(cur.right));
8629
+ } else {
8630
+ stack.pop();
8631
+ const line = this.isNIL(cur) ? "S" : String(cur.key);
8632
+ const layout = _BinaryTree._buildNodeDisplay(line, line.length, frame.leftLayout, frame.rightLayout);
8633
+ setChildResult(layout);
8634
+ }
8635
+ }
8636
+ return result;
8637
+ }
8638
+ static _buildNodeDisplay(line, width, left, right) {
8639
+ const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
8640
+ const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
8641
+ 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));
8642
+ const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
8643
+ const mergedLines = [firstLine, secondLine];
8644
+ for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
8645
+ const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
8646
+ const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
8647
+ mergedLines.push(leftLine + " ".repeat(width) + rightLine);
8648
+ }
8649
+ return [
8650
+ mergedLines,
8651
+ leftWidth + width + rightWidth,
8652
+ Math.max(leftHeight, rightHeight) + 2,
8653
+ leftWidth + Math.floor(width / 2)
8654
+ ];
8655
+ }
8656
+ /**
8657
+ * Check if a node is a display leaf (empty, null, undefined, NIL, or real leaf).
8658
+ */
8659
+ _isDisplayLeaf(node, options) {
8660
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8661
+ if (node === null && !isShowNull) return true;
8662
+ if (node === void 0 && !isShowUndefined) return true;
8663
+ if (this.isNIL(node) && !isShowRedBlackNIL) return true;
8664
+ if (node === null || node === void 0) return true;
8665
+ const hasDisplayableLeft = this._hasDisplayableChild(node.left, options);
8666
+ const hasDisplayableRight = this._hasDisplayableChild(node.right, options);
8667
+ return !hasDisplayableLeft && !hasDisplayableRight;
8668
+ }
8669
+ _hasDisplayableChild(child, options) {
8670
+ if (child === null) return !!options.isShowNull;
8671
+ if (child === void 0) return !!options.isShowUndefined;
8672
+ if (this.isNIL(child)) return !!options.isShowRedBlackNIL;
8673
+ return true;
8674
+ }
8675
+ /**
8676
+ * Resolve a display leaf node to its layout.
8677
+ */
8678
+ _resolveDisplayLeaf(node, options, emptyDisplayLayout) {
8679
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
8680
+ if (node === null && !isShowNull) return emptyDisplayLayout;
8681
+ if (node === void 0 && !isShowUndefined) return emptyDisplayLayout;
8682
+ if (this.isNIL(node) && !isShowRedBlackNIL) return emptyDisplayLayout;
8683
+ if (node !== null && node !== void 0) {
8684
+ const line2 = this.isNIL(node) ? "S" : String(node.key);
8685
+ return _BinaryTree._buildNodeDisplay(line2, line2.length, emptyDisplayLayout, emptyDisplayLayout);
8389
8686
  }
8687
+ const line = node === void 0 ? "U" : "N";
8688
+ return _BinaryTree._buildNodeDisplay(line, line.length, [[""], 1, 0, 0], [[""], 1, 0, 0]);
8390
8689
  }
8391
8690
  /**
8392
8691
  * (Protected) Swaps the key/value properties of two nodes.
@@ -9392,9 +9691,15 @@ var BST = class extends BinaryTree {
9392
9691
  if (a < b) return -1;
9393
9692
  return 0;
9394
9693
  }
9694
+ if (a instanceof Date && b instanceof Date) {
9695
+ const ta = a.getTime();
9696
+ const tb = b.getTime();
9697
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("BST"));
9698
+ return ta > tb ? 1 : ta < tb ? -1 : 0;
9699
+ }
9395
9700
  if (typeof a === "object" || typeof b === "object") {
9396
- throw TypeError(
9397
- `When comparing object type keys, a custom comparator must be provided in the constructor's options!`
9701
+ throw new TypeError(
9702
+ ERR.comparatorRequired("BST")
9398
9703
  );
9399
9704
  }
9400
9705
  return 0;
@@ -9906,7 +10211,7 @@ var BinaryIndexedTree = class {
9906
10211
  */
9907
10212
  read(count) {
9908
10213
  if (!Number.isInteger(count)) {
9909
- throw new Error("Invalid count");
10214
+ throw new Error(ERR.invalidArgument("count must be an integer", "BinaryIndexedTree"));
9910
10215
  }
9911
10216
  return this._read(Math.max(Math.min(count, this.max), 0));
9912
10217
  }
@@ -9918,7 +10223,7 @@ var BinaryIndexedTree = class {
9918
10223
  */
9919
10224
  lowerBound(sum) {
9920
10225
  if (this.negativeCount > 0) {
9921
- throw new Error("Sequence is not non-descending");
10226
+ throw new Error(ERR.invalidOperation("Sequence is not non-descending.", "BinaryIndexedTree"));
9922
10227
  }
9923
10228
  return this._binarySearch(sum, (x, y) => x < y);
9924
10229
  }
@@ -9931,7 +10236,7 @@ var BinaryIndexedTree = class {
9931
10236
  */
9932
10237
  upperBound(sum) {
9933
10238
  if (this.negativeCount > 0) {
9934
- throw new Error("Must not be descending");
10239
+ throw new Error(ERR.invalidOperation("Sequence must not be descending.", "BinaryIndexedTree"));
9935
10240
  }
9936
10241
  return this._binarySearch(sum, (x, y) => x <= y);
9937
10242
  }
@@ -9982,10 +10287,10 @@ var BinaryIndexedTree = class {
9982
10287
  */
9983
10288
  _checkIndex(index) {
9984
10289
  if (!Number.isInteger(index)) {
9985
- throw new Error("Invalid index: Index must be an integer.");
10290
+ throw new TypeError(ERR.invalidIndex("BinaryIndexedTree"));
9986
10291
  }
9987
10292
  if (index < 0 || index >= this.max) {
9988
- throw new Error("Index out of range: Index must be within the range [0, this.max).");
10293
+ throw new RangeError(ERR.indexOutOfRange(index, 0, this.max - 1, "BinaryIndexedTree"));
9989
10294
  }
9990
10295
  }
9991
10296
  /**
@@ -11577,6 +11882,24 @@ var RedBlackTree = class extends BST {
11577
11882
  * @param [thisArg] - See parameter type for details.
11578
11883
  * @returns A new RedBlackTree with mapped entries.
11579
11884
  */
11885
+ /**
11886
+ * Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
11887
+ * sorted bulk insert, which naturally produces a balanced RBT.
11888
+ * @remarks Time O(N), Space O(N)
11889
+ */
11890
+ perfectlyBalance(iterationType) {
11891
+ const entries = [];
11892
+ for (const [key, value] of this) entries.push([key, value]);
11893
+ if (entries.length <= 1) return true;
11894
+ this.clear();
11895
+ this.setMany(
11896
+ entries.map(([k]) => k),
11897
+ entries.map(([, v]) => v),
11898
+ true
11899
+ // isBalanceAdd
11900
+ );
11901
+ return true;
11902
+ }
11580
11903
  map(callback, options, thisArg) {
11581
11904
  const out = this._createLike([], options);
11582
11905
  let index = 0;
@@ -11898,7 +12221,7 @@ var TreeSet = class _TreeSet {
11898
12221
  static createDefaultComparator() {
11899
12222
  return (a, b) => {
11900
12223
  if (typeof a === "number" && typeof b === "number") {
11901
- if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError("TreeSet: NaN is not a valid key");
12224
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN("TreeSet"));
11902
12225
  const aa = Object.is(a, -0) ? 0 : a;
11903
12226
  const bb = Object.is(b, -0) ? 0 : b;
11904
12227
  return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -11909,10 +12232,10 @@ var TreeSet = class _TreeSet {
11909
12232
  if (a instanceof Date && b instanceof Date) {
11910
12233
  const ta = a.getTime();
11911
12234
  const tb = b.getTime();
11912
- if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError("TreeSet: invalid Date key");
12235
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("TreeSet"));
11913
12236
  return ta > tb ? 1 : ta < tb ? -1 : 0;
11914
12237
  }
11915
- throw new TypeError("TreeSet: comparator is required for non-number/non-string/non-Date keys");
12238
+ throw new TypeError(ERR.comparatorRequired("TreeSet"));
11916
12239
  };
11917
12240
  }
11918
12241
  /**
@@ -11930,15 +12253,15 @@ var TreeSet = class _TreeSet {
11930
12253
  _validateKey(key) {
11931
12254
  if (!this.#isDefaultComparator) return;
11932
12255
  if (typeof key === "number") {
11933
- if (Number.isNaN(key)) throw new TypeError("TreeSet: NaN is not a valid key");
12256
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeSet"));
11934
12257
  return;
11935
12258
  }
11936
12259
  if (typeof key === "string") return;
11937
12260
  if (key instanceof Date) {
11938
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeSet: invalid Date key");
12261
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeSet"));
11939
12262
  return;
11940
12263
  }
11941
- throw new TypeError("TreeSet: comparator is required for non-number/non-string/non-Date keys");
12264
+ throw new TypeError(ERR.comparatorRequired("TreeSet"));
11942
12265
  }
11943
12266
  /**
11944
12267
  * Add a key to the set (no-op if already present).
@@ -12257,15 +12580,15 @@ var TreeMultiMap = class _TreeMultiMap {
12257
12580
  _validateKey(key) {
12258
12581
  if (!this.#isDefaultComparator) return;
12259
12582
  if (typeof key === "number") {
12260
- if (Number.isNaN(key)) throw new TypeError("TreeMultiMap: NaN is not a valid key");
12583
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMultiMap"));
12261
12584
  return;
12262
12585
  }
12263
12586
  if (typeof key === "string") return;
12264
12587
  if (key instanceof Date) {
12265
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMultiMap: invalid Date key");
12588
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMultiMap"));
12266
12589
  return;
12267
12590
  }
12268
- throw new TypeError("TreeMultiMap: comparator is required for non-number/non-string/non-Date keys");
12591
+ throw new TypeError(ERR.comparatorRequired("TreeMultiMap"));
12269
12592
  }
12270
12593
  /**
12271
12594
  * Number of distinct keys.
@@ -12687,7 +13010,7 @@ var TreeMap = class _TreeMap {
12687
13010
  [k, v] = toEntryFn(item);
12688
13011
  } else {
12689
13012
  if (!Array.isArray(item) || item.length < 2) {
12690
- throw new TypeError("TreeMap: each entry must be a [key, value] tuple");
13013
+ throw new TypeError(ERR.invalidEntry("TreeMap"));
12691
13014
  }
12692
13015
  k = item[0];
12693
13016
  v = item[1];
@@ -12708,7 +13031,7 @@ var TreeMap = class _TreeMap {
12708
13031
  static createDefaultComparator() {
12709
13032
  return (a, b) => {
12710
13033
  if (typeof a === "number" && typeof b === "number") {
12711
- if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError("TreeMap: NaN is not a valid key");
13034
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN("TreeMap"));
12712
13035
  const aa = Object.is(a, -0) ? 0 : a;
12713
13036
  const bb = Object.is(b, -0) ? 0 : b;
12714
13037
  return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -12719,24 +13042,24 @@ var TreeMap = class _TreeMap {
12719
13042
  if (a instanceof Date && b instanceof Date) {
12720
13043
  const ta = a.getTime();
12721
13044
  const tb = b.getTime();
12722
- if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError("TreeMap: invalid Date key");
13045
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate("TreeMap"));
12723
13046
  return ta > tb ? 1 : ta < tb ? -1 : 0;
12724
13047
  }
12725
- throw new TypeError("TreeMap: comparator is required for non-number/non-string/non-Date keys");
13048
+ throw new TypeError(ERR.comparatorRequired("TreeMap"));
12726
13049
  };
12727
13050
  }
12728
13051
  _validateKey(key) {
12729
13052
  if (!this.#isDefaultComparator) return;
12730
13053
  if (typeof key === "number") {
12731
- if (Number.isNaN(key)) throw new TypeError("TreeMap: NaN is not a valid key");
13054
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMap"));
12732
13055
  return;
12733
13056
  }
12734
13057
  if (typeof key === "string") return;
12735
13058
  if (key instanceof Date) {
12736
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMap: invalid Date key");
13059
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMap"));
12737
13060
  return;
12738
13061
  }
12739
- throw new TypeError("TreeMap: comparator is required for non-number/non-string/non-Date keys");
13062
+ throw new TypeError(ERR.comparatorRequired("TreeMap"));
12740
13063
  }
12741
13064
  /**
12742
13065
  * Number of entries in the map.
@@ -13060,22 +13383,22 @@ var TreeMultiSet = class _TreeMultiSet {
13060
13383
  _validateKey(key) {
13061
13384
  if (!this.#isDefaultComparator) return;
13062
13385
  if (typeof key === "number") {
13063
- if (Number.isNaN(key)) throw new TypeError("TreeMultiSet: NaN is not a valid key");
13386
+ if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN("TreeMultiSet"));
13064
13387
  return;
13065
13388
  }
13066
13389
  if (typeof key === "string") return;
13067
13390
  if (key instanceof Date) {
13068
- if (Number.isNaN(key.getTime())) throw new TypeError("TreeMultiSet: invalid Date key");
13391
+ if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate("TreeMultiSet"));
13069
13392
  return;
13070
13393
  }
13071
- throw new TypeError("TreeMultiSet: comparator is required for non-number/non-string/non-Date keys");
13394
+ throw new TypeError(ERR.comparatorRequired("TreeMultiSet"));
13072
13395
  }
13073
13396
  /**
13074
13397
  * Validates that count is a non-negative safe integer.
13075
13398
  * @remarks Time O(1), Space O(1)
13076
13399
  */
13077
13400
  _validateCount(n) {
13078
- if (!Number.isSafeInteger(n) || n < 0) throw new RangeError("TreeMultiSet: count must be a safe integer >= 0");
13401
+ if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(ERR.invalidArgument("count must be a safe integer >= 0.", "TreeMultiSet"));
13079
13402
  }
13080
13403
  /**
13081
13404
  * Total occurrences (sumCounts).
@@ -13229,7 +13552,7 @@ var TreeMultiSet = class _TreeMultiSet {
13229
13552
  * @remarks Time O(1), Space O(1)
13230
13553
  */
13231
13554
  get comparator() {
13232
- return this.#core._comparator;
13555
+ return this.#core.comparator;
13233
13556
  }
13234
13557
  // ━━━ clear ━━━
13235
13558
  /**
@@ -13366,7 +13689,7 @@ var TreeMultiSet = class _TreeMultiSet {
13366
13689
  filter(predicate) {
13367
13690
  const result = new _TreeMultiSet([], {
13368
13691
  comparator: this.#isDefaultComparator ? void 0 : this.comparator,
13369
- isMapMode: this.#core._isMapMode
13692
+ isMapMode: this.#core.isMapMode
13370
13693
  });
13371
13694
  for (const [k, c] of this.entries()) {
13372
13695
  if (predicate(k, c)) {
@@ -13406,7 +13729,7 @@ var TreeMultiSet = class _TreeMultiSet {
13406
13729
  map(mapper, options) {
13407
13730
  const result = new _TreeMultiSet([], {
13408
13731
  comparator: options?.comparator,
13409
- isMapMode: this.#core._isMapMode
13732
+ isMapMode: this.#core.isMapMode
13410
13733
  });
13411
13734
  for (const [k, c] of this.entries()) {
13412
13735
  const [newKey, newCount] = mapper(k, c);
@@ -13426,7 +13749,7 @@ var TreeMultiSet = class _TreeMultiSet {
13426
13749
  clone() {
13427
13750
  const result = new _TreeMultiSet([], {
13428
13751
  comparator: this.#isDefaultComparator ? void 0 : this.comparator,
13429
- isMapMode: this.#core._isMapMode
13752
+ isMapMode: this.#core.isMapMode
13430
13753
  });
13431
13754
  for (const [k, c] of this.entries()) {
13432
13755
  result.add(k, c);
@@ -13499,9 +13822,7 @@ var MaxPriorityQueue = class extends PriorityQueue {
13499
13822
  super(elements, {
13500
13823
  comparator: /* @__PURE__ */ __name((a, b) => {
13501
13824
  if (typeof a === "object" || typeof b === "object") {
13502
- throw TypeError(
13503
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
13504
- );
13825
+ throw new TypeError(ERR.comparatorRequired("MaxPriorityQueue"));
13505
13826
  }
13506
13827
  if (a < b) return 1;
13507
13828
  if (a > b) return -1;
@@ -13642,7 +13963,7 @@ var Matrix = class _Matrix {
13642
13963
  */
13643
13964
  add(matrix) {
13644
13965
  if (!this.isMatchForCalculate(matrix)) {
13645
- throw new Error("Matrix dimensions must match for addition.");
13966
+ throw new Error(ERR.matrixDimensionMismatch("addition"));
13646
13967
  }
13647
13968
  const resultData = [];
13648
13969
  for (let i = 0; i < this.rows; i++) {
@@ -13674,7 +13995,7 @@ var Matrix = class _Matrix {
13674
13995
  */
13675
13996
  subtract(matrix) {
13676
13997
  if (!this.isMatchForCalculate(matrix)) {
13677
- throw new Error("Matrix dimensions must match for subtraction.");
13998
+ throw new Error(ERR.matrixDimensionMismatch("subtraction"));
13678
13999
  }
13679
14000
  const resultData = [];
13680
14001
  for (let i = 0; i < this.rows; i++) {
@@ -13705,7 +14026,7 @@ var Matrix = class _Matrix {
13705
14026
  */
13706
14027
  multiply(matrix) {
13707
14028
  if (this.cols !== matrix.rows) {
13708
- throw new Error("Matrix dimensions must be compatible for multiplication (A.cols = B.rows).");
14029
+ throw new Error(ERR.matrixDimensionMismatch("multiplication (A.cols must equal B.rows)"));
13709
14030
  }
13710
14031
  const resultData = [];
13711
14032
  for (let i = 0; i < this.rows; i++) {
@@ -13739,7 +14060,7 @@ var Matrix = class _Matrix {
13739
14060
  */
13740
14061
  transpose() {
13741
14062
  if (this.data.some((row) => row.length !== this.rows)) {
13742
- throw new Error("Matrix must be rectangular for transposition.");
14063
+ throw new Error(ERR.matrixNotRectangular());
13743
14064
  }
13744
14065
  const resultData = [];
13745
14066
  for (let j = 0; j < this.cols; j++) {
@@ -13763,7 +14084,7 @@ var Matrix = class _Matrix {
13763
14084
  */
13764
14085
  inverse() {
13765
14086
  if (this.rows !== this.cols) {
13766
- throw new Error("Matrix must be square for inversion.");
14087
+ throw new Error(ERR.matrixNotSquare());
13767
14088
  }
13768
14089
  const augmentedMatrixData = [];
13769
14090
  for (let i = 0; i < this.rows; i++) {
@@ -13785,12 +14106,12 @@ var Matrix = class _Matrix {
13785
14106
  pivotRow++;
13786
14107
  }
13787
14108
  if (pivotRow === this.rows) {
13788
- throw new Error("Matrix is singular, and its inverse does not exist.");
14109
+ throw new Error(ERR.matrixSingular());
13789
14110
  }
13790
14111
  augmentedMatrix._swapRows(i, pivotRow);
13791
14112
  const pivotElement = augmentedMatrix.get(i, i) ?? 1;
13792
14113
  if (pivotElement === 0) {
13793
- throw new Error("Matrix is singular, and its inverse does not exist (division by zero).");
14114
+ throw new Error(ERR.matrixSingular());
13794
14115
  }
13795
14116
  augmentedMatrix._scaleRow(i, 1 / pivotElement);
13796
14117
  for (let j = 0; j < this.rows; j++) {
@@ -13820,9 +14141,7 @@ var Matrix = class _Matrix {
13820
14141
  */
13821
14142
  dot(matrix) {
13822
14143
  if (this.cols !== matrix.rows) {
13823
- throw new Error(
13824
- "Number of columns in the first matrix must be equal to the number of rows in the second matrix for dot product."
13825
- );
14144
+ throw new Error(ERR.matrixDimensionMismatch("dot product (A.cols must equal B.rows)"));
13826
14145
  }
13827
14146
  const resultData = [];
13828
14147
  for (let i = 0; i < this.rows; i++) {
@@ -14448,7 +14767,7 @@ var Trie = class extends IterableElementBase {
14448
14767
  for (const x of this) {
14449
14768
  const v = thisArg === void 0 ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
14450
14769
  if (typeof v !== "string") {
14451
- throw new TypeError(`Trie.map callback must return string; got ${typeof v}`);
14770
+ throw new TypeError(ERR.callbackReturnType("string", typeof v, "Trie.map"));
14452
14771
  }
14453
14772
  newTrie.add(v);
14454
14773
  }
@@ -14478,12 +14797,11 @@ var Trie = class extends IterableElementBase {
14478
14797
  */
14479
14798
  _createInstance(options) {
14480
14799
  const Ctor = this.constructor;
14481
- const next = new Ctor([], {
14800
+ return new Ctor([], {
14482
14801
  toElementFn: this.toElementFn,
14483
14802
  caseSensitive: this.caseSensitive,
14484
14803
  ...options ?? {}
14485
14804
  });
14486
- return next;
14487
14805
  }
14488
14806
  /**
14489
14807
  * (Protected) Create a like-kind trie and seed it from an iterable.
@@ -14699,6 +15017,7 @@ exports.DirectedGraph = DirectedGraph;
14699
15017
  exports.DirectedVertex = DirectedVertex;
14700
15018
  exports.DoublyLinkedList = DoublyLinkedList;
14701
15019
  exports.DoublyLinkedListNode = DoublyLinkedListNode;
15020
+ exports.ERR = ERR;
14702
15021
  exports.FibonacciHeap = FibonacciHeap;
14703
15022
  exports.FibonacciHeapNode = FibonacciHeapNode;
14704
15023
  exports.HashMap = HashMap;