max-priority-queue-typed 2.4.4 → 2.5.0

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 (85) hide show
  1. package/README.md +63 -0
  2. package/dist/cjs/index.cjs +403 -98
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs-legacy/index.cjs +402 -97
  5. package/dist/cjs-legacy/index.cjs.map +1 -1
  6. package/dist/esm/index.mjs +403 -99
  7. package/dist/esm/index.mjs.map +1 -1
  8. package/dist/esm-legacy/index.mjs +402 -98
  9. package/dist/esm-legacy/index.mjs.map +1 -1
  10. package/dist/types/common/error.d.ts +23 -0
  11. package/dist/types/common/index.d.ts +1 -0
  12. package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
  13. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
  14. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
  15. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +439 -78
  16. package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
  17. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +217 -31
  18. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
  19. package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
  20. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
  21. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
  22. package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
  23. package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
  24. package/dist/types/data-structures/graph/directed-graph.d.ts +220 -47
  25. package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
  26. package/dist/types/data-structures/graph/undirected-graph.d.ts +218 -59
  27. package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
  28. package/dist/types/data-structures/heap/heap.d.ts +287 -99
  29. package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
  30. package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
  31. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +286 -44
  32. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
  33. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
  34. package/dist/types/data-structures/matrix/matrix.d.ts +331 -0
  35. package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
  36. package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
  37. package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
  38. package/dist/types/data-structures/queue/deque.d.ts +313 -66
  39. package/dist/types/data-structures/queue/queue.d.ts +211 -42
  40. package/dist/types/data-structures/stack/stack.d.ts +174 -32
  41. package/dist/types/data-structures/trie/trie.d.ts +213 -43
  42. package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
  43. package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
  44. package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
  45. package/dist/umd/max-priority-queue-typed.js +400 -95
  46. package/dist/umd/max-priority-queue-typed.js.map +1 -1
  47. package/dist/umd/max-priority-queue-typed.min.js +1 -1
  48. package/dist/umd/max-priority-queue-typed.min.js.map +1 -1
  49. package/package.json +2 -2
  50. package/src/common/error.ts +60 -0
  51. package/src/common/index.ts +2 -0
  52. package/src/data-structures/base/iterable-element-base.ts +2 -2
  53. package/src/data-structures/binary-tree/avl-tree.ts +134 -51
  54. package/src/data-structures/binary-tree/binary-indexed-tree.ts +303 -247
  55. package/src/data-structures/binary-tree/binary-tree.ts +542 -121
  56. package/src/data-structures/binary-tree/bst.ts +346 -37
  57. package/src/data-structures/binary-tree/red-black-tree.ts +309 -96
  58. package/src/data-structures/binary-tree/segment-tree.ts +372 -248
  59. package/src/data-structures/binary-tree/tree-map.ts +1292 -13
  60. package/src/data-structures/binary-tree/tree-multi-map.ts +1098 -215
  61. package/src/data-structures/binary-tree/tree-multi-set.ts +863 -69
  62. package/src/data-structures/binary-tree/tree-set.ts +1143 -15
  63. package/src/data-structures/graph/abstract-graph.ts +106 -1
  64. package/src/data-structures/graph/directed-graph.ts +223 -47
  65. package/src/data-structures/graph/map-graph.ts +59 -1
  66. package/src/data-structures/graph/undirected-graph.ts +299 -59
  67. package/src/data-structures/hash/hash-map.ts +243 -79
  68. package/src/data-structures/heap/heap.ts +291 -102
  69. package/src/data-structures/heap/max-heap.ts +48 -3
  70. package/src/data-structures/heap/min-heap.ts +59 -0
  71. package/src/data-structures/linked-list/doubly-linked-list.ts +286 -44
  72. package/src/data-structures/linked-list/singly-linked-list.ts +278 -65
  73. package/src/data-structures/linked-list/skip-linked-list.ts +689 -90
  74. package/src/data-structures/matrix/matrix.ts +425 -22
  75. package/src/data-structures/priority-queue/max-priority-queue.ts +59 -3
  76. package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
  77. package/src/data-structures/priority-queue/priority-queue.ts +60 -0
  78. package/src/data-structures/queue/deque.ts +343 -68
  79. package/src/data-structures/queue/queue.ts +211 -42
  80. package/src/data-structures/stack/stack.ts +174 -32
  81. package/src/data-structures/trie/trie.ts +215 -44
  82. package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
  83. package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
  84. package/src/types/data-structures/queue/deque.ts +7 -0
  85. package/src/utils/utils.ts +4 -2
@@ -13,6 +13,7 @@ import type {
13
13
  FamilyPosition, NodePredicate,
14
14
  OptNode,
15
15
  RBTNColor,
16
+ IterationType,
16
17
  RedBlackTreeOptions
17
18
  } from '../../types';
18
19
  import { BST } from './bst';
@@ -97,16 +98,12 @@ export class RedBlackTreeNode<K = any, V = any> {
97
98
  *
98
99
  * @returns The height.
99
100
  */
101
+ /* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
100
102
  get height(): number {
101
103
  return this._height;
102
104
  }
103
105
 
104
- /**
105
- * Sets the height of the node.
106
- * @remarks Time O(1), Space O(1)
107
- *
108
- * @param value - The new height.
109
- */
106
+ /* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
110
107
  set height(value: number) {
111
108
  this._height = value;
112
109
  }
@@ -141,20 +138,11 @@ export class RedBlackTreeNode<K = any, V = any> {
141
138
  *
142
139
  * @returns The subtree node count.
143
140
  */
141
+ /* istanbul ignore next -- internal field, exercised indirectly via tree operations */
144
142
  get count(): number {
145
143
  return this._count;
146
144
  }
147
145
 
148
- /**
149
- * Sets the count of nodes in the subtree.
150
- * @remarks Time O(1), Space O(1)
151
- *
152
- * @param value - The new count.
153
- */
154
- set count(value: number) {
155
- this._count = value;
156
- }
157
-
158
146
  /**
159
147
  * Gets the position of the node relative to its parent.
160
148
  * @remarks Time O(1), Space O(1)
@@ -172,6 +160,7 @@ export class RedBlackTreeNode<K = any, V = any> {
172
160
  return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
173
161
  }
174
162
 
163
+ /* istanbul ignore next -- defensive: unreachable if tree structure is correct */
175
164
  return 'MAL_NODE';
176
165
  }
177
166
  }
@@ -186,23 +175,6 @@ export class RedBlackTreeNode<K = any, V = any> {
186
175
  * 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
187
176
  *
188
177
  * @example
189
- * // basic Red-Black Tree with simple number keys
190
- * // Create a simple Red-Black Tree with numeric keys
191
- * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
192
- *
193
- * tree.print();
194
- * // _2___
195
- * // / \
196
- * // 1 _8_
197
- * // / \
198
- * // 5 9
199
- *
200
- * // Verify the tree maintains sorted order
201
- * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
202
- *
203
- * // Check size
204
- * console.log(tree.size); // 5;
205
- * @example
206
178
  * // Red-Black Tree with key-value pairs for lookups
207
179
  * interface Employee {
208
180
  * id: number;
@@ -368,6 +340,46 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
368
340
  /**
369
341
  * Remove all nodes and clear internal caches.
370
342
  * @remarks Time O(n) average, Space O(1)
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
+ * @example
379
+ * // Remove all entries
380
+ * const rbt = new RedBlackTree<number>([1, 2, 3]);
381
+ * rbt.clear();
382
+ * console.log(rbt.isEmpty()); // true;
371
383
  */
372
384
  override clear() {
373
385
  super.clear();
@@ -603,10 +615,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
603
615
  if (hMin === NIL || hMax === NIL) {
604
616
  this._setMinCache(newNode);
605
617
  this._setMaxCache(newNode);
606
- } else if (parent === hMax && lastCompared > 0) {
607
- this._setMaxCache(newNode);
608
- } else if (parent === hMin && lastCompared < 0) {
609
- this._setMinCache(newNode);
618
+ } else /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ if (parent === hMax && lastCompared > 0) {
619
+ /* istanbul ignore next */ this._setMaxCache(newNode);
620
+ } else /* istanbul ignore next */ if (parent === hMin && lastCompared < 0) {
621
+ /* istanbul ignore next */ this._setMinCache(newNode);
610
622
  } else {
611
623
  if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
612
624
  if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
@@ -700,6 +712,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
700
712
  return newNode;
701
713
  }
702
714
 
715
+ /* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
703
716
  return this._setKVNode(key, value)?.node;
704
717
  }
705
718
 
@@ -740,6 +753,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
740
753
  return newNode;
741
754
  }
742
755
 
756
+ /* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
743
757
  return this._setKVNode(key, value)?.node;
744
758
  }
745
759
 
@@ -759,6 +773,59 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
759
773
  * - updates via a single-pass search (no double walk)
760
774
  *
761
775
  * @remarks Time O(log n) average, Space O(1)
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+
812
+ * @example
813
+ * // basic Red-Black Tree with simple number keys
814
+ * // Create a simple Red-Black Tree with numeric keys
815
+ * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
816
+ *
817
+ * tree.print();
818
+ * // _2___
819
+ * // / \
820
+ * // 1 _8_
821
+ * // / \
822
+ * // 5 9
823
+ *
824
+ * // Verify the tree maintains sorted order
825
+ * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
826
+ *
827
+ * // Check size
828
+ * console.log(tree.size); // 5;
762
829
  */
763
830
  override set(
764
831
  keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
@@ -811,6 +878,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
811
878
  }
812
879
  return true;
813
880
  }
881
+ /* istanbul ignore next -- defensive: _insert only returns CREATED|UPDATED */
814
882
  return false;
815
883
  }
816
884
 
@@ -819,6 +887,50 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
819
887
  * @remarks Time O(log n) average, Space O(1)
820
888
  * @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
821
889
  * @returns Array with deletion metadata (removed node, rebalancing hint if any).
890
+
891
+
892
+
893
+
894
+
895
+
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
+ * @example
929
+ * // Remove and rebalance
930
+ * const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
931
+ * rbt.delete(5);
932
+ * console.log(rbt.has(5)); // false;
933
+ * console.log(rbt.size); // 4;
822
934
  */
823
935
  override delete(
824
936
  keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
@@ -841,39 +953,38 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
841
953
  const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
842
954
 
843
955
  let originalColor = nodeToDelete.color;
844
- let replacementNode: RedBlackTreeNode<K, V> | undefined;
956
+ const NIL = this.NIL;
957
+ let replacementNode: RedBlackTreeNode<K, V> = NIL;
845
958
 
846
959
  if (!this.isRealNode(nodeToDelete.left)) {
847
- if (nodeToDelete.right !== null) {
848
- replacementNode = nodeToDelete.right;
849
- this._transplant(nodeToDelete, nodeToDelete.right);
850
- }
960
+ // No real left child → replace with right (may be NIL)
961
+ replacementNode = nodeToDelete.right ?? NIL;
962
+ this._transplant(nodeToDelete, replacementNode);
851
963
  } else if (!this.isRealNode(nodeToDelete.right)) {
964
+ // No real right child → replace with left
852
965
  replacementNode = nodeToDelete.left;
853
- this._transplant(nodeToDelete, nodeToDelete.left);
966
+ this._transplant(nodeToDelete, replacementNode);
854
967
  } else {
968
+ // Two children → find in-order successor
855
969
  const successor = this.getLeftMost(node => node, nodeToDelete.right);
856
970
  if (successor) {
857
971
  originalColor = successor.color;
858
- if (successor.right !== null) replacementNode = successor.right;
972
+ replacementNode = successor.right ?? NIL;
859
973
 
860
974
  if (successor.parent === nodeToDelete) {
861
- if (this.isRealNode(replacementNode)) {
862
- replacementNode.parent = successor;
863
- }
975
+ // Even if replacementNode is NIL, set its parent for fixup
976
+ replacementNode.parent = successor;
864
977
  } else {
865
- if (successor.right !== null) {
866
- this._transplant(successor, successor.right);
867
- successor.right = nodeToDelete.right;
868
- }
869
- if (this.isRealNode(successor.right)) {
978
+ this._transplant(successor, replacementNode);
979
+ successor.right = nodeToDelete.right;
980
+ if (successor.right) {
870
981
  successor.right.parent = successor;
871
982
  }
872
983
  }
873
984
 
874
985
  this._transplant(nodeToDelete, successor);
875
986
  successor.left = nodeToDelete.left;
876
- if (this.isRealNode(successor.left)) {
987
+ if (successor.left) {
877
988
  successor.left.parent = successor;
878
989
  }
879
990
  successor.color = nodeToDelete.color;
@@ -918,6 +1029,98 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
918
1029
  * @param [thisArg] - See parameter type for details.
919
1030
  * @returns A new RedBlackTree with mapped entries.
920
1031
  */
1032
+ /**
1033
+ * Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
1034
+ * sorted bulk insert, which naturally produces a balanced RBT.
1035
+ * @remarks Time O(N), Space O(N)
1036
+
1037
+
1038
+
1039
+
1040
+
1041
+
1042
+
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+
1056
+
1057
+
1058
+
1059
+
1060
+
1061
+ * @example
1062
+ * // Rebalance tree
1063
+ * const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
1064
+ * rbt.perfectlyBalance();
1065
+ * console.log(rbt.isAVLBalanced()); // true;
1066
+ */
1067
+ override perfectlyBalance(_iterationType?: IterationType): boolean {
1068
+ // Extract sorted entries, clear, re-insert — RBT self-balances on insert
1069
+ const entries: [K, V | undefined][] = [];
1070
+ for (const [key, value] of this) entries.push([key, value]);
1071
+ if (entries.length <= 1) return true;
1072
+ this.clear();
1073
+ this.setMany(
1074
+ entries.map(([k]) => k),
1075
+ entries.map(([, v]) => v),
1076
+ true // isBalanceAdd
1077
+ );
1078
+ return true;
1079
+ }
1080
+
1081
+ /**
1082
+ * Transform to new tree
1083
+
1084
+
1085
+
1086
+
1087
+
1088
+
1089
+
1090
+
1091
+
1092
+
1093
+
1094
+
1095
+
1096
+
1097
+
1098
+
1099
+
1100
+
1101
+
1102
+
1103
+
1104
+
1105
+
1106
+
1107
+
1108
+
1109
+
1110
+
1111
+
1112
+
1113
+
1114
+
1115
+
1116
+
1117
+
1118
+ * @example
1119
+ * // Transform to new tree
1120
+ * const rbt = new RedBlackTree<number, number>([[1, 10], [2, 20]]);
1121
+ * const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
1122
+ * console.log([...doubled.values()]); // [20, 40];
1123
+ */
921
1124
  override map<MK = K, MV = V, MR = any>(
922
1125
  callback: EntryCallback<K, V | undefined, [MK, MV]>,
923
1126
  options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
@@ -1131,66 +1334,76 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1131
1334
  * @returns void
1132
1335
  */
1133
1336
  protected _deleteFixup(node: RedBlackTreeNode<K, V> | undefined): void {
1134
- if (!node || node === this.root || node.color === 'BLACK') {
1135
- if (node) {
1136
- node.color = 'BLACK';
1137
- }
1138
- return;
1139
- }
1337
+ // Standard CLRS RB-DELETE-FIXUP: restore black-height invariant.
1338
+ // `node` is the child that replaced the deleted node (may be NIL sentinel).
1339
+ // If RED → just recolor BLACK (trivial fix). If BLACK → double-black repair.
1340
+ if (!node) return;
1140
1341
 
1141
- while (node && node !== this.root && node.color === 'BLACK') {
1142
- const parent: RedBlackTreeNode<K, V> | undefined = node.parent;
1342
+ const NIL = this.NIL;
1343
+ let current: RedBlackTreeNode<K, V> = node;
1143
1344
 
1144
- if (!parent) {
1145
- break;
1146
- }
1345
+ while (current !== this.root && current.color === 'BLACK') {
1346
+ const parent: RedBlackTreeNode<K, V> | undefined = current.parent;
1347
+ if (!parent) break;
1147
1348
 
1148
- if (node === parent.left) {
1149
- let sibling = parent.right;
1349
+ const nodeIsLeft = current === parent.left;
1350
+ let sibling = nodeIsLeft ? parent.right : parent.left;
1150
1351
 
1151
- if (sibling?.color === 'RED') {
1152
- sibling.color = 'BLACK';
1153
- parent.color = 'RED';
1352
+ // Case 1: sibling is RED → rotate to get a BLACK sibling
1353
+ if (sibling && sibling.color === 'RED') {
1354
+ sibling.color = 'BLACK';
1355
+ parent.color = 'RED';
1356
+ if (nodeIsLeft) {
1154
1357
  this._leftRotate(parent);
1155
1358
  sibling = parent.right;
1156
- }
1157
-
1158
- if ((sibling?.left?.color ?? 'BLACK') === 'BLACK') {
1159
- if (sibling) sibling.color = 'RED';
1160
- node = parent;
1161
1359
  } else {
1162
- if (sibling?.left) sibling.left.color = 'BLACK';
1163
- if (sibling) sibling.color = parent.color;
1164
- parent.color = 'BLACK';
1165
1360
  this._rightRotate(parent);
1166
- node = this.root;
1361
+ sibling = parent.left;
1167
1362
  }
1168
- } else {
1169
- let sibling = parent.left;
1363
+ }
1170
1364
 
1171
- if (sibling?.color === 'RED') {
1172
- sibling.color = 'BLACK';
1173
- if (parent) parent.color = 'RED';
1174
- this._rightRotate(parent);
1175
- if (parent) sibling = parent.left;
1176
- }
1365
+ const sibLeft = sibling?.left;
1366
+ const sibRight = sibling?.right;
1367
+ const sibLeftBlack = !sibLeft || sibLeft === NIL || sibLeft.color === 'BLACK';
1368
+ const sibRightBlack = !sibRight || sibRight === NIL || sibRight.color === 'BLACK';
1177
1369
 
1178
- if ((sibling?.right?.color ?? 'BLACK') === 'BLACK') {
1179
- if (sibling) sibling.color = 'RED';
1180
- node = parent;
1181
- } else {
1182
- if (sibling?.right) sibling.right.color = 'BLACK';
1370
+ if (sibLeftBlack && sibRightBlack) {
1371
+ // Case 2: sibling's children are both BLACK → recolor sibling RED, move up
1372
+ if (sibling) sibling.color = 'RED';
1373
+ current = parent;
1374
+ } else {
1375
+ if (nodeIsLeft) {
1376
+ // Case 3: sibling's right child is BLACK → rotate sibling right first
1377
+ if (sibRightBlack) {
1378
+ if (sibLeft) sibLeft.color = 'BLACK';
1379
+ if (sibling) sibling.color = 'RED';
1380
+ if (sibling) this._rightRotate(sibling);
1381
+ sibling = parent.right;
1382
+ }
1383
+ // Case 4: sibling's right child is RED → final rotation
1183
1384
  if (sibling) sibling.color = parent.color;
1184
- if (parent) parent.color = 'BLACK';
1385
+ parent.color = 'BLACK';
1386
+ if (sibling?.right) sibling.right.color = 'BLACK';
1185
1387
  this._leftRotate(parent);
1186
- node = this.root;
1388
+ } else {
1389
+ // Case 3 (mirror): sibling's left child is BLACK → rotate sibling left first
1390
+ if (sibLeftBlack) {
1391
+ if (sibRight) sibRight.color = 'BLACK';
1392
+ if (sibling) sibling.color = 'RED';
1393
+ if (sibling) this._leftRotate(sibling);
1394
+ sibling = parent.left;
1395
+ }
1396
+ // Case 4 (mirror): sibling's left child is RED → final rotation
1397
+ if (sibling) sibling.color = parent.color;
1398
+ parent.color = 'BLACK';
1399
+ if (sibling?.left) sibling.left.color = 'BLACK';
1400
+ this._rightRotate(parent);
1187
1401
  }
1402
+ current = this.root!;
1188
1403
  }
1189
1404
  }
1190
1405
 
1191
- if (node) {
1192
- node.color = 'BLACK';
1193
- }
1406
+ current.color = 'BLACK';
1194
1407
  }
1195
1408
 
1196
1409
  /**