@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/lib/list.mjs CHANGED
@@ -1,5 +1,11 @@
1
1
  import { TernaryTreeKind } from "./types.mjs";
2
- import { dataEqual, divideTernarySizes, roughIntPow } from "./utils.mjs";
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
- function decideParentDepth(...xs) {
22
- let depth = 0;
23
- for (let i = 0; i < xs.length; i++) {
24
- let x = xs[i];
25
- let y = getDepth(x);
26
- if (y > depth) {
27
- depth = y;
28
- }
29
- }
30
- return depth + 1;
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: listLen(left) + listLen(middle),
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
- checkListStructure(result);
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: listLen(left) + listLen(middle) + listLen(right),
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
- checkListStructure(result);
74
+ if (_devMode)
75
+ checkListStructure(result);
66
76
  return result;
67
77
  }
68
78
  default: {
69
- let divided = divideTernarySizes(size);
70
- let left = makeTernaryTreeList(divided.left, offset, xs);
71
- let middle = makeTernaryTreeList(divided.middle, offset + divided.left, xs);
72
- let right = makeTernaryTreeList(divided.right, offset + divided.left + divided.middle, xs);
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: listLen(left) + listLen(middle) + listLen(right),
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
- checkListStructure(result);
96
+ if (_devMode)
97
+ checkListStructure(result);
82
98
  return result;
83
99
  }
84
100
  }
85
101
  }
86
102
  export function initTernaryTreeList(xs) {
87
- let ys = new Array(xs.length);
88
- let size = xs.length;
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
- let x = xs[idx];
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(xs.length, 0, ys);
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
- let ys = new Array(to - from);
98
- for (let idx = from; idx < to; idx++) {
99
- let x = xs[idx];
100
- ys[idx - from] = { kind: TernaryTreeKind.ternaryTreeLeaf, size: 1, value: x };
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(ys.length, 0, ys);
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 != null) {
138
- switch (tree.kind) {
139
- case TernaryTreeKind.ternaryTreeLeaf: {
140
- yield tree.value;
141
- break;
142
- }
143
- case TernaryTreeKind.ternaryTreeBranch: {
144
- if (tree.left != null) {
145
- for (let x of listToItems(tree.left)) {
146
- yield x;
147
- }
148
- }
149
- if (tree.middle != null) {
150
- for (let x of listToItems(tree.middle)) {
151
- yield x;
152
- }
153
- }
154
- if (tree.right != null) {
155
- for (let x of listToItems(tree.right)) {
156
- yield x;
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
- let tryLeft = findIndex(tree.left, f);
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(tree.middle, f);
199
+ let tryMiddle = findIndex(middle, f);
184
200
  if (tryMiddle >= 0) {
185
- return tryMiddle + listLen(tree.left);
201
+ return tryMiddle + (left == null ? 0 : left.size);
186
202
  }
187
- let tryRight = findIndex(tree.right, f);
203
+ let tryRight = findIndex(right, f);
188
204
  if (tryRight >= 0) {
189
- return tryRight + listLen(tree.left) + listLen(tree.middle);
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
- let tryLeft = indexOf(tree.left, item);
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(tree.middle, item);
232
+ let tryMiddle = indexOf(middle, item);
213
233
  if (tryMiddle >= 0) {
214
- return tryMiddle + listLen(tree.left);
234
+ return tryMiddle + (left == null ? 0 : left.size);
215
235
  }
216
- let tryRight = indexOf(tree.right, item);
236
+ let tryRight = indexOf(right, item);
217
237
  if (tryRight >= 0) {
218
- return tryRight + listLen(tree.left) + listLen(tree.middle);
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
- acc[idx.value] = tree;
231
- idx.value = idx.value + 1;
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
- if (tree.left != null) {
236
- writeLeavesArray(tree.left, acc, idx);
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 (tree.middle != null) {
239
- writeLeavesArray(tree.middle, acc, idx);
264
+ if (middle != null) {
265
+ writeLeavesArray(middle, acc, idx);
240
266
  }
241
- if (tree.right != null) {
242
- writeLeavesArray(tree.right, acc, idx);
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
- let leftSize = tree.left == null ? 0 : tree.left.size;
289
- let middleSize = tree.middle == null ? 0 : tree.middle.size;
290
- let rightSize = tree.right == null ? 0 : tree.right.size;
291
- if (leftSize + middleSize + rightSize !== tree.size) {
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 = tree.left;
322
+ tree = left;
296
323
  }
297
324
  else if (idx <= leftSize + middleSize - 1) {
298
- tree = tree.middle;
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
- let leftSize = listLen(tree.left);
340
- let middleSize = listLen(tree.middle);
341
- let rightSize = listLen(tree.right);
342
- if (leftSize + middleSize + rightSize !== tree.size)
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(tree.left, idx, item);
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, tree.middle, tree.right),
380
+ depth: decideParentDepth(changedBranch, middle, right),
350
381
  left: changedBranch,
351
- middle: tree.middle,
352
- right: tree.right,
382
+ middle: middle,
383
+ right: right,
353
384
  };
354
- checkListStructure(result);
385
+ if (_devMode)
386
+ checkListStructure(result);
355
387
  return result;
356
388
  }
357
389
  else if (idx <= leftSize + middleSize - 1) {
358
- let changedBranch = assocList(tree.middle, idx - leftSize, item);
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(tree.left, changedBranch, tree.right),
363
- left: tree.left,
394
+ depth: decideParentDepth(left, changedBranch, right),
395
+ left: left,
364
396
  middle: changedBranch,
365
- right: tree.right,
397
+ right: right,
366
398
  };
367
- checkListStructure(result);
399
+ if (_devMode)
400
+ checkListStructure(result);
368
401
  return result;
369
402
  }
370
403
  else {
371
- let changedBranch = assocList(tree.right, idx - leftSize - middleSize, item);
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(tree.left, tree.middle, changedBranch),
376
- left: tree.left,
377
- middle: tree.middle,
408
+ depth: decideParentDepth(left, middle, changedBranch),
409
+ left: left,
410
+ middle: middle,
378
411
  right: changedBranch,
379
412
  };
380
- checkListStructure(result);
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
- let leftSize = listLen(tree.left);
404
- let middleSize = listLen(tree.middle);
405
- let rightSize = listLen(tree.right);
406
- if (leftSize + middleSize + rightSize !== tree.size) {
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(tree.left, idx);
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(tree.middle, tree.right),
417
- left: tree.middle,
418
- middle: tree.right,
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, tree.middle, tree.right),
464
+ depth: decideParentDepth(changedBranch, middle, right),
427
465
  left: changedBranch,
428
- middle: tree.middle,
429
- right: tree.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(tree.middle, idx - leftSize);
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(tree.left, changedBranch, tree.right),
440
- left: tree.left,
441
- middle: tree.right,
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(tree.left, changedBranch, tree.right),
450
- left: tree.left,
487
+ depth: decideParentDepth(left, changedBranch, right),
488
+ left: left,
451
489
  middle: changedBranch,
452
- right: tree.right,
490
+ right: right,
453
491
  };
454
492
  }
455
493
  }
456
494
  else {
457
- let changedBranch = dissocList(tree.right, idx - leftSize - middleSize);
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(tree.left, tree.middle, changedBranch),
465
- left: tree.left,
466
- middle: tree.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
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(result);
563
+ if (_devMode)
564
+ checkListStructure(result);
524
565
  return result;
525
566
  }
526
567
  }
527
- checkListStructure(tree);
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
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(result);
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
- let leftSize = listLen(tree.left);
616
- let middleSize = listLen(tree.middle);
617
- let rightSize = listLen(tree.right);
618
- if (leftSize + middleSize + rightSize !== tree.size) {
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 (listLen(tree.left) >= listLen(tree.middle) && listLen(tree.left) >= listLen(tree.right)) {
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
- checkListStructure(result);
684
+ if (_devMode)
685
+ checkListStructure(result);
633
686
  return result;
634
687
  }
635
688
  }
636
689
  if (idx === listLen(tree) - 1 && after) {
637
- if (listLen(tree.right) >= listLen(tree.middle) && listLen(tree.right) >= listLen(tree.left)) {
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
- checkListStructure(result);
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
- checkListStructure(result);
713
+ if (_devMode)
714
+ checkListStructure(result);
660
715
  return result;
661
716
  }
662
- if (!after && idx === 0 && rightSize === 0 && middleSize >= rightSize) {
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: tree.left,
669
- right: tree.middle,
723
+ middle: left,
724
+ right: middle,
670
725
  };
671
- checkListStructure(result);
726
+ if (_devMode)
727
+ checkListStructure(result);
672
728
  return result;
673
729
  }
674
730
  if (idx <= leftSize - 1) {
675
- let changedBranch = insert(tree.left, idx, item, after);
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, tree.middle, tree.right),
735
+ depth: decideParentDepth(changedBranch, middle, right),
680
736
  left: changedBranch,
681
- middle: tree.middle,
682
- right: tree.right,
737
+ middle: middle,
738
+ right: right,
683
739
  };
684
- checkListStructure(result);
740
+ if (_devMode)
741
+ checkListStructure(result);
685
742
  return result;
686
743
  }
687
744
  else if (idx <= leftSize + middleSize - 1) {
688
- let changedBranch = insert(tree.middle, idx - leftSize, item, after);
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(tree.left, changedBranch, tree.right),
693
- left: tree.left,
749
+ depth: decideParentDepth(left, changedBranch, right),
750
+ left: left,
694
751
  middle: changedBranch,
695
- right: tree.right,
752
+ right: right,
696
753
  };
697
- checkListStructure(result);
754
+ if (_devMode)
755
+ checkListStructure(result);
698
756
  return result;
699
757
  }
700
758
  else {
701
- let changedBranch = insert(tree.right, idx - leftSize - middleSize, item, after);
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(tree.left, tree.middle, changedBranch),
706
- left: tree.left,
707
- middle: tree.middle,
763
+ depth: decideParentDepth(left, middle, changedBranch),
764
+ left: left,
765
+ middle: middle,
708
766
  right: changedBranch,
709
767
  };
710
- checkListStructure(result);
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
- checkListStructure(result);
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
- checkListStructure(ret);
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
- checkListStructure(ret);
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
- if (tree.size !== listLen(tree.left) + listLen(tree.middle) + listLen(tree.right)) {
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 (tree.left == null && tree.middle != null) {
977
+ if (left == null && middle != null) {
909
978
  throw new Error("morformed tree");
910
979
  }
911
- if (tree.middle == null && tree.right != null) {
980
+ if (middle == null && right != null) {
912
981
  throw new Error("morformed tree");
913
982
  }
914
- if (tree.depth !== decideParentDepth(tree.left, tree.middle, tree.right)) {
915
- let x = decideParentDepth(tree.left, tree.middle, tree.right);
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(tree.left);
919
- checkListStructure(tree.middle);
920
- checkListStructure(tree.right);
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
- let leftSize = listLen(tree.left);
953
- let middleSize = listLen(tree.middle);
954
- let rightSize = listLen(tree.right);
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(tree.right, startIdx - leftSize - middleSize, endIdx - leftSize - middleSize);
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(tree.middle, startIdx - leftSize, endIdx - leftSize);
1034
+ return slice(middle, startIdx - leftSize, endIdx - leftSize);
962
1035
  }
963
1036
  else {
964
- let middleCut = slice(tree.middle, startIdx - leftSize, middleSize);
965
- let rightCut = slice(tree.right, 0, endIdx - leftSize - middleSize);
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(tree.left, startIdx, endIdx);
1042
+ return slice(left, startIdx, endIdx);
970
1043
  }
971
1044
  if (endIdx <= leftSize + middleSize) {
972
- let leftCut = slice(tree.left, startIdx, leftSize);
973
- let middleCut = slice(tree.middle, 0, endIdx - leftSize);
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(tree.left, startIdx, leftSize);
978
- let rightCut = slice(tree.right, 0, endIdx - leftSize - middleSize);
979
- return concat(concat(leftCut, tree.middle), rightCut);
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: tree.left == null ? emptyBranch : listMapValues(tree.left, f),
1027
- middle: tree.middle == null ? emptyBranch : listMapValues(tree.middle, f),
1028
- right: tree.right == null ? emptyBranch : listMapValues(tree.right, f),
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
  }