data-structure-typed 2.2.8 → 2.3.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 (137) hide show
  1. package/.github/workflows/ci.yml +9 -0
  2. package/CHANGELOG.md +1 -1
  3. package/README.md +1 -1
  4. package/README_CN.md +1 -1
  5. package/dist/cjs/index.cjs +584 -76
  6. package/dist/cjs/index.cjs.map +1 -1
  7. package/dist/cjs-legacy/index.cjs +588 -79
  8. package/dist/cjs-legacy/index.cjs.map +1 -1
  9. package/dist/esm/index.mjs +584 -76
  10. package/dist/esm/index.mjs.map +1 -1
  11. package/dist/esm-legacy/index.mjs +588 -79
  12. package/dist/esm-legacy/index.mjs.map +1 -1
  13. package/dist/types/data-structures/base/linear-base.d.ts +6 -6
  14. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +3 -4
  15. package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
  16. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
  17. package/dist/types/data-structures/binary-tree/tree-counter.d.ts +3 -3
  18. package/dist/types/interfaces/binary-tree.d.ts +1 -1
  19. package/dist/umd/data-structure-typed.js +588 -79
  20. package/dist/umd/data-structure-typed.js.map +1 -1
  21. package/dist/umd/data-structure-typed.min.js +3 -3
  22. package/dist/umd/data-structure-typed.min.js.map +1 -1
  23. package/package.json +4 -3
  24. package/src/data-structures/base/linear-base.ts +2 -12
  25. package/src/data-structures/binary-tree/binary-tree.ts +5 -6
  26. package/src/data-structures/binary-tree/bst.ts +79 -4
  27. package/src/data-structures/binary-tree/red-black-tree.ts +583 -73
  28. package/src/data-structures/binary-tree/tree-counter.ts +21 -9
  29. package/src/data-structures/queue/deque.ts +10 -0
  30. package/src/interfaces/binary-tree.ts +1 -1
  31. package/test/unit/data-structures/base/iterable-element-base.coverage.test.ts +106 -0
  32. package/test/unit/data-structures/base/iterable-element-base.more-branches.coverage.test.ts +61 -0
  33. package/test/unit/data-structures/base/linear-base.array.coverage.test.ts +168 -0
  34. package/test/unit/data-structures/base/linear-base.concat-else.coverage.test.ts +82 -0
  35. package/test/unit/data-structures/base/linear-base.coverage.test.ts +72 -0
  36. package/test/unit/data-structures/base/linear-base.more-branches.coverage.test.ts +417 -0
  37. package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches-3.coverage.test.ts +146 -0
  38. package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches.coverage.test.ts +93 -0
  39. package/test/unit/data-structures/binary-tree/avl-tree-multi-map.coverage.test.ts +108 -0
  40. package/test/unit/data-structures/binary-tree/avl-tree-multi-map.more-branches-2.coverage.test.ts +85 -0
  41. package/test/unit/data-structures/binary-tree/avl-tree-node.familyPosition-root-left.coverage.test.ts +17 -0
  42. package/test/unit/data-structures/binary-tree/avl-tree.more-branches-2.coverage.test.ts +99 -0
  43. package/test/unit/data-structures/binary-tree/binary-indexed-tree.more-branches.coverage.test.ts +18 -0
  44. package/test/unit/data-structures/binary-tree/binary-tree.more-branches.coverage.test.ts +56 -0
  45. package/test/unit/data-structures/binary-tree/binary-tree.remaining-branches.coverage.test.ts +229 -0
  46. package/test/unit/data-structures/binary-tree/bst.bound-by-predicate.coverage.test.ts +33 -0
  47. package/test/unit/data-structures/binary-tree/bst.coverage.test.ts +94 -0
  48. package/test/unit/data-structures/binary-tree/bst.deletebykey.coverage.test.ts +70 -0
  49. package/test/unit/data-structures/binary-tree/bst.deletewhere.coverage.test.ts +37 -0
  50. package/test/unit/data-structures/binary-tree/bst.floor-lower-predicate.coverage.test.ts +29 -0
  51. package/test/unit/data-structures/binary-tree/bst.floor-setmany.coverage.test.ts +72 -0
  52. package/test/unit/data-structures/binary-tree/bst.getnode.range-ensure.coverage.test.ts +22 -0
  53. package/test/unit/data-structures/binary-tree/bst.misc-branches.coverage.test.ts +100 -0
  54. package/test/unit/data-structures/binary-tree/bst.more-branches-2.coverage.test.ts +133 -0
  55. package/test/unit/data-structures/binary-tree/bst.more-branches-3.coverage.test.ts +45 -0
  56. package/test/unit/data-structures/binary-tree/bst.more-branches-4.coverage.test.ts +36 -0
  57. package/test/unit/data-structures/binary-tree/bst.more-branches-5.coverage.test.ts +40 -0
  58. package/test/unit/data-structures/binary-tree/bst.more.coverage.test.ts +39 -0
  59. package/test/unit/data-structures/binary-tree/bst.node-family.coverage.test.ts +29 -0
  60. package/test/unit/data-structures/binary-tree/bst.range-pruning.coverage.test.ts +43 -0
  61. package/test/unit/data-structures/binary-tree/bst.search-fastpath.coverage.test.ts +30 -0
  62. package/test/unit/data-structures/binary-tree/bst.test.ts +25 -55
  63. package/test/unit/data-structures/binary-tree/red-black-tree.boundary-corruption-repair.coverage.test.ts +66 -0
  64. package/test/unit/data-structures/binary-tree/red-black-tree.boundary-max-update.coverage.test.ts +18 -0
  65. package/test/unit/data-structures/binary-tree/red-black-tree.boundary-null.coverage.test.ts +53 -0
  66. package/test/unit/data-structures/binary-tree/red-black-tree.boundary-stale-cache.coverage.test.ts +25 -0
  67. package/test/unit/data-structures/binary-tree/red-black-tree.boundary-update.coverage.test.ts +23 -0
  68. package/test/unit/data-structures/binary-tree/red-black-tree.cache-delete.coverage.test.ts +49 -0
  69. package/test/unit/data-structures/binary-tree/red-black-tree.cache-edge.coverage.test.ts +37 -0
  70. package/test/unit/data-structures/binary-tree/red-black-tree.cache-stale-insert.coverage.test.ts +39 -0
  71. package/test/unit/data-structures/binary-tree/red-black-tree.coverage.test.ts +334 -0
  72. package/test/unit/data-structures/binary-tree/red-black-tree.delete-fixup.coverage.test.ts +68 -0
  73. package/test/unit/data-structures/binary-tree/red-black-tree.delete-successor.coverage.test.ts +75 -0
  74. package/test/unit/data-structures/binary-tree/red-black-tree.factories.coverage.test.ts +26 -0
  75. package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-compare-update.coverage.test.ts +74 -0
  76. package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-no-update.coverage.test.ts +44 -0
  77. package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-nullish.coverage.test.ts +61 -0
  78. package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-defined.coverage.test.ts +35 -0
  79. package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-undefined.coverage.test.ts +43 -0
  80. package/test/unit/data-structures/binary-tree/red-black-tree.hint-more.coverage.test.ts +99 -0
  81. package/test/unit/data-structures/binary-tree/red-black-tree.hint.coverage.test.ts +60 -0
  82. package/test/unit/data-structures/binary-tree/red-black-tree.insert-cache-nullish.coverage.test.ts +29 -0
  83. package/test/unit/data-structures/binary-tree/red-black-tree.insert-header-parent-nullish.coverage.test.ts +17 -0
  84. package/test/unit/data-structures/binary-tree/red-black-tree.internal-walk.coverage.test.ts +57 -0
  85. package/test/unit/data-structures/binary-tree/red-black-tree.minmax-cache.test.ts +65 -0
  86. package/test/unit/data-structures/binary-tree/red-black-tree.misc-inputs.coverage.test.ts +17 -0
  87. package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-2.coverage.test.ts +121 -0
  88. package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-3.coverage.test.ts +55 -0
  89. package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-4.coverage.test.ts +44 -0
  90. package/test/unit/data-structures/binary-tree/red-black-tree.predsucc.coverage.test.ts +40 -0
  91. package/test/unit/data-structures/binary-tree/red-black-tree.remaining-branches.coverage.test.ts +123 -0
  92. package/test/unit/data-structures/binary-tree/red-black-tree.set-inputs.coverage.test.ts +64 -0
  93. package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-parent-cache.coverage.test.ts +79 -0
  94. package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-remaining.coverage.test.ts +44 -0
  95. package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-uncovered.coverage.test.ts +74 -0
  96. package/test/unit/data-structures/binary-tree/red-black-tree.update-branches.coverage.test.ts +30 -0
  97. package/test/unit/data-structures/binary-tree/segment-tree.more-branches.coverage.test.ts +31 -0
  98. package/test/unit/data-structures/binary-tree/tree-counter.coverage.test.ts +115 -0
  99. package/test/unit/data-structures/binary-tree/tree-counter.more-branches.coverage.test.ts +244 -0
  100. package/test/unit/data-structures/binary-tree/tree-counter.test.ts +4 -2
  101. package/test/unit/data-structures/binary-tree/tree-multi-map.coverage.test.ts +104 -0
  102. package/test/unit/data-structures/binary-tree/tree-multi-map.more-branches-2.coverage.test.ts +59 -0
  103. package/test/unit/data-structures/graph/abstract-graph.more-branches-2.coverage.test.ts +40 -0
  104. package/test/unit/data-structures/graph/abstract-graph.more-branches-3.coverage.test.ts +65 -0
  105. package/test/unit/data-structures/graph/abstract-graph.more-branches-4.coverage.test.ts +98 -0
  106. package/test/unit/data-structures/graph/abstract-graph.more-branches-5.coverage.test.ts +51 -0
  107. package/test/unit/data-structures/graph/abstract-graph.more-branches.coverage.test.ts +62 -0
  108. package/test/unit/data-structures/graph/directed-graph.more-branches-2.coverage.test.ts +38 -0
  109. package/test/unit/data-structures/graph/directed-graph.more-branches-3.coverage.test.ts +25 -0
  110. package/test/unit/data-structures/graph/directed-graph.more-branches.coverage.test.ts +82 -0
  111. package/test/unit/data-structures/graph/map-graph.more-branches.coverage.test.ts +22 -0
  112. package/test/unit/data-structures/graph/undirected-graph.more-branches-2.coverage.test.ts +35 -0
  113. package/test/unit/data-structures/graph/undirected-graph.more-branches.coverage.test.ts +87 -0
  114. package/test/unit/data-structures/hash/hash-map.more-branches.coverage.test.ts +64 -0
  115. package/test/unit/data-structures/hash/hash-map.toEntryFn-branch.coverage.test.ts +9 -0
  116. package/test/unit/data-structures/heap/heap.misc-branches.coverage.test.ts +110 -0
  117. package/test/unit/data-structures/heap/heap.remaining-branches.coverage.test.ts +22 -0
  118. package/test/unit/data-structures/heap/max-heap.coverage.test.ts +29 -0
  119. package/test/unit/data-structures/linked-list/doubly-linked-list.more-branches.coverage.test.ts +72 -0
  120. package/test/unit/data-structures/linked-list/linked-list.unshiftMany-else.coverage.test.ts +15 -0
  121. package/test/unit/data-structures/linked-list/singly-linked-list.coverage.test.ts +221 -0
  122. package/test/unit/data-structures/linked-list/singly-linked-list.more-branches.coverage.test.ts +86 -0
  123. package/test/unit/data-structures/linked-list/skip-linked-list.more-branches.coverage.test.ts +31 -0
  124. package/test/unit/data-structures/matrix/matrix.more-branches.coverage.test.ts +81 -0
  125. package/test/unit/data-structures/matrix/matrix.pivotElement-nullish.coverage.test.ts +28 -0
  126. package/test/unit/data-structures/priority-queue/max-priority-queue.more-branches.coverage.test.ts +10 -0
  127. package/test/unit/data-structures/priority-queue/priority-queue.coverage.test.ts +21 -0
  128. package/test/unit/data-structures/queue/deque.coverage.test.ts +173 -0
  129. package/test/unit/data-structures/queue/deque.more-branches-2.coverage.test.ts +39 -0
  130. package/test/unit/data-structures/queue/deque.more-branches-3.coverage.test.ts +9 -0
  131. package/test/unit/data-structures/queue/deque.more-branches.coverage.test.ts +95 -0
  132. package/test/unit/data-structures/queue/queue.coverage.test.ts +138 -0
  133. package/test/unit/data-structures/queue/queue.more-branches-2.coverage.test.ts +27 -0
  134. package/test/unit/data-structures/stack/stack.coverage.test.ts +112 -0
  135. package/test/unit/data-structures/tree/tree.more-branches.coverage.test.ts +9 -0
  136. package/test/unit/data-structures/trie/trie.more-branches-2.coverage.test.ts +51 -0
  137. package/test/utils/patch.ts +33 -0
@@ -0,0 +1,40 @@
1
+ import { BST } from '../../../../src';
2
+
3
+ describe('BST additional reachable branch coverage (batch 5)', () => {
4
+ it('search(): forces shouldVisitRight key-pruning branch + covers && chain short-circuit variants', () => {
5
+ const t = new BST<number, number>();
6
+ t.setMany([
7
+ [10, 10],
8
+ [20, 20]
9
+ ]);
10
+
11
+ const originalIsPredicate = (t as any)._isPredicate.bind(t);
12
+
13
+ const run = (keyNodeOrEntry: any) => {
14
+ let calls = 0;
15
+ (t as any)._isPredicate = (p: any) => {
16
+ // 1st call (isPred computation) => true, to bypass the fast-path.
17
+ // After that, behave like the real implementation so _ensurePredicate works
18
+ // and shouldVisitRight sees "not predicate".
19
+ calls++;
20
+ if (calls === 1) return true;
21
+ return originalIsPredicate(p);
22
+ };
23
+
24
+ try {
25
+ t.search(keyNodeOrEntry);
26
+ } finally {
27
+ (t as any)._isPredicate = originalIsPredicate;
28
+ }
29
+ };
30
+
31
+ // 1) benchmarkKey === null => hits first short-circuit (benchmarkKey !== null)
32
+ run([null, 0]);
33
+
34
+ // 2) benchmarkKey === undefined => passes first check, fails second
35
+ run([undefined, 0]);
36
+
37
+ // 3) compare(...) < 0 is false => passes both nullish checks
38
+ run([5, 0]);
39
+ });
40
+ });
@@ -0,0 +1,39 @@
1
+ import { BST, BSTNode, Range } from '../../../../src';
2
+
3
+ describe('BST additional coverage', () => {
4
+ it('BSTNode.familyPosition covers MAL_NODE branch', () => {
5
+ const root = new BSTNode<number, number>(10);
6
+ const mal = new BSTNode<number, number>(5);
7
+ mal.parent = root;
8
+ // parent does not point to mal via left/right
9
+ expect(mal.familyPosition).toBe('MAL_NODE');
10
+ });
11
+
12
+ it('getNode returns undefined for null/undefined, and handles runtime Range input', () => {
13
+ const bst = new BST<number, number>();
14
+ bst.set(2, 2);
15
+
16
+ expect(bst.getNode(undefined as any)).toBeUndefined();
17
+ expect(bst.getNode(null as any)).toBeUndefined();
18
+
19
+ const r = new Range<number>(2, 2, true, true);
20
+ expect(bst.getNode(r as any)?.key).toBe(2);
21
+ });
22
+
23
+ it('getNodes returns [] for null/undefined and ignores entry with null key', () => {
24
+ const bst = new BST<number, number>([2, 1, 3]);
25
+
26
+ expect(bst.getNodes(undefined as any)).toEqual([]);
27
+ expect(bst.getNodes(null as any)).toEqual([]);
28
+
29
+ // entry with null key => targetKey undefined => []
30
+ expect(bst.getNodes([null as any, 123] as any)).toEqual([]);
31
+ });
32
+
33
+ it('getNodes returns [] when startNode is not a real node', () => {
34
+ const bst = new BST<number, number>([2, 1, 3]);
35
+
36
+ // ensureNode(null) => undefined => []
37
+ expect(bst.getNodes(2, false, null as any)).toEqual([]);
38
+ });
39
+ });
@@ -0,0 +1,29 @@
1
+ import { BSTNode } from '../../../../src';
2
+
3
+ describe('BSTNode familyPosition coverage', () => {
4
+ it('covers ISOLATED/ROOT/LEFT/RIGHT/ROOT_LEFT/ROOT_RIGHT and MAL_NODE', () => {
5
+ const isolated = new BSTNode<number, number>(1);
6
+ expect(isolated.familyPosition).toBe('ISOLATED');
7
+
8
+ const root = new BSTNode<number, number>(10);
9
+ const left = new BSTNode<number, number>(5);
10
+ const right = new BSTNode<number, number>(15);
11
+
12
+ root.left = left;
13
+ root.right = right;
14
+
15
+ expect(root.familyPosition).toBe('ROOT');
16
+ expect(left.familyPosition).toBe('LEFT');
17
+ expect(right.familyPosition).toBe('RIGHT');
18
+
19
+ left.left = new BSTNode<number, number>(2);
20
+ right.right = new BSTNode<number, number>(20);
21
+
22
+ expect(left.familyPosition).toBe('ROOT_LEFT');
23
+ expect(right.familyPosition).toBe('ROOT_RIGHT');
24
+
25
+ const mal = new BSTNode<number, number>(999);
26
+ mal.parent = root;
27
+ expect(mal.familyPosition).toBe('MAL_NODE');
28
+ });
29
+ });
@@ -0,0 +1,43 @@
1
+ import { BST, Range } from '../../../../src';
2
+
3
+ describe('BST range/pruning coverage', () => {
4
+ it('getNodes(range) covers includeLow/includeHigh pruning branches', () => {
5
+ const bst = new BST<number, number>();
6
+ for (const k of [10, 5, 15, 3, 7, 12, 18]) bst.set(k, k);
7
+
8
+ // includeLow/includeHigh true
9
+ const r1 = new Range<number>(7, 12, true, true);
10
+ expect(bst.getNodes(r1 as any).map(n => n.key)).toEqual([7, 10, 12]);
11
+
12
+ // includeLow false (exclude 7)
13
+ const r2 = new Range<number>(7, 12, false, true);
14
+ expect(bst.getNodes(r2 as any).map(n => n.key)).toEqual([10, 12]);
15
+
16
+ // includeHigh false (exclude 12)
17
+ const r3 = new Range<number>(7, 12, true, false);
18
+ expect(bst.getNodes(r3 as any).map(n => n.key)).toEqual([7, 10]);
19
+
20
+ // exclude both ends
21
+ const r4 = new Range<number>(7, 12, false, false);
22
+ expect(bst.getNodes(r4 as any).map(n => n.key)).toEqual([10]);
23
+ });
24
+
25
+ it('getNodes(predicate) exercises predicate-search shouldVisitLeft/Right fallthrough', () => {
26
+ const bst = new BST<number, number>();
27
+ for (const k of [4, 2, 6, 1, 3, 5, 7]) bst.set(k, k);
28
+
29
+ // Predicate that matches only leaf nodes, to force traversal and filtering.
30
+ const nodes = bst.getNodes(node => !bst.isRealNode(node.left) && !bst.isRealNode(node.right));
31
+ expect(nodes.map(n => n.key).sort((a, b) => a - b)).toEqual([1, 3, 5, 7]);
32
+
33
+ // onlyOne=true should early-exit after first match (in-order => 1)
34
+ const one = bst.getNodes(node => !bst.isRealNode(node.left) && !bst.isRealNode(node.right), true);
35
+ expect(one).toHaveLength(1);
36
+ expect(one[0].key).toBe(1);
37
+ });
38
+
39
+ it('rangeSearch overload [low, high] is accepted', () => {
40
+ const bst = new BST<number, number>([10, 5, 15, 3, 7, 12, 18]);
41
+ expect(bst.rangeSearch([7, 12])).toEqual([7, 10, 12]);
42
+ });
43
+ });
@@ -0,0 +1,30 @@
1
+ import { BST } from '../../../../src';
2
+
3
+ describe('BST search() fast-path coverage', () => {
4
+ it('search(node) uses node.key extraction branch and returns callback result', () => {
5
+ const t = new BST<number, string>([], { isMapMode: false });
6
+ t.set(10, 'a');
7
+ t.set(5, 'b');
8
+ t.set(15, 'c');
9
+
10
+ const n5 = t.getNode(5)!;
11
+ const out = t.search(n5, false, (n: any) => n.value);
12
+ expect(out).toEqual(['b']);
13
+ });
14
+
15
+ it('search(entry) with null key returns [] (covers entry null-key guard in fast path)', () => {
16
+ const t = new BST<number, number>([], { isMapMode: false });
17
+ for (const k of [10, 5, 15]) t.set(k, k);
18
+
19
+ const out = t.search([null as any, 1] as any);
20
+ expect(out).toEqual([]);
21
+ });
22
+
23
+ it('search by key returns [] when startNode ensureNode fails', () => {
24
+ const t = new BST<number, number>([], { isMapMode: false });
25
+ for (const k of [10, 5, 15]) t.set(k, k);
26
+
27
+ const out = t.search(10 as any, false, (n: any) => n.key, null as any);
28
+ expect(out).toEqual([]);
29
+ });
30
+ });
@@ -1,8 +1,17 @@
1
1
  import { BST, BSTNode, IBinaryTree, Range } from '../../../../src';
2
2
  import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config';
3
+ import { withMutedConsole } from '../../../utils/patch';
3
4
 
4
5
  const isDebug = isDebugTest;
5
6
 
7
+ function expectSingleDeleteResult<K, V>(removed: any, expectedKey: K) {
8
+ // Most tree delete APIs here return an array of { deleted, ... }.
9
+ expect(removed).toBeInstanceOf(Array);
10
+ expect(removed[0]).toBeDefined();
11
+ expect(removed[0].deleted).toBeDefined();
12
+ if (removed[0].deleted) expect(removed[0].deleted.key).toBe(expectedKey);
13
+ }
14
+
6
15
  describe('BST operations test', () => {
7
16
  it('should add undefined and null', () => {
8
17
  const bst = new BST<number, string>();
@@ -115,114 +124,74 @@ describe('BST operations test', () => {
115
124
  expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
116
125
 
117
126
  const removed11 = bst.delete(11);
118
- expect(removed11).toBeInstanceOf(Array);
119
- expect(removed11[0]).toBeDefined();
120
- expect(removed11[0].deleted).toBeDefined();
121
-
122
- if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11);
127
+ expectSingleDeleteResult(removed11, 11);
123
128
 
124
129
  expect(bst.isAVLBalanced()).toBe(true);
125
130
 
126
131
  expect(bst.getHeight(15)).toBe(1);
127
132
 
128
133
  const removed1 = bst.delete(1);
129
- expect(removed1).toBeInstanceOf(Array);
130
- expect(removed1[0]).toBeDefined();
131
- expect(removed1[0].deleted).toBeDefined();
132
- if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1);
134
+ expectSingleDeleteResult(removed1, 1);
133
135
 
134
136
  expect(bst.isAVLBalanced()).toBe(true);
135
137
 
136
138
  expect(bst.getHeight()).toBe(4);
137
139
 
138
140
  const removed4 = bst.delete(4);
139
- expect(removed4).toBeInstanceOf(Array);
140
- expect(removed4[0]).toBeDefined();
141
- expect(removed4[0].deleted).toBeDefined();
142
- if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4);
141
+ expectSingleDeleteResult(removed4, 4);
143
142
  expect(bst.isAVLBalanced()).toBe(true);
144
143
  expect(bst.getHeight()).toBe(4);
145
144
 
146
145
  const removed10 = bst.delete(10);
147
- expect(removed10).toBeInstanceOf(Array);
148
- expect(removed10[0]).toBeDefined();
149
- expect(removed10[0].deleted).toBeDefined();
150
- if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10);
146
+ expectSingleDeleteResult(removed10, 10);
151
147
  expect(bst.isAVLBalanced()).toBe(false);
152
148
  expect(bst.getHeight()).toBe(4);
153
149
 
154
150
  const removed15 = bst.delete(15);
155
- expect(removed15).toBeInstanceOf(Array);
156
- expect(removed15[0]).toBeDefined();
157
- expect(removed15[0].deleted).toBeDefined();
158
- if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15);
151
+ expectSingleDeleteResult(removed15, 15);
159
152
 
160
153
  expect(bst.isAVLBalanced()).toBe(true);
161
154
  expect(bst.getHeight()).toBe(3);
162
155
 
163
156
  const removed5 = bst.delete(5);
164
- expect(removed5).toBeInstanceOf(Array);
165
- expect(removed5[0]).toBeDefined();
166
- expect(removed5[0].deleted).toBeDefined();
167
- if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5);
157
+ expectSingleDeleteResult(removed5, 5);
168
158
 
169
159
  expect(bst.isAVLBalanced()).toBe(true);
170
160
  expect(bst.getHeight()).toBe(3);
171
161
 
172
162
  const removed13 = bst.delete(13);
173
- expect(removed13).toBeInstanceOf(Array);
174
- expect(removed13[0]).toBeDefined();
175
- expect(removed13[0].deleted).toBeDefined();
176
- if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13);
163
+ expectSingleDeleteResult(removed13, 13);
177
164
  expect(bst.isAVLBalanced()).toBe(true);
178
165
  expect(bst.getHeight()).toBe(3);
179
166
 
180
167
  const removed3 = bst.delete(3);
181
- expect(removed3).toBeInstanceOf(Array);
182
- expect(removed3[0]).toBeDefined();
183
- expect(removed3[0].deleted).toBeDefined();
184
- if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3);
168
+ expectSingleDeleteResult(removed3, 3);
185
169
  expect(bst.isAVLBalanced()).toBe(false);
186
170
  expect(bst.getHeight()).toBe(3);
187
171
 
188
172
  const removed8 = bst.delete(8);
189
- expect(removed8).toBeInstanceOf(Array);
190
- expect(removed8[0]).toBeDefined();
191
- expect(removed8[0].deleted).toBeDefined();
192
- if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8);
173
+ expectSingleDeleteResult(removed8, 8);
193
174
  expect(bst.isAVLBalanced()).toBe(true);
194
175
  expect(bst.getHeight()).toBe(3);
195
176
 
196
177
  const removed6 = bst.delete(6);
197
- expect(removed6).toBeInstanceOf(Array);
198
- expect(removed6[0]).toBeDefined();
199
- expect(removed6[0].deleted).toBeDefined();
200
- if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6);
178
+ expectSingleDeleteResult(removed6, 6);
201
179
  expect(bst.delete(6).length).toBe(0);
202
180
  expect(bst.isAVLBalanced()).toBe(false);
203
181
  expect(bst.getHeight()).toBe(3);
204
182
 
205
183
  const removed7 = bst.delete(7);
206
- expect(removed7).toBeInstanceOf(Array);
207
- expect(removed7[0]).toBeDefined();
208
- expect(removed7[0].deleted).toBeDefined();
209
- if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7);
184
+ expectSingleDeleteResult(removed7, 7);
210
185
  expect(bst.isAVLBalanced()).toBe(false);
211
186
  expect(bst.getHeight()).toBe(3);
212
187
 
213
188
  const removed9 = bst.delete(9);
214
- expect(removed9).toBeInstanceOf(Array);
215
- expect(removed9[0]).toBeDefined();
216
- expect(removed9[0].deleted).toBeDefined();
217
- if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9);
189
+ expectSingleDeleteResult(removed9, 9);
218
190
  expect(bst.isAVLBalanced()).toBe(false);
219
191
  expect(bst.getHeight()).toBe(3);
220
192
 
221
193
  const removed14 = bst.delete(14);
222
- expect(removed14).toBeInstanceOf(Array);
223
- expect(removed14[0]).toBeDefined();
224
- expect(removed14[0].deleted).toBeDefined();
225
- if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14);
194
+ expectSingleDeleteResult(removed14, 14);
226
195
  expect(bst.isAVLBalanced()).toBe(false);
227
196
  expect(bst.getHeight()).toBe(2);
228
197
 
@@ -3300,11 +3269,12 @@ describe('BST Comparator Tests', () => {
3300
3269
  });
3301
3270
 
3302
3271
  describe('classic use', () => {
3303
- it('@example basic BST creation and add operation', () => {
3272
+ it('@example basic BST creation and add operation', async () => {
3304
3273
  // Create a simple BST with numeric keys
3305
3274
  const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
3306
3275
 
3307
- bst.print();
3276
+ // Keep the example output in source comments but avoid noisy test logs.
3277
+ await withMutedConsole(() => bst.print());
3308
3278
  // _______8__________
3309
3279
  // / \
3310
3280
  // ___4___ ____12_____
@@ -0,0 +1,66 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree boundary corruption repair coverage', () => {
4
+ it('min-attach: when header._right is corrupted to NIL, branch mirrors max cache to new min', () => {
5
+ let tree: RedBlackTree<number, number> = new RedBlackTree<number>();
6
+ let active = false;
7
+ let flipped = false;
8
+ const comparator = (a: number, b: number) => {
9
+ // Flip once during the boundary pre-check, before the attach happens.
10
+ if (active && !flipped && tree) {
11
+ flipped = true;
12
+ (tree as any)._header._right = tree.NIL;
13
+ }
14
+ return a - b;
15
+ };
16
+
17
+ tree = new RedBlackTree<number, number>([], { isMapMode: false, comparator });
18
+ tree.set(10, 10);
19
+ tree.set(5, 5);
20
+ tree.set(15, 15);
21
+
22
+ // Boundary insert smaller than current min.
23
+ active = true;
24
+ tree.set(1, 1);
25
+ active = false;
26
+
27
+ // Repair branch should have mirrored max cache when header._right was NIL.
28
+ expect((tree as any)._header._right.key).toBe(1);
29
+
30
+ // Restore correct max cache so subsequent tests/users aren't affected.
31
+ const root = (tree as any)._root;
32
+ (tree as any)._setMaxCache(tree.isRealNode(root) ? tree.getRightMost((n: any) => n, root) : undefined);
33
+ expect((tree as any)._header._right.key).toBe(15);
34
+ });
35
+
36
+ it('max-attach: when header._left is corrupted to NIL, branch initializes min cache during max attach', () => {
37
+ let tree: RedBlackTree<number, number> = new RedBlackTree<number>();;
38
+ let active = false;
39
+ let flipped = false;
40
+ const comparator = (a: number, b: number) => {
41
+ if (active && !flipped && tree) {
42
+ flipped = true;
43
+ (tree as any)._header._left = tree.NIL;
44
+ }
45
+ return a - b;
46
+ };
47
+
48
+ tree = new RedBlackTree<number, number>([], { isMapMode: false, comparator });
49
+ tree.set(10, 10);
50
+ tree.set(5, 5);
51
+ tree.set(15, 15);
52
+
53
+ // Boundary insert larger than current max.
54
+ active = true;
55
+ tree.set(100, 100);
56
+ active = false;
57
+
58
+ // Observe the branch effect before repairing (min cache got initialized).
59
+ expect((tree as any)._header._left.key).toBe(100);
60
+
61
+ // Restore correct min cache.
62
+ const root = (tree as any)._root;
63
+ (tree as any)._setMinCache(tree.isRealNode(root) ? tree.getLeftMost((n: any) => n, root) : undefined);
64
+ expect((tree as any)._header._left.key).toBe(5);
65
+ });
66
+ });
@@ -0,0 +1,18 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree boundary max update coverage', () => {
4
+ it('mapMode: updating existing max key with defined value hits store.set branch in cMax===0 fast-path', () => {
5
+ const t = new RedBlackTree<number, string>(); // mapMode default
6
+
7
+ t.set(10, 'mid');
8
+ t.set(5, 'min');
9
+ t.set(15, 'max');
10
+
11
+ // Update existing max key with a defined value.
12
+ t.set(15, 'max2');
13
+
14
+ expect(t.get(15)).toBe('max2');
15
+ // Assert store updated (fast-path uses store.set).
16
+ expect((t as any)._store.get(15)).toBe('max2');
17
+ });
18
+ });
@@ -0,0 +1,53 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree boundary attach null/undefined coverage', () => {
4
+ it('min boundary attach works when min.left is null', () => {
5
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
6
+ t.set(10, 10);
7
+ t.set(5, 5);
8
+
9
+ const minNode = t.getNode(5)!;
10
+ // Force the boundary condition to treat left as empty via null.
11
+ (minNode as any)._left = null;
12
+
13
+ t.set(1, 1);
14
+
15
+ expect(t.getNode(1)?.parent?.key).toBe(5);
16
+ expect((t as any)._header._left.key).toBe(1);
17
+ });
18
+
19
+ it('min boundary attach works when min.left is undefined', () => {
20
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
21
+ t.set(10, 10);
22
+ t.set(5, 5);
23
+
24
+ const minNode = t.getNode(5)!;
25
+ // Force the boundary condition to treat left as empty via undefined.
26
+ (minNode as any)._left = undefined;
27
+
28
+ t.set(1, 1);
29
+
30
+ expect(t.getNode(1)?.parent?.key).toBe(5);
31
+ expect((t as any)._header._left.key).toBe(1);
32
+ });
33
+
34
+ it('max boundary attach works when max.right is null/undefined', () => {
35
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
36
+ t.set(10, 10);
37
+ t.set(15, 15);
38
+
39
+ const maxNode = t.getNode(15)!;
40
+ // First cover null.
41
+ (maxNode as any)._right = null;
42
+ t.set(20, 20);
43
+ expect(t.getNode(20)?.parent?.key).toBe(15);
44
+ expect((t as any)._header._right.key).toBe(20);
45
+
46
+ // Now cover undefined on the new max.
47
+ const maxNode2 = t.getNode(20)!;
48
+ (maxNode2 as any)._right = undefined;
49
+ t.set(25, 25);
50
+ expect(t.getNode(25)?.parent?.key).toBe(20);
51
+ expect((t as any)._header._right.key).toBe(25);
52
+ });
53
+ });
@@ -0,0 +1,25 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree boundary attach stale cache coverage', () => {
4
+ it('boundary max attach repairs stale min cache when header._left === NIL', () => {
5
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
6
+ t.set(10, 10);
7
+ t.set(5, 5);
8
+
9
+ const NIL = (t as any).NIL;
10
+
11
+ // Stale min cache: header._left is NIL, but tree is non-empty.
12
+ (t as any)._header._left = NIL;
13
+
14
+ // Insert new maximum; should take boundary max attach path and hit:
15
+ // if (header._left === NIL) this._setMinCache(newNode)
16
+ t.set(20, 20);
17
+
18
+ expect((t as any)._header._right.key).toBe(20);
19
+ expect((t as any)._header._left).not.toBe(NIL);
20
+
21
+ // Under stale/corrupted cache conditions, the boundary fast path mirrors min to the inserted node.
22
+ // (In normal operation header._left would not be NIL when the tree is non-empty.)
23
+ expect((t as any)._header._left.key).toBe(20);
24
+ });
25
+ });
@@ -0,0 +1,23 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree boundary update coverage', () => {
4
+ it('updating existing min/max via boundary cache fast paths does not change size', () => {
5
+ const t = new RedBlackTree<number, string>([], { isMapMode: false });
6
+
7
+ t.set(10, 'a');
8
+ t.set(5, 'min');
9
+ t.set(15, 'max');
10
+
11
+ const size0 = t.size;
12
+
13
+ // Update existing min key: should hit cMin===0 fast-path.
14
+ expect(t.set(5, 'min2')).toBe(true);
15
+ expect(t.size).toBe(size0);
16
+ expect(t.getNode(5)?.value).toBe('min2');
17
+
18
+ // Update existing max key: should hit cMax===0 fast-path.
19
+ expect(t.set(15, 'max2')).toBe(true);
20
+ expect(t.size).toBe(size0);
21
+ expect(t.getNode(15)?.value).toBe('max2');
22
+ });
23
+ });
@@ -0,0 +1,49 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree delete cache fallback coverage', () => {
4
+ it('forces min/max fallback recomputation branches after deleting min/max', () => {
5
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
6
+ for (const k of [10, 5, 15, 3, 7, 12, 18]) t.set(k, k);
7
+
8
+ // Delete current min and max to set willDeleteMin/Max paths.
9
+ const NIL = (t as any).NIL;
10
+ const minNode = (t as any)._header._left;
11
+ const maxNode = (t as any)._header._right;
12
+ expect(minNode).not.toBe(NIL);
13
+ expect(maxNode).not.toBe(NIL);
14
+ const minKey = minNode.key as number;
15
+ const maxKey = maxNode.key as number;
16
+
17
+ // After first delete, corrupt caches so fallback recompute branches run.
18
+ t.delete(minKey);
19
+ (t as any)._minNode = undefined;
20
+
21
+ // Delete max and also corrupt max cache.
22
+ t.delete(maxKey);
23
+ (t as any)._maxNode = undefined;
24
+
25
+ // Trigger an additional delete to enter the cache-update block with size>0.
26
+ t.delete(10);
27
+
28
+ // Should still have correct header min/max after recomputation.
29
+ const keys = [...t].map(([k]) => k).sort((a, b) => a - b);
30
+ if (keys.length > 0) {
31
+ expect((t as any)._header._left.key).toBe(keys[0]);
32
+ expect((t as any)._header._right.key).toBe(keys[keys.length - 1]);
33
+ }
34
+ });
35
+
36
+ it('size<=0 branch clears min/max caches', () => {
37
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
38
+ t.set(1, 1);
39
+
40
+ t.delete(1);
41
+
42
+ expect(t.size).toBe(0);
43
+ // expect(t.min).toBeUndefined();
44
+ // expect(t.max).toBeUndefined();
45
+ const NIL = (t as any).NIL;
46
+ expect((t as any)._header._left).toBe(NIL);
47
+ expect((t as any)._header._right).toBe(NIL);
48
+ });
49
+ });
@@ -0,0 +1,37 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree cache edge coverage', () => {
4
+ it('boundary min attach updates max cache when header._right is stale NIL', () => {
5
+ const t = new RedBlackTree<number, number>();
6
+ t.set(10, 10);
7
+
8
+ // Simulate stale max cache.
9
+ const NIL = (t as any).NIL;
10
+ (t as any)._header._right = NIL;
11
+
12
+ // Insert smaller than current min; min has no left -> boundary min attach path.
13
+ t.set(5, 5);
14
+
15
+ // After insertion, max cache should not remain NIL.
16
+ expect((t as any)._header._right).not.toBe(NIL);
17
+ expect((t as any)._maxNode).toBeDefined();
18
+ expect((t as any)._header._right).toBe((t as any)._maxNode);
19
+ });
20
+
21
+ it('boundary max attach updates min cache when header._left is stale NIL', () => {
22
+ const t = new RedBlackTree<number, number>();
23
+ t.set(10, 10);
24
+
25
+ // Simulate stale min cache.
26
+ const NIL = (t as any).NIL;
27
+ (t as any)._header._left = NIL;
28
+
29
+ // Insert greater than current max; max has no right -> boundary max attach path.
30
+ t.set(15, 15);
31
+
32
+ // After insertion, min cache should not remain NIL.
33
+ expect((t as any)._header._left).not.toBe(NIL);
34
+ expect((t as any)._minNode).toBeDefined();
35
+ expect((t as any)._header._left).toBe((t as any)._minNode);
36
+ });
37
+ });
@@ -0,0 +1,39 @@
1
+ import { RedBlackTree } from '../../../../src';
2
+
3
+ describe('RedBlackTree stale cache insert coverage', () => {
4
+ it('stale header min/max caches trigger comparison-based cache repair on insertion', () => {
5
+ const t = new RedBlackTree<number, number>([], { isMapMode: false });
6
+ t.set(10, 10);
7
+ t.set(5, 5);
8
+ t.set(15, 15);
9
+
10
+ const n10 = t.getNode(10)!;
11
+
12
+ // Corrupt caches to point at an interior node.
13
+ (t as any)._header._left = n10;
14
+ (t as any)._header._right = n10;
15
+
16
+ // Insert a new min (not a boundary attach, because min cache is stale and minN.left is real).
17
+ t.set(1, 1);
18
+ expect((t as any)._header._left.key).toBe(1);
19
+
20
+ // Re-corrupt and insert a new max.
21
+ (t as any)._header._left = n10;
22
+ (t as any)._header._right = n10;
23
+
24
+ t.set(20, 20);
25
+ expect((t as any)._header._right.key).toBe(20);
26
+ });
27
+
28
+ it('mapMode update with undefined value does not take store-fast-path (no throw)', () => {
29
+ const t = new RedBlackTree<number, string>([], { isMapMode: true });
30
+ t.set(1, 'a');
31
+ expect(t.get(1)).toBe('a');
32
+
33
+ // nextValue is undefined => should bypass map-mode store.has fast-path
34
+ // and go through normal set logic.
35
+ t.set(1, undefined as any);
36
+ // Existing semantics: undefined does not overwrite stored value in mapMode.
37
+ expect(t.get(1)).toBe('a');
38
+ });
39
+ });