@calcit/ternary-tree 0.0.24 → 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/README.md +24 -4
- package/TESTING.md +155 -0
- package/lib/bench.d.mts +11 -0
- package/lib/bench.mjs +99 -0
- package/lib/benchmark.d.mts +1 -0
- package/lib/benchmark.mjs +67 -0
- package/lib/list.d.mts +1 -0
- package/lib/list.mjs +265 -188
- package/lib/map.mjs +223 -161
- package/lib/test-list-detailed-perf.d.mts +1 -0
- package/lib/test-list-detailed-perf.mjs +165 -0
- package/lib/test-list-perf.d.mts +1 -0
- package/lib/test-list-perf.mjs +153 -0
- package/lib/test-list.mjs +202 -203
- package/lib/test-map-perf.d.mts +1 -0
- package/lib/test-map-perf.mjs +70 -0
- package/lib/test-map.mjs +201 -208
- package/lib/test-utils.d.mts +9 -5
- package/lib/test-utils.mjs +131 -18
- package/lib/{main.mjs → test.mjs} +3 -0
- package/lib/types.d.mts +1 -0
- package/package.json +14 -5
- /package/lib/{main.d.mts → test.d.mts} +0 -0
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) {
|
|
@@ -41,13 +49,14 @@ export function makeTernaryTreeList(size, offset, xs) {
|
|
|
41
49
|
let middle = xs[offset + 1];
|
|
42
50
|
let result = {
|
|
43
51
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
44
|
-
size:
|
|
52
|
+
size: left.size + middle.size,
|
|
45
53
|
left: left,
|
|
46
54
|
middle: middle,
|
|
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: {
|
|
@@ -56,50 +65,57 @@ export function makeTernaryTreeList(size, offset, xs) {
|
|
|
56
65
|
let right = xs[offset + 2];
|
|
57
66
|
let result = {
|
|
58
67
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
59
|
-
size:
|
|
68
|
+
size: left.size + middle.size + right.size,
|
|
60
69
|
left: left,
|
|
61
70
|
middle: middle,
|
|
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
|
-
size:
|
|
90
|
+
size: left.size + middle.size + right.size,
|
|
76
91
|
depth: decideParentDepth(left, middle, right),
|
|
77
92
|
left: left,
|
|
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
|
}
|
|
85
101
|
}
|
|
86
102
|
export function initTernaryTreeList(xs) {
|
|
87
|
-
|
|
88
|
-
let
|
|
103
|
+
const size = xs.length;
|
|
104
|
+
let ys = new Array(size);
|
|
105
|
+
// Use cached size instead of accessing xs.length repeatedly
|
|
89
106
|
for (let idx = 0; idx < size; idx++) {
|
|
90
|
-
|
|
91
|
-
ys[idx] = { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: x };
|
|
107
|
+
ys[idx] = { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: xs[idx] };
|
|
92
108
|
}
|
|
93
|
-
return makeTernaryTreeList(
|
|
109
|
+
return makeTernaryTreeList(size, 0, ys);
|
|
94
110
|
}
|
|
95
111
|
// from a slice of an existed array
|
|
96
112
|
export function initTernaryTreeListFromRange(xs, from, to) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
ys[idx
|
|
113
|
+
const length = to - from;
|
|
114
|
+
let ys = new Array(length);
|
|
115
|
+
for (let idx = 0; idx < length; idx++) {
|
|
116
|
+
ys[idx] = { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: xs[idx + from] };
|
|
101
117
|
}
|
|
102
|
-
return makeTernaryTreeList(
|
|
118
|
+
return makeTernaryTreeList(length, 0, ys);
|
|
103
119
|
}
|
|
104
120
|
export function initEmptyTernaryTreeList() {
|
|
105
121
|
return { kind: TernaryTreeKind.ternaryTreeBranch, size: 0, depth: 1, middle: emptyBranch, left: emptyBranch, right: emptyBranch };
|
|
@@ -134,30 +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
|
-
}
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
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);
|
|
161
173
|
}
|
|
162
174
|
}
|
|
163
175
|
}
|
|
@@ -176,17 +188,21 @@ export function findIndex(tree, f) {
|
|
|
176
188
|
}
|
|
177
189
|
}
|
|
178
190
|
case TernaryTreeKind.ternaryTreeBranch: {
|
|
179
|
-
|
|
191
|
+
// Cache children to avoid repeated property access
|
|
192
|
+
const left = tree.left;
|
|
193
|
+
const middle = tree.middle;
|
|
194
|
+
const right = tree.right;
|
|
195
|
+
let tryLeft = findIndex(left, f);
|
|
180
196
|
if (tryLeft >= 0) {
|
|
181
197
|
return tryLeft;
|
|
182
198
|
}
|
|
183
|
-
let tryMiddle = findIndex(
|
|
199
|
+
let tryMiddle = findIndex(middle, f);
|
|
184
200
|
if (tryMiddle >= 0) {
|
|
185
|
-
return tryMiddle +
|
|
201
|
+
return tryMiddle + (left == null ? 0 : left.size);
|
|
186
202
|
}
|
|
187
|
-
let tryRight = findIndex(
|
|
203
|
+
let tryRight = findIndex(right, f);
|
|
188
204
|
if (tryRight >= 0) {
|
|
189
|
-
return tryRight +
|
|
205
|
+
return tryRight + (left == null ? 0 : left.size) + (middle == null ? 0 : middle.size);
|
|
190
206
|
}
|
|
191
207
|
return -1;
|
|
192
208
|
}
|
|
@@ -205,17 +221,21 @@ export function indexOf(tree, item) {
|
|
|
205
221
|
default:
|
|
206
222
|
return -1;
|
|
207
223
|
case TernaryTreeKind.ternaryTreeBranch:
|
|
208
|
-
|
|
224
|
+
// Cache children to avoid repeated property access
|
|
225
|
+
const left = tree.left;
|
|
226
|
+
const middle = tree.middle;
|
|
227
|
+
const right = tree.right;
|
|
228
|
+
let tryLeft = indexOf(left, item);
|
|
209
229
|
if (tryLeft >= 0) {
|
|
210
230
|
return tryLeft;
|
|
211
231
|
}
|
|
212
|
-
let tryMiddle = indexOf(
|
|
232
|
+
let tryMiddle = indexOf(middle, item);
|
|
213
233
|
if (tryMiddle >= 0) {
|
|
214
|
-
return tryMiddle +
|
|
234
|
+
return tryMiddle + (left == null ? 0 : left.size);
|
|
215
235
|
}
|
|
216
|
-
let tryRight = indexOf(
|
|
236
|
+
let tryRight = indexOf(right, item);
|
|
217
237
|
if (tryRight >= 0) {
|
|
218
|
-
return tryRight +
|
|
238
|
+
return tryRight + (left == null ? 0 : left.size) + (middle == null ? 0 : middle.size);
|
|
219
239
|
}
|
|
220
240
|
return -1;
|
|
221
241
|
}
|
|
@@ -227,25 +247,28 @@ function writeLeavesArray(tree, acc, idx) {
|
|
|
227
247
|
else {
|
|
228
248
|
switch (tree.kind) {
|
|
229
249
|
case TernaryTreeKind.ternaryTreeLeaf: {
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
// Cache current index to reduce property access
|
|
251
|
+
const currentIdx = idx.value;
|
|
252
|
+
acc[currentIdx] = tree;
|
|
253
|
+
idx.value = currentIdx + 1;
|
|
232
254
|
break;
|
|
233
255
|
}
|
|
234
256
|
case TernaryTreeKind.ternaryTreeBranch: {
|
|
235
|
-
|
|
236
|
-
|
|
257
|
+
// Cache children to avoid repeated property access
|
|
258
|
+
const left = tree.left;
|
|
259
|
+
const middle = tree.middle;
|
|
260
|
+
const right = tree.right;
|
|
261
|
+
if (left != null) {
|
|
262
|
+
writeLeavesArray(left, acc, idx);
|
|
237
263
|
}
|
|
238
|
-
if (
|
|
239
|
-
writeLeavesArray(
|
|
264
|
+
if (middle != null) {
|
|
265
|
+
writeLeavesArray(middle, acc, idx);
|
|
240
266
|
}
|
|
241
|
-
if (
|
|
242
|
-
writeLeavesArray(
|
|
267
|
+
if (right != null) {
|
|
268
|
+
writeLeavesArray(right, acc, idx);
|
|
243
269
|
}
|
|
244
270
|
break;
|
|
245
271
|
}
|
|
246
|
-
default: {
|
|
247
|
-
throw new Error("Unknown");
|
|
248
|
-
}
|
|
249
272
|
}
|
|
250
273
|
}
|
|
251
274
|
}
|
|
@@ -285,17 +308,21 @@ export function listGet(originalTree, originalIdx) {
|
|
|
285
308
|
if (idx > tree.size - 1) {
|
|
286
309
|
throw new Error("Index too large");
|
|
287
310
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
311
|
+
// Cache child sizes to avoid repeated property access
|
|
312
|
+
const left = tree.left;
|
|
313
|
+
const middle = tree.middle;
|
|
314
|
+
const right = tree.right;
|
|
315
|
+
const leftSize = left == null ? 0 : left.size;
|
|
316
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
317
|
+
const rightSize = right == null ? 0 : right.size;
|
|
318
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
292
319
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
293
320
|
}
|
|
294
321
|
if (idx <= leftSize - 1) {
|
|
295
|
-
tree =
|
|
322
|
+
tree = left;
|
|
296
323
|
}
|
|
297
324
|
else if (idx <= leftSize + middleSize - 1) {
|
|
298
|
-
tree =
|
|
325
|
+
tree = middle;
|
|
299
326
|
idx = idx - leftSize;
|
|
300
327
|
}
|
|
301
328
|
else {
|
|
@@ -336,48 +363,55 @@ export function assocList(tree, idx, item) {
|
|
|
336
363
|
throw new Error(`Cannot get from leaf with index ${idx}`);
|
|
337
364
|
}
|
|
338
365
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
366
|
+
// Cache children and their sizes to avoid repeated property access
|
|
367
|
+
const left = tree.left;
|
|
368
|
+
const middle = tree.middle;
|
|
369
|
+
const right = tree.right;
|
|
370
|
+
const leftSize = left == null ? 0 : left.size;
|
|
371
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
372
|
+
const rightSize = right == null ? 0 : right.size;
|
|
373
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size)
|
|
343
374
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
344
375
|
if (idx <= leftSize - 1) {
|
|
345
|
-
let changedBranch = assocList(
|
|
376
|
+
let changedBranch = assocList(left, idx, item);
|
|
346
377
|
let result = {
|
|
347
378
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
348
379
|
size: tree.size,
|
|
349
|
-
depth: decideParentDepth(changedBranch,
|
|
380
|
+
depth: decideParentDepth(changedBranch, middle, right),
|
|
350
381
|
left: changedBranch,
|
|
351
|
-
middle:
|
|
352
|
-
right:
|
|
382
|
+
middle: middle,
|
|
383
|
+
right: right,
|
|
353
384
|
};
|
|
354
|
-
|
|
385
|
+
if (_devMode)
|
|
386
|
+
checkListStructure(result);
|
|
355
387
|
return result;
|
|
356
388
|
}
|
|
357
389
|
else if (idx <= leftSize + middleSize - 1) {
|
|
358
|
-
let changedBranch = assocList(
|
|
390
|
+
let changedBranch = assocList(middle, idx - leftSize, item);
|
|
359
391
|
let result = {
|
|
360
392
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
361
393
|
size: tree.size,
|
|
362
|
-
depth: decideParentDepth(
|
|
363
|
-
left:
|
|
394
|
+
depth: decideParentDepth(left, changedBranch, right),
|
|
395
|
+
left: left,
|
|
364
396
|
middle: changedBranch,
|
|
365
|
-
right:
|
|
397
|
+
right: right,
|
|
366
398
|
};
|
|
367
|
-
|
|
399
|
+
if (_devMode)
|
|
400
|
+
checkListStructure(result);
|
|
368
401
|
return result;
|
|
369
402
|
}
|
|
370
403
|
else {
|
|
371
|
-
let changedBranch = assocList(
|
|
404
|
+
let changedBranch = assocList(right, idx - leftSize - middleSize, item);
|
|
372
405
|
let result = {
|
|
373
406
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
374
407
|
size: tree.size,
|
|
375
|
-
depth: decideParentDepth(
|
|
376
|
-
left:
|
|
377
|
-
middle:
|
|
408
|
+
depth: decideParentDepth(left, middle, changedBranch),
|
|
409
|
+
left: left,
|
|
410
|
+
middle: middle,
|
|
378
411
|
right: changedBranch,
|
|
379
412
|
};
|
|
380
|
-
|
|
413
|
+
if (_devMode)
|
|
414
|
+
checkListStructure(result);
|
|
381
415
|
return result;
|
|
382
416
|
}
|
|
383
417
|
}
|
|
@@ -400,22 +434,26 @@ export function dissocList(tree, idx) {
|
|
|
400
434
|
if (tree.kind === TernaryTreeKind.ternaryTreeLeaf) {
|
|
401
435
|
throw new Error("dissoc should be handled at branches");
|
|
402
436
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
437
|
+
// Cache children and their sizes to avoid repeated property access
|
|
438
|
+
const left = tree.left;
|
|
439
|
+
const middle = tree.middle;
|
|
440
|
+
const right = tree.right;
|
|
441
|
+
const leftSize = left == null ? 0 : left.size;
|
|
442
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
443
|
+
const rightSize = right == null ? 0 : right.size;
|
|
444
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
407
445
|
throw new Error("tree.size does not match sum from branch sizes");
|
|
408
446
|
}
|
|
409
447
|
let result = emptyBranch;
|
|
410
448
|
if (idx <= leftSize - 1) {
|
|
411
|
-
let changedBranch = dissocList(
|
|
449
|
+
let changedBranch = dissocList(left, idx);
|
|
412
450
|
if (changedBranch == null || changedBranch.size === 0) {
|
|
413
451
|
result = {
|
|
414
452
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
415
453
|
size: tree.size - 1,
|
|
416
|
-
depth: decideParentDepth(
|
|
417
|
-
left:
|
|
418
|
-
middle:
|
|
454
|
+
depth: decideParentDepth(middle, right),
|
|
455
|
+
left: middle,
|
|
456
|
+
middle: right,
|
|
419
457
|
right: emptyBranch,
|
|
420
458
|
};
|
|
421
459
|
}
|
|
@@ -423,22 +461,22 @@ export function dissocList(tree, idx) {
|
|
|
423
461
|
result = {
|
|
424
462
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
425
463
|
size: tree.size - 1,
|
|
426
|
-
depth: decideParentDepth(changedBranch,
|
|
464
|
+
depth: decideParentDepth(changedBranch, middle, right),
|
|
427
465
|
left: changedBranch,
|
|
428
|
-
middle:
|
|
429
|
-
right:
|
|
466
|
+
middle: middle,
|
|
467
|
+
right: right,
|
|
430
468
|
};
|
|
431
469
|
}
|
|
432
470
|
}
|
|
433
471
|
else if (idx <= leftSize + middleSize - 1) {
|
|
434
|
-
let changedBranch = dissocList(
|
|
472
|
+
let changedBranch = dissocList(middle, idx - leftSize);
|
|
435
473
|
if (changedBranch == null || changedBranch.size === 0) {
|
|
436
474
|
result = {
|
|
437
475
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
438
476
|
size: tree.size - 1,
|
|
439
|
-
depth: decideParentDepth(
|
|
440
|
-
left:
|
|
441
|
-
middle:
|
|
477
|
+
depth: decideParentDepth(left, right, emptyBranch),
|
|
478
|
+
left: left,
|
|
479
|
+
middle: right,
|
|
442
480
|
right: emptyBranch,
|
|
443
481
|
};
|
|
444
482
|
}
|
|
@@ -446,31 +484,32 @@ export function dissocList(tree, idx) {
|
|
|
446
484
|
result = {
|
|
447
485
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
448
486
|
size: tree.size - 1,
|
|
449
|
-
depth: decideParentDepth(
|
|
450
|
-
left:
|
|
487
|
+
depth: decideParentDepth(left, changedBranch, right),
|
|
488
|
+
left: left,
|
|
451
489
|
middle: changedBranch,
|
|
452
|
-
right:
|
|
490
|
+
right: right,
|
|
453
491
|
};
|
|
454
492
|
}
|
|
455
493
|
}
|
|
456
494
|
else {
|
|
457
|
-
let changedBranch = dissocList(
|
|
495
|
+
let changedBranch = dissocList(right, idx - leftSize - middleSize);
|
|
458
496
|
if (changedBranch == null || changedBranch.size === 0) {
|
|
459
497
|
changedBranch = emptyBranch;
|
|
460
498
|
}
|
|
461
499
|
result = {
|
|
462
500
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
463
501
|
size: tree.size - 1,
|
|
464
|
-
depth: decideParentDepth(
|
|
465
|
-
left:
|
|
466
|
-
middle:
|
|
502
|
+
depth: decideParentDepth(left, middle, changedBranch),
|
|
503
|
+
left: left,
|
|
504
|
+
middle: middle,
|
|
467
505
|
right: changedBranch,
|
|
468
506
|
};
|
|
469
507
|
}
|
|
470
508
|
if (result.middle == null) {
|
|
471
509
|
return result.left;
|
|
472
510
|
}
|
|
473
|
-
|
|
511
|
+
if (_devMode)
|
|
512
|
+
checkListStructure(result);
|
|
474
513
|
return result;
|
|
475
514
|
}
|
|
476
515
|
export function rest(tree) {
|
|
@@ -508,7 +547,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
508
547
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
509
548
|
right: emptyBranch,
|
|
510
549
|
};
|
|
511
|
-
|
|
550
|
+
if (_devMode)
|
|
551
|
+
checkListStructure(result);
|
|
512
552
|
return result;
|
|
513
553
|
}
|
|
514
554
|
else {
|
|
@@ -520,11 +560,13 @@ export function insert(tree, idx, item, after = false) {
|
|
|
520
560
|
middle: tree,
|
|
521
561
|
right: emptyBranch,
|
|
522
562
|
};
|
|
523
|
-
|
|
563
|
+
if (_devMode)
|
|
564
|
+
checkListStructure(result);
|
|
524
565
|
return result;
|
|
525
566
|
}
|
|
526
567
|
}
|
|
527
|
-
|
|
568
|
+
if (_devMode)
|
|
569
|
+
checkListStructure(tree);
|
|
528
570
|
if (listLen(tree) === 1) {
|
|
529
571
|
if (after) {
|
|
530
572
|
// in compact mode, values placed at left
|
|
@@ -536,7 +578,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
536
578
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
537
579
|
right: emptyBranch,
|
|
538
580
|
};
|
|
539
|
-
|
|
581
|
+
if (_devMode)
|
|
582
|
+
checkListStructure(result);
|
|
540
583
|
return result;
|
|
541
584
|
}
|
|
542
585
|
else {
|
|
@@ -548,7 +591,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
548
591
|
middle: tree.left,
|
|
549
592
|
right: emptyBranch,
|
|
550
593
|
};
|
|
551
|
-
|
|
594
|
+
if (_devMode)
|
|
595
|
+
checkListStructure(result);
|
|
552
596
|
return result;
|
|
553
597
|
}
|
|
554
598
|
}
|
|
@@ -563,7 +607,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
563
607
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
564
608
|
right: tree.middle,
|
|
565
609
|
};
|
|
566
|
-
|
|
610
|
+
if (_devMode)
|
|
611
|
+
checkListStructure(result);
|
|
567
612
|
return result;
|
|
568
613
|
}
|
|
569
614
|
if (idx === 1) {
|
|
@@ -575,7 +620,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
575
620
|
middle: tree.middle,
|
|
576
621
|
right: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
577
622
|
};
|
|
578
|
-
|
|
623
|
+
if (_devMode)
|
|
624
|
+
checkListStructure(result);
|
|
579
625
|
return result;
|
|
580
626
|
}
|
|
581
627
|
else {
|
|
@@ -592,7 +638,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
592
638
|
middle: tree.left,
|
|
593
639
|
right: tree.middle,
|
|
594
640
|
};
|
|
595
|
-
|
|
641
|
+
if (_devMode)
|
|
642
|
+
checkListStructure(result);
|
|
596
643
|
return result;
|
|
597
644
|
}
|
|
598
645
|
else if (idx === 1) {
|
|
@@ -604,7 +651,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
604
651
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
605
652
|
right: tree.middle,
|
|
606
653
|
};
|
|
607
|
-
|
|
654
|
+
if (_devMode)
|
|
655
|
+
checkListStructure(result);
|
|
608
656
|
return result;
|
|
609
657
|
}
|
|
610
658
|
else {
|
|
@@ -612,15 +660,19 @@ export function insert(tree, idx, item, after = false) {
|
|
|
612
660
|
}
|
|
613
661
|
}
|
|
614
662
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
663
|
+
// Cache children and their sizes to avoid repeated property access
|
|
664
|
+
const left = tree.left;
|
|
665
|
+
const middle = tree.middle;
|
|
666
|
+
const right = tree.right;
|
|
667
|
+
const leftSize = left == null ? 0 : left.size;
|
|
668
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
669
|
+
const rightSize = right == null ? 0 : right.size;
|
|
670
|
+
if (_devMode && leftSize + middleSize + rightSize !== tree.size) {
|
|
619
671
|
throw new Error("tree.size does not match sum case branch sizes");
|
|
620
672
|
}
|
|
621
673
|
// echo "picking: ", idx, " ", leftSize, " ", middleSize, " ", rightSize
|
|
622
674
|
if (idx === 0 && !after) {
|
|
623
|
-
if (
|
|
675
|
+
if (leftSize >= middleSize && leftSize >= rightSize) {
|
|
624
676
|
let result = {
|
|
625
677
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
626
678
|
size: tree.size + 1,
|
|
@@ -629,12 +681,13 @@ export function insert(tree, idx, item, after = false) {
|
|
|
629
681
|
middle: tree,
|
|
630
682
|
right: emptyBranch,
|
|
631
683
|
};
|
|
632
|
-
|
|
684
|
+
if (_devMode)
|
|
685
|
+
checkListStructure(result);
|
|
633
686
|
return result;
|
|
634
687
|
}
|
|
635
688
|
}
|
|
636
689
|
if (idx === listLen(tree) - 1 && after) {
|
|
637
|
-
if (
|
|
690
|
+
if (rightSize >= middleSize && rightSize >= leftSize) {
|
|
638
691
|
let result = {
|
|
639
692
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
640
693
|
size: tree.size + 1,
|
|
@@ -643,7 +696,8 @@ export function insert(tree, idx, item, after = false) {
|
|
|
643
696
|
middle: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
644
697
|
right: emptyBranch,
|
|
645
698
|
};
|
|
646
|
-
|
|
699
|
+
if (_devMode)
|
|
700
|
+
checkListStructure(result);
|
|
647
701
|
return result;
|
|
648
702
|
}
|
|
649
703
|
}
|
|
@@ -656,58 +710,63 @@ export function insert(tree, idx, item, after = false) {
|
|
|
656
710
|
middle: tree.middle,
|
|
657
711
|
right: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
658
712
|
};
|
|
659
|
-
|
|
713
|
+
if (_devMode)
|
|
714
|
+
checkListStructure(result);
|
|
660
715
|
return result;
|
|
661
716
|
}
|
|
662
|
-
if (!after && idx === 0 && rightSize === 0 && middleSize >=
|
|
717
|
+
if (!after && idx === 0 && rightSize === 0 && middleSize >= leftSize) {
|
|
663
718
|
let result = {
|
|
664
719
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
665
720
|
size: tree.size + 1,
|
|
666
721
|
depth: tree.depth,
|
|
667
722
|
left: { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: item },
|
|
668
|
-
middle:
|
|
669
|
-
right:
|
|
723
|
+
middle: left,
|
|
724
|
+
right: middle,
|
|
670
725
|
};
|
|
671
|
-
|
|
726
|
+
if (_devMode)
|
|
727
|
+
checkListStructure(result);
|
|
672
728
|
return result;
|
|
673
729
|
}
|
|
674
730
|
if (idx <= leftSize - 1) {
|
|
675
|
-
let changedBranch = insert(
|
|
731
|
+
let changedBranch = insert(left, idx, item, after);
|
|
676
732
|
let result = {
|
|
677
733
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
678
734
|
size: tree.size + 1,
|
|
679
|
-
depth: decideParentDepth(changedBranch,
|
|
735
|
+
depth: decideParentDepth(changedBranch, middle, right),
|
|
680
736
|
left: changedBranch,
|
|
681
|
-
middle:
|
|
682
|
-
right:
|
|
737
|
+
middle: middle,
|
|
738
|
+
right: right,
|
|
683
739
|
};
|
|
684
|
-
|
|
740
|
+
if (_devMode)
|
|
741
|
+
checkListStructure(result);
|
|
685
742
|
return result;
|
|
686
743
|
}
|
|
687
744
|
else if (idx <= leftSize + middleSize - 1) {
|
|
688
|
-
let changedBranch = insert(
|
|
745
|
+
let changedBranch = insert(middle, idx - leftSize, item, after);
|
|
689
746
|
let result = {
|
|
690
747
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
691
748
|
size: tree.size + 1,
|
|
692
|
-
depth: decideParentDepth(
|
|
693
|
-
left:
|
|
749
|
+
depth: decideParentDepth(left, changedBranch, right),
|
|
750
|
+
left: left,
|
|
694
751
|
middle: changedBranch,
|
|
695
|
-
right:
|
|
752
|
+
right: right,
|
|
696
753
|
};
|
|
697
|
-
|
|
754
|
+
if (_devMode)
|
|
755
|
+
checkListStructure(result);
|
|
698
756
|
return result;
|
|
699
757
|
}
|
|
700
758
|
else {
|
|
701
|
-
let changedBranch = insert(
|
|
759
|
+
let changedBranch = insert(right, idx - leftSize - middleSize, item, after);
|
|
702
760
|
let result = {
|
|
703
761
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
704
762
|
size: tree.size + 1,
|
|
705
|
-
depth: decideParentDepth(
|
|
706
|
-
left:
|
|
707
|
-
middle:
|
|
763
|
+
depth: decideParentDepth(left, middle, changedBranch),
|
|
764
|
+
left: left,
|
|
765
|
+
middle: middle,
|
|
708
766
|
right: changedBranch,
|
|
709
767
|
};
|
|
710
|
-
|
|
768
|
+
if (_devMode)
|
|
769
|
+
checkListStructure(result);
|
|
711
770
|
return result;
|
|
712
771
|
}
|
|
713
772
|
}
|
|
@@ -775,7 +834,8 @@ export function concat(...xsGroups) {
|
|
|
775
834
|
}
|
|
776
835
|
let result = makeTernaryTreeList(xsGroups.length, 0, xsGroups);
|
|
777
836
|
maybeReblance(result);
|
|
778
|
-
|
|
837
|
+
if (_devMode)
|
|
838
|
+
checkListStructure(result);
|
|
779
839
|
return result;
|
|
780
840
|
}
|
|
781
841
|
export function concat2(left, middle) {
|
|
@@ -813,7 +873,8 @@ export function concat2(left, middle) {
|
|
|
813
873
|
middle: middle,
|
|
814
874
|
right: emptyBranch,
|
|
815
875
|
};
|
|
816
|
-
|
|
876
|
+
if (_devMode)
|
|
877
|
+
checkListStructure(ret);
|
|
817
878
|
return ret;
|
|
818
879
|
}
|
|
819
880
|
export function concat3(left, middle, right) {
|
|
@@ -825,7 +886,8 @@ export function concat3(left, middle, right) {
|
|
|
825
886
|
middle,
|
|
826
887
|
right,
|
|
827
888
|
};
|
|
828
|
-
|
|
889
|
+
if (_devMode)
|
|
890
|
+
checkListStructure(ret);
|
|
829
891
|
return ret;
|
|
830
892
|
}
|
|
831
893
|
export function sameListShape(xs, ys) {
|
|
@@ -902,22 +964,29 @@ export function checkListStructure(tree) {
|
|
|
902
964
|
if (tree.size >= 6 && tree.depth >= tree.size) {
|
|
903
965
|
throw new Error(`Bad depth at branch ${formatListInline(tree)}`);
|
|
904
966
|
}
|
|
905
|
-
|
|
967
|
+
// Cache children and their sizes to avoid repeated property access
|
|
968
|
+
const left = tree.left;
|
|
969
|
+
const middle = tree.middle;
|
|
970
|
+
const right = tree.right;
|
|
971
|
+
const leftSize = left == null ? 0 : left.size;
|
|
972
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
973
|
+
const rightSize = right == null ? 0 : right.size;
|
|
974
|
+
if (tree.size !== leftSize + middleSize + rightSize) {
|
|
906
975
|
throw new Error(`Bad size at branch ${formatListInline(tree)}`);
|
|
907
976
|
}
|
|
908
|
-
if (
|
|
977
|
+
if (left == null && middle != null) {
|
|
909
978
|
throw new Error("morformed tree");
|
|
910
979
|
}
|
|
911
|
-
if (
|
|
980
|
+
if (middle == null && right != null) {
|
|
912
981
|
throw new Error("morformed tree");
|
|
913
982
|
}
|
|
914
|
-
if (tree.depth !== decideParentDepth(
|
|
915
|
-
let x = decideParentDepth(
|
|
983
|
+
if (tree.depth !== decideParentDepth(left, middle, right)) {
|
|
984
|
+
let x = decideParentDepth(left, middle, right);
|
|
916
985
|
throw new Error(`Bad depth at branch ${formatListInline(tree)}`);
|
|
917
986
|
}
|
|
918
|
-
checkListStructure(
|
|
919
|
-
checkListStructure(
|
|
920
|
-
checkListStructure(
|
|
987
|
+
checkListStructure(left);
|
|
988
|
+
checkListStructure(middle);
|
|
989
|
+
checkListStructure(right);
|
|
921
990
|
break;
|
|
922
991
|
}
|
|
923
992
|
}
|
|
@@ -949,34 +1018,38 @@ export function slice(tree, startIdx, endIdx) {
|
|
|
949
1018
|
if (startIdx === 0 && endIdx === listLen(tree)) {
|
|
950
1019
|
return tree;
|
|
951
1020
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1021
|
+
// Cache children and their sizes to avoid repeated property access
|
|
1022
|
+
const left = tree.left;
|
|
1023
|
+
const middle = tree.middle;
|
|
1024
|
+
const right = tree.right;
|
|
1025
|
+
const leftSize = left == null ? 0 : left.size;
|
|
1026
|
+
const middleSize = middle == null ? 0 : middle.size;
|
|
1027
|
+
const rightSize = right == null ? 0 : right.size;
|
|
955
1028
|
// echo "sizes: {leftSize} {middleSize} {rightSize}"
|
|
956
1029
|
if (startIdx >= leftSize + middleSize) {
|
|
957
|
-
return slice(
|
|
1030
|
+
return slice(right, startIdx - leftSize - middleSize, endIdx - leftSize - middleSize);
|
|
958
1031
|
}
|
|
959
1032
|
if (startIdx >= leftSize)
|
|
960
1033
|
if (endIdx <= leftSize + middleSize) {
|
|
961
|
-
return slice(
|
|
1034
|
+
return slice(middle, startIdx - leftSize, endIdx - leftSize);
|
|
962
1035
|
}
|
|
963
1036
|
else {
|
|
964
|
-
let middleCut = slice(
|
|
965
|
-
let rightCut = slice(
|
|
1037
|
+
let middleCut = slice(middle, startIdx - leftSize, middleSize);
|
|
1038
|
+
let rightCut = slice(right, 0, endIdx - leftSize - middleSize);
|
|
966
1039
|
return concat(middleCut, rightCut);
|
|
967
1040
|
}
|
|
968
1041
|
if (endIdx <= leftSize) {
|
|
969
|
-
return slice(
|
|
1042
|
+
return slice(left, startIdx, endIdx);
|
|
970
1043
|
}
|
|
971
1044
|
if (endIdx <= leftSize + middleSize) {
|
|
972
|
-
let leftCut = slice(
|
|
973
|
-
let middleCut = slice(
|
|
1045
|
+
let leftCut = slice(left, startIdx, leftSize);
|
|
1046
|
+
let middleCut = slice(middle, 0, endIdx - leftSize);
|
|
974
1047
|
return concat(leftCut, middleCut);
|
|
975
1048
|
}
|
|
976
1049
|
if (endIdx <= leftSize + middleSize + rightSize) {
|
|
977
|
-
let leftCut = slice(
|
|
978
|
-
let rightCut = slice(
|
|
979
|
-
return concat(concat(leftCut,
|
|
1050
|
+
let leftCut = slice(left, startIdx, leftSize);
|
|
1051
|
+
let rightCut = slice(right, 0, endIdx - leftSize - middleSize);
|
|
1052
|
+
return concat(concat(leftCut, middle), rightCut);
|
|
980
1053
|
}
|
|
981
1054
|
throw new Error("Unknown");
|
|
982
1055
|
}
|
|
@@ -1019,13 +1092,17 @@ export function listMapValues(tree, f) {
|
|
|
1019
1092
|
return result;
|
|
1020
1093
|
}
|
|
1021
1094
|
case TernaryTreeKind.ternaryTreeBranch: {
|
|
1095
|
+
// Cache children to avoid repeated property access
|
|
1096
|
+
const left = tree.left;
|
|
1097
|
+
const middle = tree.middle;
|
|
1098
|
+
const right = tree.right;
|
|
1022
1099
|
let result = {
|
|
1023
1100
|
kind: TernaryTreeKind.ternaryTreeBranch,
|
|
1024
1101
|
size: tree.size,
|
|
1025
1102
|
depth: tree.depth,
|
|
1026
|
-
left:
|
|
1027
|
-
middle:
|
|
1028
|
-
right:
|
|
1103
|
+
left: left == null ? emptyBranch : listMapValues(left, f),
|
|
1104
|
+
middle: middle == null ? emptyBranch : listMapValues(middle, f),
|
|
1105
|
+
right: right == null ? emptyBranch : listMapValues(right, f),
|
|
1029
1106
|
};
|
|
1030
1107
|
return result;
|
|
1031
1108
|
}
|