data-structure-typed 2.6.0 → 2.6.1

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 (80) hide show
  1. package/.github/workflows/ci.yml +7 -2
  2. package/.github/workflows/release-package.yml +9 -2
  3. package/docs-site-docusaurus/docs/api/classes/AVLTree.md +108 -108
  4. package/docs-site-docusaurus/docs/api/classes/BST.md +101 -101
  5. package/docs-site-docusaurus/docs/api/classes/BinaryIndexedTree.md +13 -13
  6. package/docs-site-docusaurus/docs/api/classes/BinaryTree.md +66 -66
  7. package/docs-site-docusaurus/docs/api/classes/Deque.md +235 -51
  8. package/docs-site-docusaurus/docs/api/classes/DirectedGraph.md +21 -21
  9. package/docs-site-docusaurus/docs/api/classes/DoublyLinkedList.md +231 -67
  10. package/docs-site-docusaurus/docs/api/classes/FibonacciHeap.md +9 -9
  11. package/docs-site-docusaurus/docs/api/classes/FibonacciHeapNode.md +1 -1
  12. package/docs-site-docusaurus/docs/api/classes/HashMap.md +14 -14
  13. package/docs-site-docusaurus/docs/api/classes/Heap.md +117 -34
  14. package/docs-site-docusaurus/docs/api/classes/IterableElementBase.md +83 -13
  15. package/docs-site-docusaurus/docs/api/classes/LinearBase.md +124 -20
  16. package/docs-site-docusaurus/docs/api/classes/LinearLinkedBase.md +140 -32
  17. package/docs-site-docusaurus/docs/api/classes/LinkedHashMap.md +23 -23
  18. package/docs-site-docusaurus/docs/api/classes/LinkedListQueue.md +159 -51
  19. package/docs-site-docusaurus/docs/api/classes/MapGraph.md +20 -20
  20. package/docs-site-docusaurus/docs/api/classes/Matrix.md +23 -23
  21. package/docs-site-docusaurus/docs/api/classes/MaxHeap.md +117 -34
  22. package/docs-site-docusaurus/docs/api/classes/MaxPriorityQueue.md +117 -34
  23. package/docs-site-docusaurus/docs/api/classes/MinHeap.md +117 -34
  24. package/docs-site-docusaurus/docs/api/classes/MinPriorityQueue.md +117 -34
  25. package/docs-site-docusaurus/docs/api/classes/PriorityQueue.md +117 -34
  26. package/docs-site-docusaurus/docs/api/classes/Queue.md +142 -34
  27. package/docs-site-docusaurus/docs/api/classes/RedBlackTree.md +117 -117
  28. package/docs-site-docusaurus/docs/api/classes/SegmentTree.md +8 -8
  29. package/docs-site-docusaurus/docs/api/classes/SinglyLinkedList.md +158 -50
  30. package/docs-site-docusaurus/docs/api/classes/SkipList.md +21 -21
  31. package/docs-site-docusaurus/docs/api/classes/Stack.md +108 -26
  32. package/docs-site-docusaurus/docs/api/classes/TreeMap.md +33 -33
  33. package/docs-site-docusaurus/docs/api/classes/TreeMultiMap.md +75 -39
  34. package/docs-site-docusaurus/docs/api/classes/TreeSet.md +301 -39
  35. package/docs-site-docusaurus/docs/api/classes/Trie.md +110 -28
  36. package/docs-site-docusaurus/docs/api/classes/UndirectedGraph.md +20 -20
  37. package/package.json +45 -46
  38. package/src/common/error.ts +15 -32
  39. package/src/common/index.ts +0 -3
  40. package/src/data-structures/base/iterable-element-base.ts +0 -3
  41. package/src/data-structures/base/linear-base.ts +2 -36
  42. package/src/data-structures/binary-tree/avl-tree.ts +31 -529
  43. package/src/data-structures/binary-tree/binary-indexed-tree.ts +47 -572
  44. package/src/data-structures/binary-tree/binary-tree.ts +326 -1311
  45. package/src/data-structures/binary-tree/bst.ts +158 -1082
  46. package/src/data-structures/binary-tree/red-black-tree.ts +451 -1290
  47. package/src/data-structures/binary-tree/segment-tree.ts +73 -351
  48. package/src/data-structures/binary-tree/tree-map.ts +462 -5124
  49. package/src/data-structures/binary-tree/tree-multi-map.ts +302 -4914
  50. package/src/data-structures/binary-tree/tree-multi-set.ts +284 -3972
  51. package/src/data-structures/binary-tree/tree-set.ts +338 -4836
  52. package/src/data-structures/graph/abstract-graph.ts +98 -167
  53. package/src/data-structures/graph/directed-graph.ts +137 -562
  54. package/src/data-structures/graph/map-graph.ts +0 -3
  55. package/src/data-structures/graph/undirected-graph.ts +132 -511
  56. package/src/data-structures/hash/hash-map.ts +154 -582
  57. package/src/data-structures/heap/heap.ts +200 -795
  58. package/src/data-structures/linked-list/doubly-linked-list.ts +121 -865
  59. package/src/data-structures/linked-list/singly-linked-list.ts +122 -794
  60. package/src/data-structures/linked-list/skip-linked-list.ts +211 -918
  61. package/src/data-structures/matrix/matrix.ts +179 -518
  62. package/src/data-structures/matrix/navigator.ts +0 -1
  63. package/src/data-structures/priority-queue/max-priority-queue.ts +1 -6
  64. package/src/data-structures/priority-queue/min-priority-queue.ts +6 -11
  65. package/src/data-structures/priority-queue/priority-queue.ts +1 -2
  66. package/src/data-structures/queue/deque.ts +214 -882
  67. package/src/data-structures/queue/queue.ts +102 -625
  68. package/src/data-structures/stack/stack.ts +76 -505
  69. package/src/data-structures/trie/trie.ts +98 -628
  70. package/src/types/common.ts +0 -10
  71. package/src/types/data-structures/binary-tree/bst.ts +0 -7
  72. package/src/types/data-structures/binary-tree/red-black-tree.ts +0 -1
  73. package/src/types/data-structures/graph/abstract-graph.ts +0 -2
  74. package/src/types/data-structures/hash/hash-map.ts +0 -3
  75. package/src/types/data-structures/hash/index.ts +0 -1
  76. package/src/types/data-structures/matrix/navigator.ts +0 -2
  77. package/src/types/utils/utils.ts +0 -7
  78. package/src/types/utils/validate-type.ts +0 -7
  79. package/src/utils/number.ts +0 -2
  80. package/src/utils/utils.ts +0 -5
@@ -5,15 +5,15 @@
5
5
  * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
-
9
8
  import type {
10
9
  BTNRep,
11
10
  CRUD,
12
11
  EntryCallback,
13
- FamilyPosition, NodePredicate,
12
+ FamilyPosition,
13
+ IterationType,
14
+ NodePredicate,
14
15
  OptNode,
15
16
  RBTNColor,
16
- IterationType,
17
17
  RedBlackTreeOptions
18
18
  } from '../../types';
19
19
  import { BST } from './bst';
@@ -31,7 +31,6 @@ export class RedBlackTreeNode<K = any, V = any> {
31
31
  * @param [value] - Node value (unused in map mode trees).
32
32
  * @param color - Node color.
33
33
  */
34
-
35
34
  constructor(key: K, value?: V, color: RBTNColor = 'BLACK') {
36
35
  this.key = key;
37
36
  this.value = value;
@@ -45,7 +44,6 @@ export class RedBlackTreeNode<K = any, V = any> {
45
44
  * @remarks Time O(1), Space O(1)
46
45
  * @returns Left child node, or null/undefined.
47
46
  */
48
-
49
47
  get left(): RedBlackTreeNode<K, V> | null | undefined {
50
48
  return this._left;
51
49
  }
@@ -56,7 +54,6 @@ export class RedBlackTreeNode<K = any, V = any> {
56
54
  * @param v - New left node, or null/undefined.
57
55
  * @returns void
58
56
  */
59
-
60
57
  set left(v: RedBlackTreeNode<K, V> | null | undefined) {
61
58
  if (v) {
62
59
  v.parent = this;
@@ -71,7 +68,6 @@ export class RedBlackTreeNode<K = any, V = any> {
71
68
  * @remarks Time O(1), Space O(1)
72
69
  * @returns Right child node, or null/undefined.
73
70
  */
74
-
75
71
  get right(): RedBlackTreeNode<K, V> | null | undefined {
76
72
  return this._right;
77
73
  }
@@ -82,7 +78,6 @@ export class RedBlackTreeNode<K = any, V = any> {
82
78
  * @param v - New right node, or null/undefined.
83
79
  * @returns void
84
80
  */
85
-
86
81
  set right(v: RedBlackTreeNode<K, V> | null | undefined) {
87
82
  if (v) {
88
83
  v.parent = this;
@@ -98,6 +93,7 @@ export class RedBlackTreeNode<K = any, V = any> {
98
93
  *
99
94
  * @returns The height.
100
95
  */
96
+
101
97
  /* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
102
98
  get height(): number {
103
99
  return this._height;
@@ -138,6 +134,7 @@ export class RedBlackTreeNode<K = any, V = any> {
138
134
  *
139
135
  * @returns The subtree node count.
140
136
  */
137
+
141
138
  /* istanbul ignore next -- internal field, exercised indirectly via tree operations */
142
139
  get count(): number {
143
140
  return this._count;
@@ -153,13 +150,11 @@ export class RedBlackTreeNode<K = any, V = any> {
153
150
  if (!this.parent) {
154
151
  return this.left || this.right ? 'ROOT' : 'ISOLATED';
155
152
  }
156
-
157
153
  if (this.parent.left === this) {
158
154
  return this.left || this.right ? 'ROOT_LEFT' : 'LEFT';
159
155
  } else if (this.parent.right === this) {
160
156
  return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
161
157
  }
162
-
163
158
  /* istanbul ignore next -- defensive: unreachable if tree structure is correct */
164
159
  return 'MAL_NODE';
165
160
  }
@@ -250,26 +245,41 @@ export class RedBlackTreeNode<K = any, V = any> {
250
245
  * console.log(stocksInRange.some((s: any) => s.symbol === 'META')); // true;
251
246
  * console.log(stocksInRange.some((s: any) => s.symbol === 'MSFT')); // true;
252
247
  */
253
-
254
248
  export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implements IBinaryTree<K, V, R> {
249
+ /**
250
+ * (Internal) Header sentinel:
251
+ * - header.parent -> root
252
+ * - header._left -> min (or NIL)
253
+ * - header._right -> max (or NIL)
254
+ *
255
+ * IMPORTANT:
256
+ * - This header is NOT part of the actual tree.
257
+ * - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
258
+ * and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
259
+ */
260
+ protected _header: RedBlackTreeNode<K, V>;
261
+
262
+ /**
263
+ * (Internal) Cache of the current minimum and maximum nodes.
264
+ * Used for fast-path insert/update when keys are monotonic or near-boundary.
265
+ */
266
+ protected _minNode: RedBlackTreeNode<K, V> | undefined;
267
+ protected _maxNode: RedBlackTreeNode<K, V> | undefined;
268
+
255
269
  constructor(
256
270
  keysNodesEntriesOrRaws: Iterable<
257
271
  K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
258
272
  > = [],
259
273
  options?: RedBlackTreeOptions<K, V, R>
260
274
  ) {
261
-
262
275
  super([], options);
263
-
264
276
  this._root = this.NIL;
265
-
266
277
  // Not part of the actual tree; used only as an internal cache hub.
267
278
  this._header = new RedBlackTreeNode<K, V>(undefined as K, undefined, 'BLACK');
268
279
  this._header.parent = this.NIL;
269
280
  // Avoid using accessors here: they would set NIL.parent and can corrupt sentinel invariants.
270
281
  this._header._left = this.NIL;
271
282
  this._header._right = this.NIL;
272
-
273
283
  if (keysNodesEntriesOrRaws) {
274
284
  this.setMany(keysNodesEntriesOrRaws);
275
285
  }
@@ -277,26 +287,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
277
287
 
278
288
  protected override _root: RedBlackTreeNode<K, V> | undefined;
279
289
 
280
- /**
281
- * (Internal) Header sentinel:
282
- * - header.parent -> root
283
- * - header._left -> min (or NIL)
284
- * - header._right -> max (or NIL)
285
- *
286
- * IMPORTANT:
287
- * - This header is NOT part of the actual tree.
288
- * - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
289
- * and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
290
- */
291
- protected _header: RedBlackTreeNode<K, V>;
292
-
293
- /**
294
- * (Internal) Cache of the current minimum and maximum nodes.
295
- * Used for fast-path insert/update when keys are monotonic or near-boundary.
296
- */
297
- protected _minNode: RedBlackTreeNode<K, V> | undefined;
298
- protected _maxNode: RedBlackTreeNode<K, V> | undefined;
299
-
300
290
  /**
301
291
  * Get the current root node.
302
292
  * @remarks Time O(1), Space O(1)
@@ -324,7 +314,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
324
314
  * @param keyNodeOrEntry - See parameter type for details.
325
315
  * @returns True if the value is a RedBlackTreeNode.
326
316
  */
327
-
328
317
  override isNode(
329
318
  keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
330
319
  ): keyNodeOrEntry is RedBlackTreeNode<K, V> {
@@ -334,170 +323,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
334
323
  /**
335
324
  * Remove all nodes, clear the key→value store (if in map mode) and internal caches.
336
325
  * @remarks Time O(n), Space O(1)
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
-
447
-
448
-
449
-
450
-
451
-
452
-
453
-
454
-
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
-
490
-
491
-
492
-
493
-
494
-
495
-
496
- * @example
497
- * // Remove all entries
498
- * const rbt = new RedBlackTree<number>([1, 2, 3]);
499
- * rbt.clear();
500
- * console.log(rbt.isEmpty()); // true;
326
+ * @example
327
+ * // Remove all entries
328
+ * const rbt = new RedBlackTree<number>([1, 2, 3]);
329
+ * rbt.clear();
330
+ * console.log(rbt.isEmpty()); // true;
501
331
  */
502
332
  override clear() {
503
333
  super.clear();
@@ -507,569 +337,137 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
507
337
  this._setMaxCache(undefined);
508
338
  }
509
339
 
510
-
511
340
  /**
512
- * (Internal) Find a node by key using a tight BST walk (no allocations).
341
+ * Insert/update using a hint node to speed up nearby insertions.
513
342
  *
514
- * NOTE: This uses `header.parent` as the canonical root pointer.
343
+ * close to the expected insertion position (often the previously returned node in a loop).
344
+ *
345
+ * When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
346
+ * normal root-to-leaf search and reduce constant factors.
347
+ *
348
+ * When the hint does not match (random workloads), this will fall back to the normal set path.
515
349
  * @remarks Time O(log n) average, Space O(1)
516
350
  */
517
- protected _findNodeByKey(key: K): RedBlackTreeNode<K, V> | undefined {
518
- const NIL = this.NIL;
351
+ setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
352
+ if (!hint || !this.isRealNode(hint)) {
353
+ return this._setKVNode(key, value)?.node;
354
+ }
519
355
  const cmp = this._compare.bind(this);
520
-
521
- let cur = (this._header.parent) ?? NIL;
522
- while (cur !== NIL) {
523
- const c = cmp(key, cur.key);
524
- if (c < 0) cur = cur.left ?? NIL;
525
- else if (c > 0) cur = cur.right ?? NIL;
526
- else return cur;
356
+ const c0 = cmp(key, hint.key);
357
+ if (c0 === 0) {
358
+ hint.value = value;
359
+ if (this._isMapMode) this._store.set(key, hint);
360
+ return hint;
527
361
  }
528
- return undefined;
529
- }
530
-
531
- /**
532
- * (Internal) In-order predecessor of a node in a BST.
533
- * @remarks Time O(log n) average, Space O(1)
534
- */
535
- protected _predecessorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
536
- const NIL = this.NIL;
537
- if (node.left && node.left !== NIL) {
538
- let cur = node.left;
539
- while (cur.right && cur.right !== NIL) cur = cur.right;
540
- return cur;
362
+ if (c0 < 0) {
363
+ // Ultra-fast path: direct attach if the target slot is empty.
364
+ if (!this.isRealNode(hint.left)) {
365
+ const newNode = this.createNode(key, value);
366
+ if (!this.isRealNode(newNode)) return undefined;
367
+ this._attachNewNode(hint, 'left', newNode);
368
+ if (this._isMapMode) this._store.set(key, newNode);
369
+ this._size++;
370
+ // Maintain header/min/max caches.
371
+ const NIL = this.NIL;
372
+ const hMin = this._header._left ?? NIL;
373
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
374
+ const hMax = this._header._right ?? NIL;
375
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
376
+ return newNode;
377
+ }
378
+ const pred = this._predecessorOf(hint);
379
+ if (pred && cmp(pred.key, key) >= 0) {
380
+ return this._setKVNode(key, value)?.node;
381
+ }
382
+ // Try attach as right of pred.
383
+ if (pred && !this.isRealNode(pred.right)) {
384
+ const newNode = this.createNode(key, value);
385
+ if (!this.isRealNode(newNode)) return undefined;
386
+ this._attachNewNode(pred, 'right', newNode);
387
+ if (this._isMapMode) this._store.set(key, newNode);
388
+ this._size++;
389
+ // Maintain header/min/max caches.
390
+ const NIL = this.NIL;
391
+ const hMin = this._header._left ?? NIL;
392
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
393
+ const hMax = this._header._right ?? NIL;
394
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
395
+ return newNode;
396
+ }
397
+ /* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
398
+ return this._setKVNode(key, value)?.node;
541
399
  }
542
- let cur: RedBlackTreeNode<K, V> | undefined = node;
543
- let p = node.parent;
544
- while (p && cur === p.left) {
545
- cur = p;
546
- p = p.parent;
400
+ // c0 > 0
401
+ // Ultra-fast path: direct attach if the target slot is empty.
402
+ if (!this.isRealNode(hint.right)) {
403
+ const newNode = this.createNode(key, value);
404
+ if (!this.isRealNode(newNode)) return undefined;
405
+ this._attachNewNode(hint, 'right', newNode);
406
+ if (this._isMapMode) this._store.set(key, newNode);
407
+ this._size++;
408
+ // Maintain header/min/max caches.
409
+ const NIL = this.NIL;
410
+ const hMin = this._header._left ?? NIL;
411
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
412
+ const hMax = this._header._right ?? NIL;
413
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
414
+ return newNode;
547
415
  }
548
- return p;
549
- }
550
-
551
- /**
552
- * (Internal) In-order successor of a node in a BST.
553
- * @remarks Time O(log n) average, Space O(1)
554
- */
555
- protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
556
- const NIL = this.NIL;
557
- if (node.right && node.right !== NIL) {
558
- let cur = node.right;
559
- while (cur.left && cur.left !== NIL) cur = cur.left;
560
- return cur;
416
+ const succ = this._successorOf(hint);
417
+ if (succ && cmp(succ.key, key) <= 0) {
418
+ return this._setKVNode(key, value)?.node;
561
419
  }
562
- let cur: RedBlackTreeNode<K, V> | undefined = node;
563
- let p = node.parent;
564
- while (p && cur === p.right) {
565
- cur = p;
566
- p = p.parent;
420
+ if (succ && !this.isRealNode(succ.left)) {
421
+ const newNode = this.createNode(key, value);
422
+ if (!this.isRealNode(newNode)) return undefined;
423
+ this._attachNewNode(succ, 'left', newNode);
424
+ if (this._isMapMode) this._store.set(key, newNode);
425
+ this._size++;
426
+ // Maintain header/min/max caches.
427
+ const NIL = this.NIL;
428
+ const hMin = this._header._left ?? NIL;
429
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
430
+ const hMax = this._header._right ?? NIL;
431
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
432
+ return newNode;
567
433
  }
568
- return p;
434
+ /* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
435
+ return this._setKVNode(key, value)?.node;
569
436
  }
570
437
 
571
438
  /**
572
- * (Internal) Attach a new node directly under a known parent/side (no search).
573
- *
574
- * This is a performance-oriented helper used by boundary fast paths and hinted insertion.
575
- * It will:
576
- * - wire parent/child pointers (using accessors, so parent pointers are updated)
577
- * - initialize children to NIL
578
- * - mark the new node RED, then run insert fix-up
579
- *
580
- * Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
439
+ * Boolean wrapper for setWithHintNode.
581
440
  * @remarks Time O(log n) average, Space O(1)
582
441
  */
583
- protected _attachNewNode(parent: RedBlackTreeNode<K, V>, side: 'left' | 'right', node: RedBlackTreeNode<K, V>): void {
584
- const NIL = this.NIL;
585
- node.parent = parent;
586
- if (side === 'left') parent.left = node;
587
- else parent.right = node;
588
- node.left = NIL;
589
- node.right = NIL;
590
- node.color = 'RED';
591
- this._updateCountAlongPath(node);
592
- this._insertFixup(node);
593
- if (this.isRealNode(this._root)) this._root.color = 'BLACK';
594
- }
595
-
596
- /**
597
- * (Internal) a single source of truth for min/max is header._left/_right.
598
- * Keep legacy _minNode/_maxNode mirrored for compatibility.
599
- * @remarks Time O(1), Space O(1)
600
- */
601
- /**
602
- * (Internal) Update min cache pointers (header._left is the canonical min pointer).
603
- * @remarks Time O(1), Space O(1)
604
- */
605
- protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
606
- this._minNode = node;
607
- this._header._left = node ?? this.NIL;
608
- }
609
-
610
- /**
611
- * (Internal) Update max cache pointers (header._right is the canonical max pointer).
612
- * @remarks Time O(1), Space O(1)
613
- */
614
- protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
615
- this._maxNode = node;
616
- this._header._right = node ?? this.NIL;
442
+ setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
443
+ return this.setWithHintNode(key, value, hint) !== undefined;
617
444
  }
618
445
 
619
446
  /**
620
- * (Internal) Core set implementation returning the affected node.
447
+ * Insert or update a key/value (map mode) or key-only (set mode).
621
448
  *
622
- * Hot path goals:
623
- * - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
624
- * - Use header min/max caches to fast-path boundary inserts.
625
- * - Keep header._left/_right as canonical min/max pointers.
449
+ * This method is optimized for:
450
+ * - monotonic inserts via min/max boundary fast paths
451
+ * - updates via a single-pass search (no double walk)
626
452
  *
627
- * Return value:
628
- * - `{ node, created:false }` when an existing key is updated
629
- * - `{ node, created:true }` when a new node is inserted
630
- * - `undefined` only on unexpected internal failure.
631
453
  * @remarks Time O(log n) average, Space O(1)
632
- */
633
- protected _setKVNode(key: K, nextValue?: V): { node: RedBlackTreeNode<K, V>; created: boolean } | undefined {
634
- const NIL = this.NIL;
635
- const comparator = this._comparator;
636
-
637
- // Read via header to avoid undefined checks (header uses NIL when empty).
638
- const header = this._header;
639
- const minN = header._left ?? NIL;
640
- if (minN !== NIL) {
641
- const cMin = comparator(key, minN.key);
642
- if (cMin === 0) {
643
- minN.value = nextValue as V;
644
- if (this._isMapMode) this._store.set(key, minN);
645
- return { node: minN, created: false };
646
- }
647
- // Boundary attach: if key is smaller than current min and min has no left child.
648
- // Inline NIL/null/undefined check to avoid isRealNode overhead on hot path.
649
- const minL = minN.left;
650
- if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
651
- const newNode = this.createNode(key, nextValue);
652
- this._attachNewNode(minN, 'left', newNode);
653
- if (this._isMapMode) this._store.set(newNode.key, newNode);
654
- this._size++;
655
- this._setMinCache(newNode);
656
- // If max is not initialized yet (tree had 0/1 nodes), mirror max too.
657
- if (header._right === NIL) this._setMaxCache(newNode);
658
- return { node: newNode, created: true };
659
- }
660
-
661
- // Only touch max when key is not less than min.
662
- if (cMin > 0) {
663
- const maxN = header._right ?? NIL;
664
- // Boundary attach: if key is greater than current max and max has no right child.
665
- const cMax = comparator(key, maxN.key);
666
- if (cMax === 0) {
667
- maxN.value = nextValue as V;
668
- if (this._isMapMode) this._store.set(key, maxN);
669
- return { node: maxN, created: false };
670
- }
671
- const maxR = maxN.right;
672
- if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
673
- const newNode = this.createNode(key, nextValue);
674
- this._attachNewNode(maxN, 'right', newNode);
675
- if (this._isMapMode) this._store.set(newNode.key, newNode);
676
- this._size++;
677
- this._setMaxCache(newNode);
678
- if (header._left === NIL) this._setMinCache(newNode);
679
- return { node: newNode, created: true };
680
- }
681
- }
682
- }
683
-
684
- // Normal path: single-pass search + insert/update (avoid double-walking the tree).
685
- const cmp = comparator;
686
- const isMapMode = this._isMapMode;
687
- const store = this._store;
688
- let current = this._header.parent ?? NIL;
689
- let parent: RedBlackTreeNode<K, V> | undefined;
690
- let lastCompared = 0;
691
-
692
- while (current !== NIL) {
693
- parent = current;
694
- lastCompared = cmp(key, current.key);
695
- if (lastCompared < 0) current = current.left ?? NIL;
696
- else if (lastCompared > 0) current = current.right ?? NIL;
697
- else {
698
- // Update existing.
699
- current.value = nextValue as V;
700
- if (isMapMode) store.set(key, current);
701
- return { node: current, created: false };
702
- }
703
- }
704
-
705
- // Insert new.
706
- const newNode = this.createNode(key, nextValue);
707
- // createNode always returns a real node in RedBlackTree.
708
- newNode.parent = parent;
709
-
710
- if (!parent) {
711
- this._setRoot(newNode);
712
- } else if (lastCompared < 0) {
713
- parent.left = newNode;
714
- } else {
715
- parent.right = newNode;
716
- }
717
-
718
- newNode.left = NIL;
719
- newNode.right = NIL;
720
- newNode.color = 'RED';
721
-
722
- this._updateCountAlongPath(newNode);
723
- this._insertFixup(newNode);
724
- if (this.isRealNode(this._root)) this._root.color = 'BLACK';
725
- else return undefined;
726
-
727
- if (isMapMode) store.set(newNode.key, newNode);
728
- this._size++;
729
-
730
- // Maintain min/max caches on insertion (header.left/right are canonical).
731
- const hMin = this._header._left ?? NIL;
732
- const hMax = this._header._right ?? NIL;
733
-
734
- // Fast-path: empty tree or attaching directly to an extreme.
735
- if (hMin === NIL || hMax === NIL) {
736
- this._setMinCache(newNode);
737
- this._setMaxCache(newNode);
738
- } else /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ if (parent === hMax && lastCompared > 0) {
739
- /* istanbul ignore next */ this._setMaxCache(newNode);
740
- } else /* istanbul ignore next */ if (parent === hMin && lastCompared < 0) {
741
- /* istanbul ignore next */ this._setMinCache(newNode);
742
- } else {
743
- if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
744
- if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
745
- }
746
-
747
- return { node: newNode, created: true };
748
- }
749
-
750
- /**
751
- * (Internal) Boolean wrapper around `_setKVNode`.
752
- *
753
- * Includes a map-mode update fast-path:
754
- * - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
755
- * require any tree search/rotation (tree shape depends only on key).
756
- * - This path is intentionally limited to `nextValue !== undefined` to preserve existing
757
- * semantics for `undefined` values.
758
- * @remarks Time O(log n) average, Space O(1)
759
- */
760
- protected _setKV(key: K, nextValue?: V): boolean {
761
- if (this._isMapMode) {
762
- const store = this._store;
763
- const node = store.get(key);
764
- if (node) {
765
- node.value = nextValue as V;
766
- return true;
767
- }
768
- }
769
-
770
- return this._setKVNode(key, nextValue) !== undefined;
771
- }
772
-
773
- /**
774
- * Insert/update using a hint node to speed up nearby insertions.
775
- *
776
- * close to the expected insertion position (often the previously returned node in a loop).
777
- *
778
- * When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
779
- * normal root-to-leaf search and reduce constant factors.
780
- *
781
- * When the hint does not match (random workloads), this will fall back to the normal set path.
782
- * @remarks Time O(log n) average, Space O(1)
783
- */
784
- setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
785
- if (!hint || !this.isRealNode(hint)) {
786
- return this._setKVNode(key, value)?.node;
787
- }
788
-
789
- const cmp = this._compare.bind(this);
790
- const c0 = cmp(key, hint.key);
791
- if (c0 === 0) {
792
- hint.value = value;
793
- if (this._isMapMode) this._store.set(key, hint);
794
- return hint;
795
- }
796
-
797
- if (c0 < 0) {
798
- // Ultra-fast path: direct attach if the target slot is empty.
799
- if (!this.isRealNode(hint.left)) {
800
- const newNode = this.createNode(key, value);
801
- if (!this.isRealNode(newNode)) return undefined;
802
- this._attachNewNode(hint, 'left', newNode);
803
- if (this._isMapMode) this._store.set(key, newNode);
804
- this._size++;
805
- // Maintain header/min/max caches.
806
- const NIL = this.NIL;
807
- const hMin = this._header._left ?? NIL;
808
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
809
- const hMax = this._header._right ?? NIL;
810
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
811
- return newNode;
812
- }
813
-
814
- const pred = this._predecessorOf(hint);
815
- if (pred && cmp(pred.key, key) >= 0) {
816
- return this._setKVNode(key, value)?.node;
817
- }
818
-
819
- // Try attach as right of pred.
820
- if (pred && !this.isRealNode(pred.right)) {
821
- const newNode = this.createNode(key, value);
822
- if (!this.isRealNode(newNode)) return undefined;
823
- this._attachNewNode(pred, 'right', newNode);
824
- if (this._isMapMode) this._store.set(key, newNode);
825
- this._size++;
826
- // Maintain header/min/max caches.
827
- const NIL = this.NIL;
828
- const hMin = this._header._left ?? NIL;
829
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
830
- const hMax = this._header._right ?? NIL;
831
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
832
- return newNode;
833
- }
834
-
835
- /* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
836
- return this._setKVNode(key, value)?.node;
837
- }
838
-
839
- // c0 > 0
840
- // Ultra-fast path: direct attach if the target slot is empty.
841
- if (!this.isRealNode(hint.right)) {
842
- const newNode = this.createNode(key, value);
843
- if (!this.isRealNode(newNode)) return undefined;
844
- this._attachNewNode(hint, 'right', newNode);
845
- if (this._isMapMode) this._store.set(key, newNode);
846
- this._size++;
847
- // Maintain header/min/max caches.
848
- const NIL = this.NIL;
849
- const hMin = this._header._left ?? NIL;
850
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
851
- const hMax = this._header._right ?? NIL;
852
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
853
- return newNode;
854
- }
855
-
856
- const succ = this._successorOf(hint);
857
- if (succ && cmp(succ.key, key) <= 0) {
858
- return this._setKVNode(key, value)?.node;
859
- }
860
-
861
- if (succ && !this.isRealNode(succ.left)) {
862
- const newNode = this.createNode(key, value);
863
- if (!this.isRealNode(newNode)) return undefined;
864
- this._attachNewNode(succ, 'left', newNode);
865
- if (this._isMapMode) this._store.set(key, newNode);
866
- this._size++;
867
- // Maintain header/min/max caches.
868
- const NIL = this.NIL;
869
- const hMin = this._header._left ?? NIL;
870
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
871
- const hMax = this._header._right ?? NIL;
872
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
873
- return newNode;
874
- }
875
-
876
- /* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
877
- return this._setKVNode(key, value)?.node;
878
- }
879
-
880
- /**
881
- * Boolean wrapper for setWithHintNode.
882
- * @remarks Time O(log n) average, Space O(1)
883
- */
884
- setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
885
- return this.setWithHintNode(key, value, hint) !== undefined;
886
- }
887
-
888
- /**
889
- * Insert or update a key/value (map mode) or key-only (set mode).
890
- *
891
- * This method is optimized for:
892
- * - monotonic inserts via min/max boundary fast paths
893
- * - updates via a single-pass search (no double walk)
894
- *
895
- * @remarks Time O(log n) average, Space O(1)
896
-
897
-
898
-
899
-
900
-
901
-
902
-
903
-
904
-
905
-
906
-
907
-
908
-
909
-
910
-
911
-
912
-
913
-
914
-
915
-
916
-
917
-
918
-
919
-
920
-
921
-
922
-
923
-
924
-
925
-
926
-
927
-
928
-
929
-
930
-
931
-
932
-
933
-
934
-
935
-
936
-
937
-
938
-
939
-
940
-
941
-
942
-
943
-
944
-
945
-
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
-
955
-
956
-
957
-
958
-
959
-
960
-
961
-
962
-
963
-
964
-
965
-
966
-
967
-
968
-
969
-
970
-
971
-
972
-
973
-
974
-
975
-
976
-
977
-
978
-
979
-
980
-
981
-
982
-
983
-
984
-
985
-
986
-
987
-
988
-
989
-
990
-
991
-
992
-
993
-
994
-
995
-
996
-
997
-
998
-
999
-
1000
-
1001
-
1002
-
1003
-
1004
-
1005
-
1006
-
1007
-
1008
-
1009
-
1010
-
1011
-
1012
-
1013
-
1014
-
1015
-
1016
-
1017
-
1018
-
1019
-
1020
-
1021
-
1022
-
1023
-
1024
-
1025
-
1026
-
1027
-
1028
-
1029
-
1030
-
1031
-
1032
-
1033
-
1034
-
1035
-
1036
-
1037
-
1038
-
1039
-
1040
-
1041
-
1042
-
1043
-
1044
-
1045
-
1046
-
1047
-
1048
-
1049
-
1050
-
1051
-
1052
-
1053
-
1054
-
1055
-
1056
- * @example
1057
- * // basic Red-Black Tree with simple number keys
1058
- * // Create a simple Red-Black Tree with numeric keys
1059
- * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
1060
- *
1061
- * tree.print();
1062
- * // _2___
1063
- * // / \
1064
- * // 1 _8_
1065
- * // / \
1066
- * // 5 9
1067
- *
1068
- * // Verify the tree maintains sorted order
1069
- * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
1070
- *
1071
- * // Check size
1072
- * console.log(tree.size); // 5;
454
+ * @example
455
+ * // basic Red-Black Tree with simple number keys
456
+ * // Create a simple Red-Black Tree with numeric keys
457
+ * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
458
+ *
459
+ * tree.print();
460
+ * // _2___
461
+ * // / \
462
+ * // 1 _8_
463
+ * // / \
464
+ * // 5 9
465
+ *
466
+ * // Verify the tree maintains sorted order
467
+ * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
468
+ *
469
+ * // Check size
470
+ * console.log(tree.size); // 5;
1073
471
  */
1074
472
  override set(
1075
473
  keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
@@ -1078,24 +476,19 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1078
476
  // Common path: tree.set(key, value) or tree.set([key, value]).
1079
477
  if (!this.isNode(keyNodeOrEntry)) {
1080
478
  if (keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false;
1081
-
1082
479
  if (this.isEntry(keyNodeOrEntry)) {
1083
480
  const key = keyNodeOrEntry[0];
1084
481
  if (key === null || key === undefined) return false;
1085
482
  const nextValue = value ?? keyNodeOrEntry[1];
1086
483
  return this._setKV(key, nextValue);
1087
484
  }
1088
-
1089
485
  // key-only
1090
486
  return this._setKV(keyNodeOrEntry, value);
1091
487
  }
1092
-
1093
488
  // Node insertion path (advanced usage)
1094
489
  const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
1095
490
  if (!this.isRealNode(newNode)) return false;
1096
-
1097
491
  const insertStatus = this._insert(newNode);
1098
-
1099
492
  if (insertStatus === 'CREATED') {
1100
493
  if (this.isRealNode(this._root)) {
1101
494
  this._root.color = 'BLACK';
@@ -1131,198 +524,34 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1131
524
  * @remarks Time O(log n) average, Space O(1)
1132
525
  * @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
1133
526
  * @returns Array with deletion metadata (removed node, rebalancing hint if any).
1134
-
1135
-
1136
-
1137
-
1138
-
1139
-
1140
-
1141
-
1142
-
1143
-
1144
-
1145
-
1146
-
1147
-
1148
-
1149
-
1150
-
1151
-
1152
-
1153
-
1154
-
1155
-
1156
-
1157
-
1158
-
1159
-
1160
-
1161
-
1162
-
1163
-
1164
-
1165
-
1166
-
1167
-
1168
-
1169
-
1170
-
1171
-
1172
-
1173
-
1174
-
1175
-
1176
-
1177
-
1178
-
1179
-
1180
-
1181
-
1182
-
1183
-
1184
-
1185
-
1186
-
1187
-
1188
-
1189
-
1190
-
1191
-
1192
-
1193
-
1194
-
1195
-
1196
-
1197
-
1198
-
1199
-
1200
-
1201
-
1202
-
1203
-
1204
-
1205
-
1206
-
1207
-
1208
-
1209
-
1210
-
1211
-
1212
-
1213
-
1214
-
1215
-
1216
-
1217
-
1218
-
1219
-
1220
-
1221
-
1222
-
1223
-
1224
-
1225
-
1226
-
1227
-
1228
-
1229
-
1230
-
1231
-
1232
-
1233
-
1234
-
1235
-
1236
-
1237
-
1238
-
1239
-
1240
-
1241
-
1242
-
1243
-
1244
-
1245
-
1246
-
1247
-
1248
-
1249
-
1250
-
1251
-
1252
-
1253
-
1254
-
1255
-
1256
-
1257
-
1258
-
1259
-
1260
-
1261
-
1262
-
1263
-
1264
-
1265
-
1266
-
1267
-
1268
-
1269
-
1270
-
1271
-
1272
-
1273
-
1274
-
1275
-
1276
-
1277
-
1278
-
1279
-
1280
-
1281
-
1282
-
1283
-
1284
-
1285
-
1286
-
1287
-
1288
-
1289
-
1290
-
1291
-
1292
-
1293
-
1294
-
1295
-
1296
- * @example
1297
- * // Remove and rebalance
1298
- * const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
1299
- * rbt.delete(5);
1300
- * console.log(rbt.has(5)); // false;
1301
- * console.log(rbt.size); // 4;
527
+ * @example
528
+ * // Remove and rebalance
529
+ * const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
530
+ * rbt.delete(5);
531
+ * console.log(rbt.has(5)); // false;
532
+ * console.log(rbt.size); // 4;
1302
533
  */
1303
534
  override delete(
1304
535
  keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
1305
536
  ): boolean {
1306
537
  if (keyNodeEntryRawOrPredicate === null) return false;
1307
-
1308
538
  let nodeToDelete: OptNode<RedBlackTreeNode<K, V>>;
1309
539
  if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
1310
- else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
1311
-
540
+ else
541
+ nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate)
542
+ ? keyNodeEntryRawOrPredicate
543
+ : this.getNode(keyNodeEntryRawOrPredicate);
1312
544
  if (!nodeToDelete) {
1313
545
  return false;
1314
546
  }
1315
-
1316
547
  // Track min/max cache updates before structural modifications.
1317
548
  const willDeleteMin = nodeToDelete === this._minNode;
1318
549
  const willDeleteMax = nodeToDelete === this._maxNode;
1319
550
  const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : undefined;
1320
551
  const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
1321
-
1322
552
  let originalColor = nodeToDelete.color;
1323
553
  const NIL = this.NIL;
1324
554
  let replacementNode: RedBlackTreeNode<K, V> = NIL;
1325
-
1326
555
  if (!this.isRealNode(nodeToDelete.left)) {
1327
556
  // No real left child → replace with right (may be NIL)
1328
557
  replacementNode = nodeToDelete.right ?? NIL;
@@ -1337,7 +566,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1337
566
  if (successor) {
1338
567
  originalColor = successor.color;
1339
568
  replacementNode = successor.right ?? NIL;
1340
-
1341
569
  if (successor.parent === nodeToDelete) {
1342
570
  // Even if replacementNode is NIL, set its parent for fixup
1343
571
  replacementNode.parent = successor;
@@ -1348,7 +576,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1348
576
  successor.right.parent = successor;
1349
577
  }
1350
578
  }
1351
-
1352
579
  this._transplant(nodeToDelete, successor);
1353
580
  successor.left = nodeToDelete.left;
1354
581
  if (successor.left) {
@@ -1359,10 +586,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1359
586
  }
1360
587
  if (this._isMapMode) this._store.delete(nodeToDelete.key);
1361
588
  this._size--;
1362
-
1363
589
  // Update order-statistic counts from replacement up to root
1364
- this._updateCountAlongPath(replacementNode?.parent as RedBlackTreeNode<K, V> | undefined ?? replacementNode);
1365
-
590
+ this._updateCountAlongPath((replacementNode?.parent as RedBlackTreeNode<K, V> | undefined) ?? replacementNode);
1366
591
  // Update min/max caches.
1367
592
  if (this._size <= 0) {
1368
593
  this._setMinCache(undefined);
@@ -1378,346 +603,319 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1378
603
  this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost(n => n, this._root) : undefined);
1379
604
  }
1380
605
  }
1381
-
1382
- if (originalColor === 'BLACK') {
1383
- this._deleteFixup(replacementNode);
606
+ if (originalColor === 'BLACK') {
607
+ this._deleteFixup(replacementNode);
608
+ }
609
+ return true;
610
+ }
611
+
612
+ /**
613
+ * Transform entries into a like-kind red-black tree with possibly different key/value types.
614
+ * @remarks Time O(n) average, Space O(n)
615
+ * @template MK
616
+ * @template MV
617
+ * @template MR
618
+ * @param callback - Mapping function from (key, value, index, tree) to a new [key, value].
619
+ * @param [options] - See parameter type for details.
620
+ * @param [thisArg] - See parameter type for details.
621
+ * @returns A new RedBlackTree with mapped entries.
622
+ */
623
+ /**
624
+ * Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
625
+ * sorted bulk insert, which naturally produces a balanced RBT.
626
+ * @remarks Time O(N), Space O(N)
627
+ * @example
628
+ * // Rebalance tree
629
+ * const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
630
+ * rbt.perfectlyBalance();
631
+ * console.log(rbt.isAVLBalanced()); // true;
632
+ */
633
+ override perfectlyBalance(_iterationType?: IterationType): boolean {
634
+ // Extract sorted entries, clear, re-insert — RBT self-balances on insert
635
+ const entries: [K, V | undefined][] = [];
636
+ for (const [key, value] of this) entries.push([key, value]);
637
+ if (entries.length <= 1) return true;
638
+ this.clear();
639
+ this.setMany(
640
+ entries.map(([k]) => k),
641
+ entries.map(([, v]) => v),
642
+ true // isBalanceAdd
643
+ );
644
+ return true;
645
+ }
646
+
647
+ /**
648
+ * Transform to new tree
649
+ * @example
650
+ * // Transform to new tree
651
+ * const rbt = new RedBlackTree<number, number>([
652
+ * [1, 10],
653
+ * [2, 20]
654
+ * ]);
655
+ * const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
656
+ * console.log([...doubled.values()]); // [20, 40];
657
+ */
658
+ override map<MK = K, MV = V, MR = any>(
659
+ callback: EntryCallback<K, V | undefined, [MK, MV]>,
660
+ options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
661
+ thisArg?: unknown
662
+ ): RedBlackTree<MK, MV, MR> {
663
+ const out = this._createLike<MK, MV, MR>([], options);
664
+ let index = 0;
665
+ for (const [key, value] of this) {
666
+ out.set(callback.call(thisArg, value, key, index++, this));
667
+ }
668
+ return out;
669
+ }
670
+
671
+ /**
672
+ * (Internal) Find a node by key using a tight BST walk (no allocations).
673
+ *
674
+ * NOTE: This uses `header.parent` as the canonical root pointer.
675
+ * @remarks Time O(log n) average, Space O(1)
676
+ */
677
+ protected _findNodeByKey(key: K): RedBlackTreeNode<K, V> | undefined {
678
+ const NIL = this.NIL;
679
+ const cmp = this._compare.bind(this);
680
+ let cur = this._header.parent ?? NIL;
681
+ while (cur !== NIL) {
682
+ const c = cmp(key, cur.key);
683
+ if (c < 0) cur = cur.left ?? NIL;
684
+ else if (c > 0) cur = cur.right ?? NIL;
685
+ else return cur;
686
+ }
687
+ return undefined;
688
+ }
689
+
690
+ /**
691
+ * (Internal) In-order predecessor of a node in a BST.
692
+ * @remarks Time O(log n) average, Space O(1)
693
+ */
694
+ protected _predecessorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
695
+ const NIL = this.NIL;
696
+ if (node.left && node.left !== NIL) {
697
+ let cur = node.left;
698
+ while (cur.right && cur.right !== NIL) cur = cur.right;
699
+ return cur;
700
+ }
701
+ let cur: RedBlackTreeNode<K, V> | undefined = node;
702
+ let p = node.parent;
703
+ while (p && cur === p.left) {
704
+ cur = p;
705
+ p = p.parent;
706
+ }
707
+ return p;
708
+ }
709
+
710
+ /**
711
+ * (Internal) In-order successor of a node in a BST.
712
+ * @remarks Time O(log n) average, Space O(1)
713
+ */
714
+ protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
715
+ const NIL = this.NIL;
716
+ if (node.right && node.right !== NIL) {
717
+ let cur = node.right;
718
+ while (cur.left && cur.left !== NIL) cur = cur.left;
719
+ return cur;
720
+ }
721
+ let cur: RedBlackTreeNode<K, V> | undefined = node;
722
+ let p = node.parent;
723
+ while (p && cur === p.right) {
724
+ cur = p;
725
+ p = p.parent;
1384
726
  }
727
+ return p;
728
+ }
1385
729
 
1386
- return true;
730
+ /**
731
+ * (Internal) Attach a new node directly under a known parent/side (no search).
732
+ *
733
+ * This is a performance-oriented helper used by boundary fast paths and hinted insertion.
734
+ * It will:
735
+ * - wire parent/child pointers (using accessors, so parent pointers are updated)
736
+ * - initialize children to NIL
737
+ * - mark the new node RED, then run insert fix-up
738
+ *
739
+ * Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
740
+ * @remarks Time O(log n) average, Space O(1)
741
+ */
742
+ protected _attachNewNode(parent: RedBlackTreeNode<K, V>, side: 'left' | 'right', node: RedBlackTreeNode<K, V>): void {
743
+ const NIL = this.NIL;
744
+ node.parent = parent;
745
+ if (side === 'left') parent.left = node;
746
+ else parent.right = node;
747
+ node.left = NIL;
748
+ node.right = NIL;
749
+ node.color = 'RED';
750
+ this._updateCountAlongPath(node);
751
+ this._insertFixup(node);
752
+ if (this.isRealNode(this._root)) this._root.color = 'BLACK';
1387
753
  }
1388
754
 
1389
755
  /**
1390
- * Transform entries into a like-kind red-black tree with possibly different key/value types.
1391
- * @remarks Time O(n) average, Space O(n)
1392
- * @template MK
1393
- * @template MV
1394
- * @template MR
1395
- * @param callback - Mapping function from (key, value, index, tree) to a new [key, value].
1396
- * @param [options] - See parameter type for details.
1397
- * @param [thisArg] - See parameter type for details.
1398
- * @returns A new RedBlackTree with mapped entries.
756
+ * (Internal) a single source of truth for min/max is header._left/_right.
757
+ * Keep legacy _minNode/_maxNode mirrored for compatibility.
758
+ * @remarks Time O(1), Space O(1)
1399
759
  */
1400
760
  /**
1401
- * Red-Black trees are self-balancing `perfectlyBalance` rebuilds via
1402
- * sorted bulk insert, which naturally produces a balanced RBT.
1403
- * @remarks Time O(N), Space O(N)
1404
-
1405
-
1406
-
1407
-
1408
-
1409
-
1410
-
1411
-
1412
-
1413
-
1414
-
1415
-
1416
-
1417
-
1418
-
1419
-
1420
-
1421
-
1422
-
1423
-
1424
-
1425
-
1426
-
1427
-
1428
-
1429
-
1430
-
1431
-
1432
-
1433
-
1434
-
1435
-
1436
-
1437
-
1438
-
1439
-
1440
-
1441
-
1442
-
1443
-
1444
-
1445
-
1446
-
1447
-
1448
-
1449
-
1450
-
1451
-
1452
-
1453
-
1454
-
1455
-
1456
-
1457
-
1458
-
1459
-
1460
-
1461
-
1462
-
1463
-
1464
-
1465
-
1466
-
1467
-
1468
-
1469
-
1470
-
1471
-
1472
-
1473
-
1474
-
1475
-
1476
-
1477
-
1478
-
1479
-
1480
-
1481
-
1482
-
1483
-
1484
-
1485
-
1486
-
1487
-
1488
-
1489
-
1490
-
1491
-
1492
-
1493
-
1494
-
1495
-
1496
-
1497
-
1498
-
1499
-
1500
-
1501
-
1502
-
1503
-
1504
-
1505
-
1506
-
1507
-
1508
-
1509
-
1510
-
1511
-
1512
-
1513
-
1514
-
1515
-
1516
-
1517
-
1518
-
1519
-
1520
-
1521
-
1522
- * @example
1523
- * // Rebalance tree
1524
- * const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
1525
- * rbt.perfectlyBalance();
1526
- * console.log(rbt.isAVLBalanced()); // true;
761
+ * (Internal) Update min cache pointers (header._left is the canonical min pointer).
762
+ * @remarks Time O(1), Space O(1)
1527
763
  */
1528
- override perfectlyBalance(_iterationType?: IterationType): boolean {
1529
- // Extract sorted entries, clear, re-insert — RBT self-balances on insert
1530
- const entries: [K, V | undefined][] = [];
1531
- for (const [key, value] of this) entries.push([key, value]);
1532
- if (entries.length <= 1) return true;
1533
- this.clear();
1534
- this.setMany(
1535
- entries.map(([k]) => k),
1536
- entries.map(([, v]) => v),
1537
- true // isBalanceAdd
1538
- );
1539
- return true;
764
+ protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
765
+ this._minNode = node;
766
+ this._header._left = node ?? this.NIL;
1540
767
  }
1541
768
 
1542
- /**
1543
- * Transform to new tree
1544
-
1545
-
1546
-
1547
-
1548
-
1549
-
1550
-
1551
-
1552
-
1553
-
1554
-
1555
-
1556
-
1557
-
1558
-
1559
-
1560
-
1561
-
1562
-
1563
-
1564
-
1565
-
1566
-
1567
-
1568
-
1569
-
1570
-
1571
-
1572
-
1573
-
1574
-
1575
-
1576
-
1577
-
1578
-
1579
-
1580
-
1581
-
1582
-
1583
-
1584
-
1585
-
1586
-
1587
-
1588
-
1589
-
1590
-
1591
-
1592
-
1593
-
1594
-
1595
-
1596
-
1597
-
1598
-
1599
-
1600
-
1601
-
1602
-
1603
-
1604
-
1605
-
1606
-
1607
-
1608
-
1609
-
1610
-
1611
-
1612
-
1613
-
1614
-
1615
-
1616
-
1617
-
1618
-
1619
-
1620
-
1621
-
1622
-
1623
-
1624
-
1625
-
1626
-
1627
-
1628
-
1629
-
1630
-
1631
-
1632
-
1633
-
1634
-
1635
-
1636
-
1637
-
1638
-
1639
-
1640
-
1641
-
1642
-
1643
-
1644
-
1645
-
1646
-
1647
-
1648
-
1649
-
1650
-
1651
-
1652
-
1653
-
1654
-
1655
-
1656
-
1657
-
1658
-
1659
-
1660
-
1661
-
1662
-
1663
-
1664
-
1665
-
1666
-
1667
-
1668
-
1669
-
1670
-
1671
-
1672
-
1673
-
1674
-
1675
-
1676
-
1677
-
1678
-
1679
-
1680
-
1681
-
1682
-
1683
-
1684
-
1685
-
1686
-
1687
-
1688
-
1689
-
1690
-
1691
-
1692
-
1693
-
1694
-
1695
-
1696
-
1697
-
1698
-
1699
-
1700
-
1701
-
1702
-
1703
- * @example
1704
- * // Transform to new tree
1705
- * const rbt = new RedBlackTree<number, number>([[1, 10], [2, 20]]);
1706
- * const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
1707
- * console.log([...doubled.values()]); // [20, 40];
769
+ /**
770
+ * (Internal) Update max cache pointers (header._right is the canonical max pointer).
771
+ * @remarks Time O(1), Space O(1)
1708
772
  */
1709
- override map<MK = K, MV = V, MR = any>(
1710
- callback: EntryCallback<K, V | undefined, [MK, MV]>,
1711
- options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
1712
- thisArg?: unknown
1713
- ): RedBlackTree<MK, MV, MR> {
1714
- const out = this._createLike<MK, MV, MR>([], options);
773
+ protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
774
+ this._maxNode = node;
775
+ this._header._right = node ?? this.NIL;
776
+ }
1715
777
 
1716
- let index = 0;
1717
- for (const [key, value] of this) {
1718
- out.set(callback.call(thisArg, value, key, index++, this));
778
+ /**
779
+ * (Internal) Core set implementation returning the affected node.
780
+ *
781
+ * Hot path goals:
782
+ * - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
783
+ * - Use header min/max caches to fast-path boundary inserts.
784
+ * - Keep header._left/_right as canonical min/max pointers.
785
+ *
786
+ * Return value:
787
+ * - `{ node, created:false }` when an existing key is updated
788
+ * - `{ node, created:true }` when a new node is inserted
789
+ * - `undefined` only on unexpected internal failure.
790
+ * @remarks Time O(log n) average, Space O(1)
791
+ */
792
+ protected _setKVNode(key: K, nextValue?: V): { node: RedBlackTreeNode<K, V>; created: boolean } | undefined {
793
+ const NIL = this.NIL;
794
+ const comparator = this._comparator;
795
+ // Read via header to avoid undefined checks (header uses NIL when empty).
796
+ const header = this._header;
797
+ const minN = header._left ?? NIL;
798
+ if (minN !== NIL) {
799
+ const cMin = comparator(key, minN.key);
800
+ if (cMin === 0) {
801
+ minN.value = nextValue as V;
802
+ if (this._isMapMode) this._store.set(key, minN);
803
+ return { node: minN, created: false };
804
+ }
805
+ // Boundary attach: if key is smaller than current min and min has no left child.
806
+ // Inline NIL/null/undefined check to avoid isRealNode overhead on hot path.
807
+ const minL = minN.left;
808
+ if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
809
+ const newNode = this.createNode(key, nextValue);
810
+ this._attachNewNode(minN, 'left', newNode);
811
+ if (this._isMapMode) this._store.set(newNode.key, newNode);
812
+ this._size++;
813
+ this._setMinCache(newNode);
814
+ // If max is not initialized yet (tree had 0/1 nodes), mirror max too.
815
+ if (header._right === NIL) this._setMaxCache(newNode);
816
+ return { node: newNode, created: true };
817
+ }
818
+ // Only touch max when key is not less than min.
819
+ if (cMin > 0) {
820
+ const maxN = header._right ?? NIL;
821
+ // Boundary attach: if key is greater than current max and max has no right child.
822
+ const cMax = comparator(key, maxN.key);
823
+ if (cMax === 0) {
824
+ maxN.value = nextValue as V;
825
+ if (this._isMapMode) this._store.set(key, maxN);
826
+ return { node: maxN, created: false };
827
+ }
828
+ const maxR = maxN.right;
829
+ if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
830
+ const newNode = this.createNode(key, nextValue);
831
+ this._attachNewNode(maxN, 'right', newNode);
832
+ if (this._isMapMode) this._store.set(newNode.key, newNode);
833
+ this._size++;
834
+ this._setMaxCache(newNode);
835
+ if (header._left === NIL) this._setMinCache(newNode);
836
+ return { node: newNode, created: true };
837
+ }
838
+ }
1719
839
  }
1720
- return out;
840
+ // Normal path: single-pass search + insert/update (avoid double-walking the tree).
841
+ const cmp = comparator;
842
+ const isMapMode = this._isMapMode;
843
+ const store = this._store;
844
+ let current = this._header.parent ?? NIL;
845
+ let parent: RedBlackTreeNode<K, V> | undefined;
846
+ let lastCompared = 0;
847
+ while (current !== NIL) {
848
+ parent = current;
849
+ lastCompared = cmp(key, current.key);
850
+ if (lastCompared < 0) current = current.left ?? NIL;
851
+ else if (lastCompared > 0) current = current.right ?? NIL;
852
+ else {
853
+ // Update existing.
854
+ current.value = nextValue as V;
855
+ if (isMapMode) store.set(key, current);
856
+ return { node: current, created: false };
857
+ }
858
+ }
859
+ // Insert new.
860
+ const newNode = this.createNode(key, nextValue);
861
+ // createNode always returns a real node in RedBlackTree.
862
+ newNode.parent = parent;
863
+ if (!parent) {
864
+ this._setRoot(newNode);
865
+ } else if (lastCompared < 0) {
866
+ parent.left = newNode;
867
+ } else {
868
+ parent.right = newNode;
869
+ }
870
+ newNode.left = NIL;
871
+ newNode.right = NIL;
872
+ newNode.color = 'RED';
873
+ this._updateCountAlongPath(newNode);
874
+ this._insertFixup(newNode);
875
+ if (this.isRealNode(this._root)) this._root.color = 'BLACK';
876
+ else return undefined;
877
+ if (isMapMode) store.set(newNode.key, newNode);
878
+ this._size++;
879
+ // Maintain min/max caches on insertion (header.left/right are canonical).
880
+ const hMin = this._header._left ?? NIL;
881
+ const hMax = this._header._right ?? NIL;
882
+ // Fast-path: empty tree or attaching directly to an extreme.
883
+ if (hMin === NIL || hMax === NIL) {
884
+ this._setMinCache(newNode);
885
+ this._setMaxCache(newNode);
886
+ } else if (parent === hMax && lastCompared > 0) {
887
+ /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ /* istanbul ignore next */
888
+ this._setMaxCache(newNode);
889
+ } else if (parent === hMin && lastCompared < 0) {
890
+ /* istanbul ignore next */ /* istanbul ignore next */
891
+ this._setMinCache(newNode);
892
+ } else {
893
+ if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
894
+ if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
895
+ }
896
+ return { node: newNode, created: true };
897
+ }
898
+
899
+ /**
900
+ * (Internal) Boolean wrapper around `_setKVNode`.
901
+ *
902
+ * Includes a map-mode update fast-path:
903
+ * - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
904
+ * require any tree search/rotation (tree shape depends only on key).
905
+ * - This path is intentionally limited to `nextValue !== undefined` to preserve existing
906
+ * semantics for `undefined` values.
907
+ * @remarks Time O(log n) average, Space O(1)
908
+ */
909
+ protected _setKV(key: K, nextValue?: V): boolean {
910
+ if (this._isMapMode) {
911
+ const store = this._store;
912
+ const node = store.get(key);
913
+ if (node) {
914
+ node.value = nextValue as V;
915
+ return true;
916
+ }
917
+ }
918
+ return this._setKVNode(key, nextValue) !== undefined;
1721
919
  }
1722
920
 
1723
921
  /**
@@ -1773,7 +971,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1773
971
  newNode: RedBlackTreeNode<K, V>
1774
972
  ): RedBlackTreeNode<K, V> {
1775
973
  newNode.color = oldNode.color;
1776
-
1777
974
  return super._replaceNode(oldNode, newNode);
1778
975
  }
1779
976
 
@@ -1786,11 +983,9 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1786
983
  protected _insert(node: RedBlackTreeNode<K, V>): CRUD {
1787
984
  const NIL = this.NIL;
1788
985
  const cmp = this._compare.bind(this);
1789
-
1790
986
  let current = this._header.parent ?? NIL;
1791
987
  let parent: RedBlackTreeNode<K, V> | undefined;
1792
988
  let lastCompared = 0;
1793
-
1794
989
  while (current !== NIL) {
1795
990
  parent = current;
1796
991
  lastCompared = cmp(node.key, current.key);
@@ -1803,9 +998,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1803
998
  return 'UPDATED';
1804
999
  }
1805
1000
  }
1806
-
1807
1001
  node.parent = parent;
1808
-
1809
1002
  if (!parent) {
1810
1003
  this._setRoot(node);
1811
1004
  } else if (lastCompared < 0) {
@@ -1813,14 +1006,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1813
1006
  } else {
1814
1007
  parent.right = node;
1815
1008
  }
1816
-
1817
1009
  node.left = NIL;
1818
1010
  node.right = NIL;
1819
1011
  node.color = 'RED';
1820
-
1821
1012
  // Update counts along insertion path before fixup
1822
1013
  this._updateCountAlongPath(node);
1823
-
1824
1014
  this._insertFixup(node);
1825
1015
  return 'CREATED';
1826
1016
  }
@@ -1840,7 +1030,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1840
1030
  } else {
1841
1031
  u.parent.right = v;
1842
1032
  }
1843
-
1844
1033
  if (v) {
1845
1034
  v.parent = u.parent;
1846
1035
  }
@@ -1855,14 +1044,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1855
1044
  protected _insertFixup(z: RedBlackTreeNode<K, V> | undefined): void {
1856
1045
  const leftRotate = this._leftRotate.bind(this);
1857
1046
  const rightRotate = this._rightRotate.bind(this);
1858
-
1859
1047
  while (z) {
1860
1048
  const p = z.parent;
1861
1049
  if (!p || p.color !== 'RED') break;
1862
-
1863
1050
  const gp = p.parent;
1864
1051
  if (!gp) break;
1865
-
1866
1052
  if (p === gp.left) {
1867
1053
  const y = gp.right;
1868
1054
  if (y?.color === 'RED') {
@@ -1872,12 +1058,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1872
1058
  z = gp;
1873
1059
  continue;
1874
1060
  }
1875
-
1876
1061
  if (z === p.right) {
1877
1062
  z = p;
1878
1063
  leftRotate(z);
1879
1064
  }
1880
-
1881
1065
  const p2 = z?.parent;
1882
1066
  const gp2 = p2?.parent;
1883
1067
  if (p2 && gp2) {
@@ -1894,12 +1078,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1894
1078
  z = gp;
1895
1079
  continue;
1896
1080
  }
1897
-
1898
1081
  if (z === p.left) {
1899
1082
  z = p;
1900
1083
  rightRotate(z);
1901
1084
  }
1902
-
1903
1085
  const p2 = z?.parent;
1904
1086
  const gp2 = p2?.parent;
1905
1087
  if (p2 && gp2) {
@@ -1908,10 +1090,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1908
1090
  leftRotate(gp2);
1909
1091
  }
1910
1092
  }
1911
-
1912
1093
  break;
1913
1094
  }
1914
-
1915
1095
  if (this.isRealNode(this._root)) this._root.color = 'BLACK';
1916
1096
  }
1917
1097
 
@@ -1926,17 +1106,13 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1926
1106
  // `node` is the child that replaced the deleted node (may be NIL sentinel).
1927
1107
  // If RED → just recolor BLACK (trivial fix). If BLACK → double-black repair.
1928
1108
  if (!node) return;
1929
-
1930
1109
  const NIL = this.NIL;
1931
1110
  let current: RedBlackTreeNode<K, V> = node;
1932
-
1933
1111
  while (current !== this.root && current.color === 'BLACK') {
1934
1112
  const parent: RedBlackTreeNode<K, V> | undefined = current.parent;
1935
1113
  if (!parent) break;
1936
-
1937
1114
  const nodeIsLeft = current === parent.left;
1938
1115
  let sibling = nodeIsLeft ? parent.right : parent.left;
1939
-
1940
1116
  // Case 1: sibling is RED → rotate to get a BLACK sibling
1941
1117
  if (sibling && sibling.color === 'RED') {
1942
1118
  sibling.color = 'BLACK';
@@ -1949,12 +1125,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1949
1125
  sibling = parent.left;
1950
1126
  }
1951
1127
  }
1952
-
1953
1128
  const sibLeft = sibling?.left;
1954
1129
  const sibRight = sibling?.right;
1955
1130
  const sibLeftBlack = !sibLeft || sibLeft === NIL || sibLeft.color === 'BLACK';
1956
1131
  const sibRightBlack = !sibRight || sibRight === NIL || sibRight.color === 'BLACK';
1957
-
1958
1132
  if (sibLeftBlack && sibRightBlack) {
1959
1133
  // Case 2: sibling's children are both BLACK → recolor sibling RED, move up
1960
1134
  if (sibling) sibling.color = 'RED';
@@ -1990,7 +1164,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1990
1164
  current = this.root!;
1991
1165
  }
1992
1166
  }
1993
-
1994
1167
  current.color = 'BLACK';
1995
1168
  }
1996
1169
 
@@ -2004,16 +1177,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
2004
1177
  if (!x || !x.right) {
2005
1178
  return;
2006
1179
  }
2007
-
2008
1180
  const y = x.right;
2009
1181
  x.right = y.left;
2010
-
2011
1182
  if (y.left && y.left !== this.NIL) {
2012
1183
  y.left.parent = x;
2013
1184
  }
2014
-
2015
1185
  y.parent = x.parent;
2016
-
2017
1186
  if (!x.parent) {
2018
1187
  this._setRoot(y);
2019
1188
  } else if (x === x.parent.left) {
@@ -2021,10 +1190,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
2021
1190
  } else {
2022
1191
  x.parent.right = y;
2023
1192
  }
2024
-
2025
1193
  y.left = x;
2026
1194
  x.parent = y;
2027
-
2028
1195
  // Update counts: x first (now child), then y (now parent)
2029
1196
  this._updateCount(x);
2030
1197
  this._updateCount(y);
@@ -2040,16 +1207,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
2040
1207
  if (!y || !y.left) {
2041
1208
  return;
2042
1209
  }
2043
-
2044
1210
  const x = y.left;
2045
1211
  y.left = x.right;
2046
-
2047
1212
  if (x.right && x.right !== this.NIL) {
2048
1213
  x.right.parent = y;
2049
1214
  }
2050
-
2051
1215
  x.parent = y.parent;
2052
-
2053
1216
  if (!y.parent) {
2054
1217
  this._setRoot(x);
2055
1218
  } else if (y === y.parent.left) {
@@ -2057,10 +1220,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
2057
1220
  } else {
2058
1221
  y.parent.right = x;
2059
1222
  }
2060
-
2061
1223
  x.right = y;
2062
1224
  y.parent = x;
2063
-
2064
1225
  // Update counts: y first (now child), then x (now parent)
2065
1226
  this._updateCount(y);
2066
1227
  this._updateCount(x);