@calcit/ternary-tree 0.0.25 → 0.0.26
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.
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +6 -0
- package/lib/bench.d.mts +11 -0
- package/lib/bench.mjs +99 -0
- package/lib/list.d.mts +1 -0
- package/lib/list.mjs +106 -75
- package/lib/map.mjs +99 -60
- package/lib/types.d.mts +1 -0
- package/package.json +6 -5
|
Binary file
|
package/.yarnrc.yml
ADDED
package/lib/bench.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Micro-benchmark for ts-ternary-tree hot paths.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* yarn bench
|
|
6
|
+
*
|
|
7
|
+
* Each bench() call runs the task for ~1 second and reports ops/sec.
|
|
8
|
+
* A higher number is better. Compare before/after code changes to judge
|
|
9
|
+
* whether an optimisation has real impact.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/lib/bench.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Micro-benchmark for ts-ternary-tree hot paths.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* yarn bench
|
|
6
|
+
*
|
|
7
|
+
* Each bench() call runs the task for ~1 second and reports ops/sec.
|
|
8
|
+
* A higher number is better. Compare before/after code changes to judge
|
|
9
|
+
* whether an optimisation has real impact.
|
|
10
|
+
*/
|
|
11
|
+
import { initTernaryTreeList, listGet, assocList, dissocList, listToItems, concat, } from "./list.mjs";
|
|
12
|
+
import { initTernaryTreeMapFromArray, assocMap, dissocMap, mapGetDefault, } from "./map.mjs";
|
|
13
|
+
import { deepEqual, overwriteComparator } from "./utils.mjs";
|
|
14
|
+
import { mergeValueHash, overwriteHashGenerator, valueHash } from "./types.mjs";
|
|
15
|
+
overwriteComparator(deepEqual);
|
|
16
|
+
overwriteHashGenerator((x) => mergeValueHash(10, valueHash(x)));
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
function bench(label, fn, durationMs = 1000) {
|
|
21
|
+
// Warmup
|
|
22
|
+
for (let i = 0; i < 100; i++)
|
|
23
|
+
fn();
|
|
24
|
+
let ops = 0;
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
while (Date.now() - start < durationMs) {
|
|
27
|
+
fn();
|
|
28
|
+
ops++;
|
|
29
|
+
}
|
|
30
|
+
const elapsed = Date.now() - start;
|
|
31
|
+
const opsPerSec = Math.round((ops / elapsed) * 1000);
|
|
32
|
+
console.log(` ${label.padEnd(40)} ${opsPerSec.toLocaleString()} ops/sec`);
|
|
33
|
+
}
|
|
34
|
+
function section(title) {
|
|
35
|
+
console.log(`\n--- ${title} ---`);
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Fixtures
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
const SIZE_SMALL = 100;
|
|
41
|
+
const SIZE_MEDIUM = 1000;
|
|
42
|
+
const SIZE_LARGE = 10000;
|
|
43
|
+
const arrSmall = Array.from({ length: SIZE_SMALL }, (_, i) => i);
|
|
44
|
+
const arrMedium = Array.from({ length: SIZE_MEDIUM }, (_, i) => i);
|
|
45
|
+
const arrLarge = Array.from({ length: SIZE_LARGE }, (_, i) => i);
|
|
46
|
+
const listSmall = initTernaryTreeList(arrSmall);
|
|
47
|
+
const listMedium = initTernaryTreeList(arrMedium);
|
|
48
|
+
const listLarge = initTernaryTreeList(arrLarge);
|
|
49
|
+
const mapMediumPairs = arrMedium.map((i) => [`k${i}`, i]);
|
|
50
|
+
const mapMedium = initTernaryTreeMapFromArray(mapMediumPairs);
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// List benchmarks
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
section("listGet (random access)");
|
|
55
|
+
bench("listGet small (n=100) middle idx", () => listGet(listSmall, 50));
|
|
56
|
+
bench("listGet medium (n=1k) middle idx", () => listGet(listMedium, 500));
|
|
57
|
+
bench("listGet large (n=10k) middle idx", () => listGet(listLarge, 5000));
|
|
58
|
+
section("listToItems (full iteration)");
|
|
59
|
+
bench("listToItems small (n=100)", () => {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
61
|
+
for (const _ of listToItems(listSmall)) { /* consume */ }
|
|
62
|
+
});
|
|
63
|
+
bench("listToItems medium (n=1k)", () => {
|
|
64
|
+
for (const _ of listToItems(listMedium)) { /* consume */ }
|
|
65
|
+
});
|
|
66
|
+
bench("listToItems large (n=10k)", () => {
|
|
67
|
+
for (const _ of listToItems(listLarge)) { /* consume */ }
|
|
68
|
+
});
|
|
69
|
+
section("assocList (element update)");
|
|
70
|
+
bench("assocList small (n=100) idx=50", () => assocList(listSmall, 50, 999));
|
|
71
|
+
bench("assocList medium (n=1k) idx=500", () => assocList(listMedium, 500, 999));
|
|
72
|
+
bench("assocList large (n=10k) idx=5000", () => assocList(listLarge, 5000, 999));
|
|
73
|
+
section("dissocList (element remove)");
|
|
74
|
+
bench("dissocList small (n=100) idx=50", () => dissocList(listSmall, 50));
|
|
75
|
+
bench("dissocList medium (n=1k) idx=500", () => dissocList(listMedium, 500));
|
|
76
|
+
bench("dissocList large (n=10k) idx=5000", () => dissocList(listLarge, 5000));
|
|
77
|
+
section("initTernaryTreeList (construction)");
|
|
78
|
+
bench("init list small (n=100)", () => initTernaryTreeList(arrSmall));
|
|
79
|
+
bench("init list medium (n=1k)", () => initTernaryTreeList(arrMedium));
|
|
80
|
+
bench("init list large (n=10k)", () => initTernaryTreeList(arrLarge));
|
|
81
|
+
section("concat");
|
|
82
|
+
const half1 = initTernaryTreeList(arrMedium.slice(0, 500));
|
|
83
|
+
const half2 = initTernaryTreeList(arrMedium.slice(500));
|
|
84
|
+
bench("concat two halves (n=500 each)", () => concat(half1, half2));
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Map benchmarks
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
section("assocMap (key update/insert)");
|
|
89
|
+
bench("assocMap medium (n=1k) existing key", () => assocMap(mapMedium, "k500", 9999));
|
|
90
|
+
bench("assocMap medium (n=1k) new key", () => assocMap(mapMedium, "new-key", 9999));
|
|
91
|
+
section("dissocMap (key remove)");
|
|
92
|
+
bench("dissocMap medium (n=1k)", () => dissocMap(mapMedium, "k500"));
|
|
93
|
+
section("mapGetDefault (lookup)");
|
|
94
|
+
bench("mapGetDefault medium (n=1k) hit", () => mapGetDefault(mapMedium, "k500", -1));
|
|
95
|
+
bench("mapGetDefault medium (n=1k) miss", () => mapGetDefault(mapMedium, "no-such-key", -1));
|
|
96
|
+
section("initTernaryTreeMap (construction)");
|
|
97
|
+
bench("init map small (n=100)", () => initTernaryTreeMapFromArray(mapMediumPairs.slice(0, 100)));
|
|
98
|
+
bench("init map medium (n=1k)", () => initTernaryTreeMapFromArray(mapMediumPairs));
|
|
99
|
+
console.log("\nDone.");
|
package/lib/list.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TernaryTreeList } from "./types.mjs";
|
|
2
|
+
export declare function enableStructureCheck(enabled?: boolean): void;
|
|
2
3
|
export declare function getDepth<T>(tree: TernaryTreeList<T>): number;
|
|
3
4
|
export declare function makeTernaryTreeList<T>(size: number, offset: number, xs: Array<TernaryTreeList<T>>): TernaryTreeList<T>;
|
|
4
5
|
export declare function initTernaryTreeList<T>(xs: Array<T>): TernaryTreeList<T>;
|
package/lib/list.mjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { TernaryTreeKind } from "./types.mjs";
|
|
2
|
-
import { dataEqual,
|
|
2
|
+
import { dataEqual, roughIntPow } from "./utils.mjs";
|
|
3
|
+
// When false (default), internal structure checks are skipped for performance.
|
|
4
|
+
// Set to true in development/testing to catch structural bugs early.
|
|
5
|
+
let _devMode = false;
|
|
6
|
+
export function enableStructureCheck(enabled = true) {
|
|
7
|
+
_devMode = enabled;
|
|
8
|
+
}
|
|
3
9
|
// just get, will not compute recursively
|
|
4
10
|
export function getDepth(tree) {
|
|
5
11
|
if (tree == null)
|
|
@@ -18,16 +24,18 @@ let isEmptyBranch = (x) => {
|
|
|
18
24
|
}
|
|
19
25
|
return x.size == 0;
|
|
20
26
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
// Explicit 2/3-arg versions avoid rest-parameter array allocation on every call.
|
|
28
|
+
// Inlining getDepth avoids indirect function calls in V8's lower JIT tiers.
|
|
29
|
+
function decideParentDepth(a, b, c) {
|
|
30
|
+
let da = a == null ? 0 : a.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : a.depth;
|
|
31
|
+
let db = b == null ? 0 : b.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : b.depth;
|
|
32
|
+
let d = da > db ? da : db;
|
|
33
|
+
if (c != null) {
|
|
34
|
+
let dc = c.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : c.depth;
|
|
35
|
+
if (dc > d)
|
|
36
|
+
d = dc;
|
|
37
|
+
}
|
|
38
|
+
return d + 1;
|
|
31
39
|
}
|
|
32
40
|
export function makeTernaryTreeList(size, offset, xs) {
|
|
33
41
|
switch (size) {
|
|
@@ -47,7 +55,8 @@ export function makeTernaryTreeList(size, offset, xs) {
|
|
|
47
55
|
right: emptyBranch,
|
|
48
56
|
depth: decideParentDepth(left, middle),
|
|
49
57
|
};
|
|
50
|
-
|
|
58
|
+
if (_devMode)
|
|
59
|
+
checkListStructure(result);
|
|
51
60
|
return result;
|
|
52
61
|
}
|
|
53
62
|
case 3: {
|
|
@@ -62,14 +71,20 @@ export function makeTernaryTreeList(size, offset, xs) {
|
|
|
62
71
|
right: right,
|
|
63
72
|
depth: decideParentDepth(left, middle, right),
|
|
64
73
|
};
|
|
65
|
-
|
|
74
|
+
if (_devMode)
|
|
75
|
+
checkListStructure(result);
|
|
66
76
|
return result;
|
|
67
77
|
}
|
|
68
78
|
default: {
|
|
69
|
-
|
|
70
|
-
let
|
|
71
|
-
let
|
|
72
|
-
let
|
|
79
|
+
// Inline divideTernarySizes to avoid heap object allocation per recursive call
|
|
80
|
+
let extra = size % 3;
|
|
81
|
+
let groupSize = (size / 3) | 0;
|
|
82
|
+
let leftSize = groupSize + (extra === 2 ? 1 : 0);
|
|
83
|
+
let middleSize = groupSize + (extra === 1 ? 1 : 0);
|
|
84
|
+
let rightSize = groupSize + (extra === 2 ? 1 : 0);
|
|
85
|
+
let left = makeTernaryTreeList(leftSize, offset, xs);
|
|
86
|
+
let middle = makeTernaryTreeList(middleSize, offset + leftSize, xs);
|
|
87
|
+
let right = makeTernaryTreeList(rightSize, offset + leftSize + middleSize, xs);
|
|
73
88
|
let result = {
|
|
74
89
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
75
90
|
size: left.size + middle.size + right.size,
|
|
@@ -78,7 +93,8 @@ export function makeTernaryTreeList(size, offset, xs) {
|
|
|
78
93
|
middle: middle,
|
|
79
94
|
right: right,
|
|
80
95
|
};
|
|
81
|
-
|
|
96
|
+
if (_devMode)
|
|
97
|
+
checkListStructure(result);
|
|
82
98
|
return result;
|
|
83
99
|
}
|
|
84
100
|
}
|
|
@@ -134,34 +150,26 @@ export function formatListInline(tree) {
|
|
|
134
150
|
}
|
|
135
151
|
}
|
|
136
152
|
export function* listToItems(tree) {
|
|
137
|
-
if (tree
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
if (right != null) {
|
|
159
|
-
for (let x of listToItems(right)) {
|
|
160
|
-
yield x;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
153
|
+
if (tree == null)
|
|
154
|
+
return;
|
|
155
|
+
// Iterative pre-order traversal using an explicit stack — avoids
|
|
156
|
+
// O(n) nested generator delegation overhead of the recursive version.
|
|
157
|
+
let stack = [tree];
|
|
158
|
+
while (stack.length > 0) {
|
|
159
|
+
let node = stack.pop();
|
|
160
|
+
if (node == null || node.size === 0)
|
|
161
|
+
continue;
|
|
162
|
+
if (node.kind === TernaryTreeKind.ternaryTreeLeaf) {
|
|
163
|
+
yield node.value;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// Push right first so left is popped (processed) first (LIFO order).
|
|
167
|
+
if (node.right != null && node.right.size > 0)
|
|
168
|
+
stack.push(node.right);
|
|
169
|
+
if (node.middle != null && node.middle.size > 0)
|
|
170
|
+
stack.push(node.middle);
|
|
171
|
+
if (node.left != null && node.left.size > 0)
|
|
172
|
+
stack.push(node.left);
|
|
165
173
|
}
|
|
166
174
|
}
|
|
167
175
|
}
|
|
@@ -307,7 +315,7 @@ export function listGet(originalTree, originalIdx) {
|
|
|
307
315
|
const leftSize = left == null ? 0 : left.size;
|
|
308
316
|
const middleSize = middle == null ? 0 : middle.size;
|
|
309
317
|
const rightSize = right == null ? 0 : right.size;
|
|
310
|
-
if (leftSize + middleSize + rightSize !== tree.size) {
|
|
318
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
311
319
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
312
320
|
}
|
|
313
321
|
if (idx <= leftSize - 1) {
|
|
@@ -362,7 +370,7 @@ export function assocList(tree, idx, item) {
|
|
|
362
370
|
const leftSize = left == null ? 0 : left.size;
|
|
363
371
|
const middleSize = middle == null ? 0 : middle.size;
|
|
364
372
|
const rightSize = right == null ? 0 : right.size;
|
|
365
|
-
if (leftSize + middleSize + rightSize !== tree.size)
|
|
373
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size)
|
|
366
374
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
367
375
|
if (idx <= leftSize - 1) {
|
|
368
376
|
let changedBranch = assocList(left, idx, item);
|
|
@@ -374,7 +382,8 @@ export function assocList(tree, idx, item) {
|
|
|
374
382
|
middle: middle,
|
|
375
383
|
right: right,
|
|
376
384
|
};
|
|
377
|
-
|
|
385
|
+
if (_devMode)
|
|
386
|
+
checkListStructure(result);
|
|
378
387
|
return result;
|
|
379
388
|
}
|
|
380
389
|
else if (idx <= leftSize + middleSize - 1) {
|
|
@@ -387,7 +396,8 @@ export function assocList(tree, idx, item) {
|
|
|
387
396
|
middle: changedBranch,
|
|
388
397
|
right: right,
|
|
389
398
|
};
|
|
390
|
-
|
|
399
|
+
if (_devMode)
|
|
400
|
+
checkListStructure(result);
|
|
391
401
|
return result;
|
|
392
402
|
}
|
|
393
403
|
else {
|
|
@@ -400,7 +410,8 @@ export function assocList(tree, idx, item) {
|
|
|
400
410
|
middle: middle,
|
|
401
411
|
right: changedBranch,
|
|
402
412
|
};
|
|
403
|
-
|
|
413
|
+
if (_devMode)
|
|
414
|
+
checkListStructure(result);
|
|
404
415
|
return result;
|
|
405
416
|
}
|
|
406
417
|
}
|
|
@@ -430,7 +441,7 @@ export function dissocList(tree, idx) {
|
|
|
430
441
|
const leftSize = left == null ? 0 : left.size;
|
|
431
442
|
const middleSize = middle == null ? 0 : middle.size;
|
|
432
443
|
const rightSize = right == null ? 0 : right.size;
|
|
433
|
-
if (leftSize + middleSize + rightSize !== tree.size) {
|
|
444
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
434
445
|
throw new Error("tree.size does not match sum from branch sizes");
|
|
435
446
|
}
|
|
436
447
|
let result = emptyBranch;
|
|
@@ -463,7 +474,7 @@ export function dissocList(tree, idx) {
|
|
|
463
474
|
result = {
|
|
464
475
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
465
476
|
size: tree.size - 1,
|
|
466
|
-
depth: decideParentDepth(left,
|
|
477
|
+
depth: decideParentDepth(left, right, emptyBranch),
|
|
467
478
|
left: left,
|
|
468
479
|
middle: right,
|
|
469
480
|
right: emptyBranch,
|
|
@@ -497,7 +508,8 @@ export function dissocList(tree, idx) {
|
|
|
497
508
|
if (result.middle == null) {
|
|
498
509
|
return result.left;
|
|
499
510
|
}
|
|
500
|
-
|
|
511
|
+
if (_devMode)
|
|
512
|
+
checkListStructure(result);
|
|
501
513
|
return result;
|
|
502
514
|
}
|
|
503
515
|
export function rest(tree) {
|
|
@@ -535,7 +547,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
535
547
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
536
548
|
right: emptyBranch,
|
|
537
549
|
};
|
|
538
|
-
|
|
550
|
+
if (_devMode)
|
|
551
|
+
checkListStructure(result);
|
|
539
552
|
return result;
|
|
540
553
|
}
|
|
541
554
|
else {
|
|
@@ -547,11 +560,13 @@ export function insert(tree, idx, item, after = false) {
|
|
|
547
560
|
middle: tree,
|
|
548
561
|
right: emptyBranch,
|
|
549
562
|
};
|
|
550
|
-
|
|
563
|
+
if (_devMode)
|
|
564
|
+
checkListStructure(result);
|
|
551
565
|
return result;
|
|
552
566
|
}
|
|
553
567
|
}
|
|
554
|
-
|
|
568
|
+
if (_devMode)
|
|
569
|
+
checkListStructure(tree);
|
|
555
570
|
if (listLen(tree) === 1) {
|
|
556
571
|
if (after) {
|
|
557
572
|
// in compact mode, values placed at left
|
|
@@ -563,7 +578,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
563
578
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
564
579
|
right: emptyBranch,
|
|
565
580
|
};
|
|
566
|
-
|
|
581
|
+
if (_devMode)
|
|
582
|
+
checkListStructure(result);
|
|
567
583
|
return result;
|
|
568
584
|
}
|
|
569
585
|
else {
|
|
@@ -575,7 +591,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
575
591
|
middle: tree.left,
|
|
576
592
|
right: emptyBranch,
|
|
577
593
|
};
|
|
578
|
-
|
|
594
|
+
if (_devMode)
|
|
595
|
+
checkListStructure(result);
|
|
579
596
|
return result;
|
|
580
597
|
}
|
|
581
598
|
}
|
|
@@ -590,7 +607,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
590
607
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
591
608
|
right: tree.middle,
|
|
592
609
|
};
|
|
593
|
-
|
|
610
|
+
if (_devMode)
|
|
611
|
+
checkListStructure(result);
|
|
594
612
|
return result;
|
|
595
613
|
}
|
|
596
614
|
if (idx === 1) {
|
|
@@ -602,7 +620,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
602
620
|
middle: tree.middle,
|
|
603
621
|
right: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
604
622
|
};
|
|
605
|
-
|
|
623
|
+
if (_devMode)
|
|
624
|
+
checkListStructure(result);
|
|
606
625
|
return result;
|
|
607
626
|
}
|
|
608
627
|
else {
|
|
@@ -619,7 +638,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
619
638
|
middle: tree.left,
|
|
620
639
|
right: tree.middle,
|
|
621
640
|
};
|
|
622
|
-
|
|
641
|
+
if (_devMode)
|
|
642
|
+
checkListStructure(result);
|
|
623
643
|
return result;
|
|
624
644
|
}
|
|
625
645
|
else if (idx === 1) {
|
|
@@ -631,7 +651,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
631
651
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
632
652
|
right: tree.middle,
|
|
633
653
|
};
|
|
634
|
-
|
|
654
|
+
if (_devMode)
|
|
655
|
+
checkListStructure(result);
|
|
635
656
|
return result;
|
|
636
657
|
}
|
|
637
658
|
else {
|
|
@@ -646,7 +667,7 @@ export function insert(tree, idx, item, after = false) {
|
|
|
646
667
|
const leftSize = left == null ? 0 : left.size;
|
|
647
668
|
const middleSize = middle == null ? 0 : middle.size;
|
|
648
669
|
const rightSize = right == null ? 0 : right.size;
|
|
649
|
-
if (leftSize + middleSize + rightSize !== tree.size) {
|
|
670
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
650
671
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
651
672
|
}
|
|
652
673
|
// echo "picking: ", idx, " ", leftSize, " ", middleSize, " ", rightSize
|
|
@@ -660,7 +681,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
660
681
|
middle: tree,
|
|
661
682
|
right: emptyBranch,
|
|
662
683
|
};
|
|
663
|
-
|
|
684
|
+
if (_devMode)
|
|
685
|
+
checkListStructure(result);
|
|
664
686
|
return result;
|
|
665
687
|
}
|
|
666
688
|
}
|
|
@@ -674,7 +696,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
674
696
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
675
697
|
right: emptyBranch,
|
|
676
698
|
};
|
|
677
|
-
|
|
699
|
+
if (_devMode)
|
|
700
|
+
checkListStructure(result);
|
|
678
701
|
return result;
|
|
679
702
|
}
|
|
680
703
|
}
|
|
@@ -687,10 +710,11 @@ export function insert(tree, idx, item, after = false) {
|
|
|
687
710
|
middle: tree.middle,
|
|
688
711
|
right: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
689
712
|
};
|
|
690
|
-
|
|
713
|
+
if (_devMode)
|
|
714
|
+
checkListStructure(result);
|
|
691
715
|
return result;
|
|
692
716
|
}
|
|
693
|
-
if (!after && idx === 0 && rightSize === 0 && middleSize >=
|
|
717
|
+
if (!after && idx === 0 && rightSize === 0 && middleSize >= leftSize) {
|
|
694
718
|
let result = {
|
|
695
719
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
696
720
|
size: tree.size + 1,
|
|
@@ -699,7 +723,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
699
723
|
middle: left,
|
|
700
724
|
right: middle,
|
|
701
725
|
};
|
|
702
|
-
|
|
726
|
+
if (_devMode)
|
|
727
|
+
checkListStructure(result);
|
|
703
728
|
return result;
|
|
704
729
|
}
|
|
705
730
|
if (idx <= leftSize - 1) {
|
|
@@ -712,7 +737,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
712
737
|
middle: middle,
|
|
713
738
|
right: right,
|
|
714
739
|
};
|
|
715
|
-
|
|
740
|
+
if (_devMode)
|
|
741
|
+
checkListStructure(result);
|
|
716
742
|
return result;
|
|
717
743
|
}
|
|
718
744
|
else if (idx <= leftSize + middleSize - 1) {
|
|
@@ -725,7 +751,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
725
751
|
middle: changedBranch,
|
|
726
752
|
right: right,
|
|
727
753
|
};
|
|
728
|
-
|
|
754
|
+
if (_devMode)
|
|
755
|
+
checkListStructure(result);
|
|
729
756
|
return result;
|
|
730
757
|
}
|
|
731
758
|
else {
|
|
@@ -738,7 +765,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
738
765
|
middle: middle,
|
|
739
766
|
right: changedBranch,
|
|
740
767
|
};
|
|
741
|
-
|
|
768
|
+
if (_devMode)
|
|
769
|
+
checkListStructure(result);
|
|
742
770
|
return result;
|
|
743
771
|
}
|
|
744
772
|
}
|
|
@@ -806,7 +834,8 @@ export function concat(...xsGroups) {
|
|
|
806
834
|
}
|
|
807
835
|
let result = makeTernaryTreeList(xsGroups.length, 0, xsGroups);
|
|
808
836
|
maybeReblance(result);
|
|
809
|
-
|
|
837
|
+
if (_devMode)
|
|
838
|
+
checkListStructure(result);
|
|
810
839
|
return result;
|
|
811
840
|
}
|
|
812
841
|
export function concat2(left, middle) {
|
|
@@ -844,7 +873,8 @@ export function concat2(left, middle) {
|
|
|
844
873
|
middle: middle,
|
|
845
874
|
right: emptyBranch,
|
|
846
875
|
};
|
|
847
|
-
|
|
876
|
+
if (_devMode)
|
|
877
|
+
checkListStructure(ret);
|
|
848
878
|
return ret;
|
|
849
879
|
}
|
|
850
880
|
export function concat3(left, middle, right) {
|
|
@@ -856,7 +886,8 @@ export function concat3(left, middle, right) {
|
|
|
856
886
|
middle,
|
|
857
887
|
right,
|
|
858
888
|
};
|
|
859
|
-
|
|
889
|
+
if (_devMode)
|
|
890
|
+
checkListStructure(ret);
|
|
860
891
|
return ret;
|
|
861
892
|
}
|
|
862
893
|
export function sameListShape(xs, ys) {
|
package/lib/map.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TernaryTreeKind, hashGenerator, } from "./types.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { cmp, dataEqual } from "./utils.mjs";
|
|
3
3
|
let emptyBranch = null;
|
|
4
4
|
let nilResult = null;
|
|
5
5
|
function getMax(tree) {
|
|
@@ -29,7 +29,6 @@ function getMin(tree) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
export function getMapDepth(tree) {
|
|
32
|
-
// console.log( "calling...", tree)
|
|
33
32
|
if (tree == null) {
|
|
34
33
|
return 0;
|
|
35
34
|
}
|
|
@@ -37,11 +36,21 @@ export function getMapDepth(tree) {
|
|
|
37
36
|
case TernaryTreeKind.ternaryTreeLeaf:
|
|
38
37
|
return 1;
|
|
39
38
|
case TernaryTreeKind.ternaryTreeBranch:
|
|
40
|
-
return
|
|
39
|
+
return tree.depth; // use cached depth — O(1)
|
|
41
40
|
default:
|
|
42
41
|
throw new Error("Unknown");
|
|
43
42
|
}
|
|
44
43
|
}
|
|
44
|
+
function decideMapBranchDepth(left, middle, right) {
|
|
45
|
+
// Inline getMapDepth to avoid 3 indirect function calls per invocation
|
|
46
|
+
let dl = left == null ? 0 : left.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : left.depth;
|
|
47
|
+
let dm = middle == null ? 0 : middle.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : middle.depth;
|
|
48
|
+
let dr = right == null ? 0 : right.kind === TernaryTreeKind.ternaryTreeLeaf ? 1 : right.depth;
|
|
49
|
+
let d = dl > dm ? dl : dm;
|
|
50
|
+
if (dr > d)
|
|
51
|
+
d = dr;
|
|
52
|
+
return d + 1;
|
|
53
|
+
}
|
|
45
54
|
function createLeaf(k, v) {
|
|
46
55
|
let result = {
|
|
47
56
|
kind: TernaryTreeKind.ternaryTreeLeaf,
|
|
@@ -81,7 +90,8 @@ function makeTernaryTreeMap(size, offset, xs) {
|
|
|
81
90
|
left: createLeafFromHashEntry(leftPair),
|
|
82
91
|
middle: createLeafFromHashEntry(middlePair),
|
|
83
92
|
right: emptyBranch,
|
|
84
|
-
depth:
|
|
93
|
+
depth: 2,
|
|
94
|
+
size: leftPair.pairs.length + middlePair.pairs.length,
|
|
85
95
|
};
|
|
86
96
|
return result;
|
|
87
97
|
}
|
|
@@ -96,15 +106,21 @@ function makeTernaryTreeMap(size, offset, xs) {
|
|
|
96
106
|
left: createLeafFromHashEntry(leftPair),
|
|
97
107
|
middle: createLeafFromHashEntry(middlePair),
|
|
98
108
|
right: createLeafFromHashEntry(rightPair),
|
|
99
|
-
depth:
|
|
109
|
+
depth: 2,
|
|
110
|
+
size: leftPair.pairs.length + middlePair.pairs.length + rightPair.pairs.length,
|
|
100
111
|
};
|
|
101
112
|
return result;
|
|
102
113
|
}
|
|
103
114
|
default: {
|
|
104
|
-
|
|
105
|
-
let
|
|
106
|
-
let
|
|
107
|
-
let
|
|
115
|
+
// Inline divideTernarySizes to avoid heap object allocation per recursive call
|
|
116
|
+
let extra = size % 3;
|
|
117
|
+
let groupSize = (size / 3) | 0;
|
|
118
|
+
let leftSize = groupSize + (extra === 2 ? 1 : 0);
|
|
119
|
+
let middleSize = groupSize + (extra === 1 ? 1 : 0);
|
|
120
|
+
let rightSize = groupSize + (extra === 2 ? 1 : 0);
|
|
121
|
+
let left = makeTernaryTreeMap(leftSize, offset, xs);
|
|
122
|
+
let middle = makeTernaryTreeMap(middleSize, offset + leftSize, xs);
|
|
123
|
+
let right = makeTernaryTreeMap(rightSize, offset + leftSize + middleSize, xs);
|
|
108
124
|
let result = {
|
|
109
125
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
110
126
|
maxHash: getMax(right),
|
|
@@ -112,7 +128,8 @@ function makeTernaryTreeMap(size, offset, xs) {
|
|
|
112
128
|
left: left,
|
|
113
129
|
middle: middle,
|
|
114
130
|
right: right,
|
|
115
|
-
depth:
|
|
131
|
+
depth: decideMapBranchDepth(left, middle, right),
|
|
132
|
+
size: mapLen(left) + mapLen(middle) + mapLen(right),
|
|
116
133
|
};
|
|
117
134
|
return result;
|
|
118
135
|
}
|
|
@@ -202,7 +219,7 @@ export function mapLen(tree) {
|
|
|
202
219
|
case TernaryTreeKind.ternaryTreeLeaf:
|
|
203
220
|
return tree.elements.length;
|
|
204
221
|
case TernaryTreeKind.ternaryTreeBranch:
|
|
205
|
-
return
|
|
222
|
+
return tree.size; // O(1) via cached size field
|
|
206
223
|
default:
|
|
207
224
|
throw new Error("Unknown");
|
|
208
225
|
}
|
|
@@ -264,13 +281,13 @@ export function isMapEmpty(tree) {
|
|
|
264
281
|
}
|
|
265
282
|
export function isMapOfOne(tree, counted = 0) {
|
|
266
283
|
if (tree == null) {
|
|
267
|
-
return
|
|
284
|
+
return false;
|
|
268
285
|
}
|
|
269
286
|
switch (tree.kind) {
|
|
270
287
|
case TernaryTreeKind.ternaryTreeLeaf:
|
|
271
|
-
return
|
|
288
|
+
return tree.elements.length === 1;
|
|
272
289
|
case TernaryTreeKind.ternaryTreeBranch:
|
|
273
|
-
return tree
|
|
290
|
+
return mapLenBound(tree, 2) === 1;
|
|
274
291
|
default:
|
|
275
292
|
throw new Error("Unknown");
|
|
276
293
|
}
|
|
@@ -534,7 +551,7 @@ export function checkMapStructure(tree) {
|
|
|
534
551
|
throw new Error(`Bad hash at leaf node ${tree}`);
|
|
535
552
|
}
|
|
536
553
|
}
|
|
537
|
-
if (mapLenBound(tree, 2)
|
|
554
|
+
if (mapLenBound(tree, 2) < 1) {
|
|
538
555
|
throw new Error(`Bad len at leaf node ${tree}`);
|
|
539
556
|
}
|
|
540
557
|
}
|
|
@@ -629,46 +646,49 @@ function assocExisted(tree, key, item, thisHash = null) {
|
|
|
629
646
|
throw new Error("Unexpected missing hash in assoc, found not branch");
|
|
630
647
|
}
|
|
631
648
|
if (rangeContainsHash(tree.left, thisHash)) {
|
|
632
|
-
let
|
|
649
|
+
let newLeft = assocExisted(tree.left, key, item, thisHash);
|
|
650
|
+
return {
|
|
633
651
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
634
652
|
maxHash: tree.maxHash,
|
|
635
653
|
minHash: tree.minHash,
|
|
636
|
-
left:
|
|
654
|
+
left: newLeft,
|
|
637
655
|
middle: tree.middle,
|
|
638
656
|
right: tree.right,
|
|
639
|
-
depth:
|
|
657
|
+
depth: decideMapBranchDepth(newLeft, tree.middle, tree.right),
|
|
658
|
+
size: tree.size,
|
|
640
659
|
};
|
|
641
|
-
return result;
|
|
642
660
|
}
|
|
643
661
|
if (tree.middle == null) {
|
|
644
662
|
throw new Error("Unexpected missing hash in assoc, found not branch");
|
|
645
663
|
}
|
|
646
664
|
if (rangeContainsHash(tree.middle, thisHash)) {
|
|
647
|
-
let
|
|
665
|
+
let newMiddle = assocExisted(tree.middle, key, item, thisHash);
|
|
666
|
+
return {
|
|
648
667
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
649
668
|
maxHash: tree.maxHash,
|
|
650
669
|
minHash: tree.minHash,
|
|
651
670
|
left: tree.left,
|
|
652
|
-
middle:
|
|
671
|
+
middle: newMiddle,
|
|
653
672
|
right: tree.right,
|
|
654
|
-
depth:
|
|
673
|
+
depth: decideMapBranchDepth(tree.left, newMiddle, tree.right),
|
|
674
|
+
size: tree.size,
|
|
655
675
|
};
|
|
656
|
-
return result;
|
|
657
676
|
}
|
|
658
677
|
if (tree.right == null) {
|
|
659
678
|
throw new Error("Unexpected missing hash in assoc, found not branch");
|
|
660
679
|
}
|
|
661
680
|
if (rangeContainsHash(tree.right, thisHash)) {
|
|
662
|
-
let
|
|
681
|
+
let newRight = assocExisted(tree.right, key, item, thisHash);
|
|
682
|
+
return {
|
|
663
683
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
664
684
|
maxHash: tree.maxHash,
|
|
665
685
|
minHash: tree.minHash,
|
|
666
686
|
left: tree.left,
|
|
667
687
|
middle: tree.middle,
|
|
668
|
-
right:
|
|
669
|
-
depth:
|
|
688
|
+
right: newRight,
|
|
689
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, newRight),
|
|
690
|
+
size: tree.size,
|
|
670
691
|
};
|
|
671
|
-
return result;
|
|
672
692
|
}
|
|
673
693
|
throw new Error("Unexpected missing hash in assoc, found not branch");
|
|
674
694
|
}
|
|
@@ -692,7 +712,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
692
712
|
left: tree,
|
|
693
713
|
middle: childBranch,
|
|
694
714
|
right: emptyBranch,
|
|
695
|
-
depth:
|
|
715
|
+
depth: decideMapBranchDepth(tree, childBranch, emptyBranch),
|
|
716
|
+
size: mapLen(tree) + 1,
|
|
696
717
|
};
|
|
697
718
|
return result;
|
|
698
719
|
}
|
|
@@ -709,7 +730,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
709
730
|
left: childBranch,
|
|
710
731
|
middle: tree,
|
|
711
732
|
right: emptyBranch,
|
|
712
|
-
depth:
|
|
733
|
+
depth: decideMapBranchDepth(childBranch, tree, emptyBranch),
|
|
734
|
+
size: mapLen(tree) + 1,
|
|
713
735
|
};
|
|
714
736
|
return result;
|
|
715
737
|
}
|
|
@@ -750,7 +772,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
750
772
|
left: childBranch,
|
|
751
773
|
middle: tree.left,
|
|
752
774
|
right: tree.middle,
|
|
753
|
-
depth:
|
|
775
|
+
depth: decideMapBranchDepth(childBranch, tree.left, tree.middle),
|
|
776
|
+
size: tree.size + 1,
|
|
754
777
|
};
|
|
755
778
|
return result;
|
|
756
779
|
}
|
|
@@ -767,7 +790,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
767
790
|
left: childBranch,
|
|
768
791
|
middle: tree,
|
|
769
792
|
right: emptyBranch,
|
|
770
|
-
depth:
|
|
793
|
+
depth: decideMapBranchDepth(childBranch, tree, emptyBranch),
|
|
794
|
+
size: tree.size + 1,
|
|
771
795
|
};
|
|
772
796
|
return result;
|
|
773
797
|
}
|
|
@@ -787,7 +811,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
787
811
|
left: tree.left,
|
|
788
812
|
middle: childBranch,
|
|
789
813
|
right: emptyBranch,
|
|
790
|
-
depth:
|
|
814
|
+
depth: decideMapBranchDepth(tree.left, childBranch, emptyBranch),
|
|
815
|
+
size: tree.size + 1,
|
|
791
816
|
};
|
|
792
817
|
return result;
|
|
793
818
|
}
|
|
@@ -804,7 +829,8 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
804
829
|
left: tree.left,
|
|
805
830
|
middle: tree.middle,
|
|
806
831
|
right: childBranch,
|
|
807
|
-
depth:
|
|
832
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, childBranch),
|
|
833
|
+
size: tree.size + 1,
|
|
808
834
|
};
|
|
809
835
|
return result;
|
|
810
836
|
}
|
|
@@ -821,73 +847,79 @@ function assocNew(tree, key, item, thisHash = null) {
|
|
|
821
847
|
left: tree,
|
|
822
848
|
middle: childBranch,
|
|
823
849
|
right: emptyBranch,
|
|
824
|
-
depth:
|
|
850
|
+
depth: decideMapBranchDepth(tree, childBranch, emptyBranch),
|
|
851
|
+
size: tree.size + 1,
|
|
825
852
|
};
|
|
826
853
|
return result;
|
|
827
854
|
}
|
|
828
855
|
}
|
|
829
856
|
if (rangeContainsHash(tree.left, thisHash)) {
|
|
830
|
-
let
|
|
857
|
+
let newLeft = assocNew(tree.left, key, item, thisHash);
|
|
858
|
+
return {
|
|
831
859
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
832
860
|
maxHash: tree.maxHash,
|
|
833
861
|
minHash: tree.minHash,
|
|
834
|
-
left:
|
|
862
|
+
left: newLeft,
|
|
835
863
|
middle: tree.middle,
|
|
836
864
|
right: tree.right,
|
|
837
|
-
depth:
|
|
865
|
+
depth: decideMapBranchDepth(newLeft, tree.middle, tree.right),
|
|
866
|
+
size: tree.size + 1,
|
|
838
867
|
};
|
|
839
|
-
return result;
|
|
840
868
|
}
|
|
841
869
|
if (rangeContainsHash(tree.middle, thisHash)) {
|
|
842
|
-
let
|
|
870
|
+
let newMiddle = assocNew(tree.middle, key, item, thisHash);
|
|
871
|
+
return {
|
|
843
872
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
844
873
|
maxHash: tree.maxHash,
|
|
845
874
|
minHash: tree.minHash,
|
|
846
875
|
left: tree.left,
|
|
847
|
-
middle:
|
|
876
|
+
middle: newMiddle,
|
|
848
877
|
right: tree.right,
|
|
849
|
-
depth:
|
|
878
|
+
depth: decideMapBranchDepth(tree.left, newMiddle, tree.right),
|
|
879
|
+
size: tree.size + 1,
|
|
850
880
|
};
|
|
851
|
-
return result;
|
|
852
881
|
}
|
|
853
882
|
if (rangeContainsHash(tree.right, thisHash)) {
|
|
854
|
-
let
|
|
883
|
+
let newRight = assocNew(tree.right, key, item, thisHash);
|
|
884
|
+
return {
|
|
855
885
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
856
886
|
maxHash: tree.maxHash,
|
|
857
887
|
minHash: tree.minHash,
|
|
858
888
|
left: tree.left,
|
|
859
889
|
middle: tree.middle,
|
|
860
|
-
right:
|
|
861
|
-
depth:
|
|
890
|
+
right: newRight,
|
|
891
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, newRight),
|
|
892
|
+
size: tree.size + 1,
|
|
862
893
|
};
|
|
863
|
-
return result;
|
|
864
894
|
}
|
|
865
895
|
if (tree.middle == null) {
|
|
866
896
|
throw new Error("unreachable. if inside range, then middle should be here");
|
|
867
897
|
}
|
|
868
898
|
if (thisHash < getMin(tree.middle)) {
|
|
869
|
-
let
|
|
899
|
+
let newLeft = assocNew(tree.left, key, item, thisHash);
|
|
900
|
+
return {
|
|
870
901
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
871
902
|
maxHash: tree.maxHash,
|
|
872
903
|
minHash: tree.minHash,
|
|
873
|
-
left:
|
|
904
|
+
left: newLeft,
|
|
874
905
|
middle: tree.middle,
|
|
875
906
|
right: tree.right,
|
|
876
|
-
depth:
|
|
907
|
+
depth: decideMapBranchDepth(newLeft, tree.middle, tree.right),
|
|
908
|
+
size: tree.size + 1,
|
|
877
909
|
};
|
|
878
|
-
return result;
|
|
879
910
|
}
|
|
880
911
|
else {
|
|
881
|
-
let
|
|
912
|
+
let newRight = assocNew(tree.right, key, item, thisHash);
|
|
913
|
+
return {
|
|
882
914
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
883
915
|
maxHash: tree.maxHash,
|
|
884
916
|
minHash: tree.minHash,
|
|
885
917
|
left: tree.left,
|
|
886
918
|
middle: tree.middle,
|
|
887
|
-
right:
|
|
888
|
-
depth:
|
|
919
|
+
right: newRight,
|
|
920
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, newRight),
|
|
921
|
+
size: tree.size + 1,
|
|
889
922
|
};
|
|
890
|
-
return result;
|
|
891
923
|
}
|
|
892
924
|
}
|
|
893
925
|
}
|
|
@@ -948,7 +980,8 @@ function dissocExisted(tree, key) {
|
|
|
948
980
|
left: tree.middle,
|
|
949
981
|
middle: tree.right,
|
|
950
982
|
right: emptyBranch,
|
|
951
|
-
depth:
|
|
983
|
+
depth: decideMapBranchDepth(tree.middle, tree.right, emptyBranch),
|
|
984
|
+
size: tree.size - 1,
|
|
952
985
|
};
|
|
953
986
|
return result;
|
|
954
987
|
}
|
|
@@ -960,7 +993,8 @@ function dissocExisted(tree, key) {
|
|
|
960
993
|
left: changedBranch,
|
|
961
994
|
middle: tree.middle,
|
|
962
995
|
right: tree.right,
|
|
963
|
-
depth:
|
|
996
|
+
depth: decideMapBranchDepth(changedBranch, tree.middle, tree.right),
|
|
997
|
+
size: tree.size - 1,
|
|
964
998
|
};
|
|
965
999
|
return result;
|
|
966
1000
|
}
|
|
@@ -978,7 +1012,8 @@ function dissocExisted(tree, key) {
|
|
|
978
1012
|
left: tree.left,
|
|
979
1013
|
middle: tree.right,
|
|
980
1014
|
right: emptyBranch,
|
|
981
|
-
depth:
|
|
1015
|
+
depth: decideMapBranchDepth(tree.left, tree.right, emptyBranch),
|
|
1016
|
+
size: tree.size - 1,
|
|
982
1017
|
};
|
|
983
1018
|
return result;
|
|
984
1019
|
}
|
|
@@ -990,7 +1025,8 @@ function dissocExisted(tree, key) {
|
|
|
990
1025
|
left: tree.left,
|
|
991
1026
|
middle: changedBranch,
|
|
992
1027
|
right: tree.right,
|
|
993
|
-
depth:
|
|
1028
|
+
depth: decideMapBranchDepth(tree.left, changedBranch, tree.right),
|
|
1029
|
+
size: tree.size - 1,
|
|
994
1030
|
};
|
|
995
1031
|
return result;
|
|
996
1032
|
}
|
|
@@ -1005,7 +1041,8 @@ function dissocExisted(tree, key) {
|
|
|
1005
1041
|
left: tree.left,
|
|
1006
1042
|
middle: tree.middle,
|
|
1007
1043
|
right: emptyBranch,
|
|
1008
|
-
depth:
|
|
1044
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, emptyBranch),
|
|
1045
|
+
size: tree.size - 1,
|
|
1009
1046
|
};
|
|
1010
1047
|
return result;
|
|
1011
1048
|
}
|
|
@@ -1017,7 +1054,8 @@ function dissocExisted(tree, key) {
|
|
|
1017
1054
|
left: tree.left,
|
|
1018
1055
|
middle: tree.middle,
|
|
1019
1056
|
right: changedBranch,
|
|
1020
|
-
depth:
|
|
1057
|
+
depth: decideMapBranchDepth(tree.left, tree.middle, changedBranch),
|
|
1058
|
+
size: tree.size - 1,
|
|
1021
1059
|
};
|
|
1022
1060
|
return result;
|
|
1023
1061
|
}
|
|
@@ -1298,6 +1336,7 @@ export function mapMapValues(tree, f) {
|
|
|
1298
1336
|
left: mapMapValues(tree.left, f),
|
|
1299
1337
|
middle: mapMapValues(tree.middle, f),
|
|
1300
1338
|
right: mapMapValues(tree.right, f),
|
|
1339
|
+
size: tree.size,
|
|
1301
1340
|
};
|
|
1302
1341
|
return result;
|
|
1303
1342
|
}
|
package/lib/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/ternary-tree",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"main": "./lib/index.mjs",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc",
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
"test:list": "TARGET=list yarn test",
|
|
10
10
|
"test:map": "TARGET=map yarn test",
|
|
11
11
|
"test:list-perf": "yarn build && node lib/test-list-perf.mjs",
|
|
12
|
-
"test:list-detailed-perf": "yarn build && node lib/test-list-detailed-perf.mjs"
|
|
12
|
+
"test:list-detailed-perf": "yarn build && node lib/test-list-detailed-perf.mjs",
|
|
13
|
+
"bench": "tsc && node lib/bench.mjs"
|
|
13
14
|
},
|
|
14
15
|
"devDependencies": {
|
|
15
|
-
"@types/node": "
|
|
16
|
-
"prettier": "
|
|
17
|
-
"typescript": "
|
|
16
|
+
"@types/node": "*",
|
|
17
|
+
"prettier": "*",
|
|
18
|
+
"typescript": "*"
|
|
18
19
|
}
|
|
19
20
|
}
|