@calcit/ternary-tree 0.0.23 → 0.0.25

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/map.mjs CHANGED
@@ -159,24 +159,21 @@ export function initTernaryTreeMap(t) {
159
159
  }
160
160
  // use for..in for performance
161
161
  export function initTernaryTreeMapFromArray(t) {
162
- let groupBuffers = {};
162
+ // Use Map instead of Record for better performance with numeric keys
163
+ let groupBuffers = new Map();
163
164
  let xs = [];
164
- for (let idx = 0; idx < t.length; idx++) {
165
+ const length = t.length;
166
+ for (let idx = 0; idx < length; idx++) {
165
167
  let k = t[idx][0];
166
168
  let v = t[idx][1];
167
169
  let h = hashGenerator(k);
168
- if (groupBuffers[h] != null) {
169
- let branch = groupBuffers[h];
170
- if (branch != null) {
171
- branch.push([k, v]);
172
- }
173
- else {
174
- throw new Error("Expected referece to pairs");
175
- }
170
+ let branch = groupBuffers.get(h);
171
+ if (branch != null) {
172
+ branch.push([k, v]);
176
173
  }
177
174
  else {
178
175
  let pairs = [[k, v]];
179
- groupBuffers[h] = pairs;
176
+ groupBuffers.set(h, pairs);
180
177
  xs.push({
181
178
  hash: h,
182
179
  pairs: pairs,
@@ -285,11 +282,15 @@ function collectHashSortedArray(tree, acc, idx) {
285
282
  else {
286
283
  switch (tree.kind) {
287
284
  case TernaryTreeKind.ternaryTreeLeaf: {
288
- for (let i = 0; i < tree.elements.length; i++) {
289
- let item = tree.elements[i];
290
- acc[idx.value] = item;
291
- idx.value = idx.value + 1;
285
+ // Cache elements and current index to reduce property access
286
+ const elements = tree.elements;
287
+ const length = elements.length;
288
+ let currentIdx = idx.value;
289
+ for (let i = 0; i < length; i++) {
290
+ acc[currentIdx] = elements[i];
291
+ currentIdx++;
292
292
  }
293
+ idx.value = currentIdx;
293
294
  break;
294
295
  }
295
296
  case TernaryTreeKind.ternaryTreeBranch: {
@@ -317,8 +318,10 @@ function collectOrderedHashEntries(tree, acc, idx) {
317
318
  else {
318
319
  switch (tree.kind) {
319
320
  case TernaryTreeKind.ternaryTreeLeaf: {
320
- acc[idx.value] = { hash: tree.hash, pairs: tree.elements };
321
- idx.value = idx.value + 1;
321
+ // Cache current index to reduce property access
322
+ const currentIdx = idx.value;
323
+ acc[currentIdx] = { hash: tree.hash, pairs: tree.elements };
324
+ idx.value = currentIdx + 1;
322
325
  break;
323
326
  }
324
327
  case TernaryTreeKind.ternaryTreeBranch: {
@@ -344,85 +347,88 @@ export function contains(originalTree, item) {
344
347
  if (originalTree == null) {
345
348
  return false;
346
349
  }
347
- // TODO
348
350
  // reduce redundant computation by reusing hash result
349
351
  let hx = hashGenerator(item);
350
352
  let tree = originalTree;
351
353
  whileLoop: while (tree != null) {
352
354
  if (tree.kind === TernaryTreeKind.ternaryTreeLeaf) {
353
355
  if (hx === tree.hash) {
354
- let size = tree.elements.length;
356
+ // Cache elements length to avoid repeated property access
357
+ const elements = tree.elements;
358
+ const size = elements.length;
355
359
  for (let idx = 0; idx < size; idx++) {
356
- let pair = tree.elements[idx];
357
- if (dataEqual(pair[0], item)) {
360
+ if (dataEqual(elements[idx][0], item)) {
358
361
  return true;
359
362
  }
360
363
  }
361
364
  }
362
365
  return false;
363
366
  }
364
- // echo "looking for: ", hx, " ", item, " in ", tree.formatInline(true)
365
- if (tree.left == null) {
367
+ // Optimize branch navigation with early exits
368
+ const left = tree.left;
369
+ if (left == null) {
366
370
  return false;
367
371
  }
368
- if (tree.left.kind === TernaryTreeKind.ternaryTreeLeaf) {
369
- if (hx < tree.left.hash) {
372
+ if (left.kind === TernaryTreeKind.ternaryTreeLeaf) {
373
+ if (hx < left.hash) {
370
374
  return false;
371
375
  }
372
- if (tree.left.hash === hx) {
373
- tree = tree.left;
374
- continue whileLoop; // notice, it jumps to while loop
376
+ if (left.hash === hx) {
377
+ tree = left;
378
+ continue whileLoop;
375
379
  }
376
380
  }
377
381
  else {
378
- if (hx < tree.left.minHash) {
382
+ if (hx < left.minHash) {
379
383
  return false;
380
384
  }
381
- if (hx <= tree.left.maxHash) {
382
- tree = tree.left;
383
- continue whileLoop; // notice, it jumps to while loop
385
+ if (hx <= left.maxHash) {
386
+ tree = left;
387
+ continue whileLoop;
384
388
  }
385
389
  }
386
- if (tree.middle == null) {
390
+ const middle = tree.middle;
391
+ if (middle == null) {
387
392
  return false;
388
393
  }
389
- if (tree.middle.kind === TernaryTreeKind.ternaryTreeLeaf) {
390
- if (hx < tree.middle.hash) {
394
+ if (middle.kind === TernaryTreeKind.ternaryTreeLeaf) {
395
+ if (hx < middle.hash) {
391
396
  return false;
392
397
  }
393
- if (tree.middle.hash === hx) {
394
- tree = tree.middle;
395
- continue whileLoop; // notice, it jumps to while loop
398
+ if (middle.hash === hx) {
399
+ tree = middle;
400
+ continue whileLoop;
396
401
  }
397
402
  }
398
403
  else {
399
- if (hx < tree.middle.minHash) {
404
+ if (hx < middle.minHash) {
400
405
  return false;
401
406
  }
402
- if (hx <= tree.middle.maxHash) {
403
- tree = tree.middle;
404
- continue whileLoop; // notice, it jumps to while loop
407
+ if (hx <= middle.maxHash) {
408
+ tree = middle;
409
+ continue whileLoop;
405
410
  }
406
411
  }
407
- if (tree.right == null) {
412
+ const right = tree.right;
413
+ if (right == null) {
408
414
  return false;
409
415
  }
410
- if (tree.right.kind === TernaryTreeKind.ternaryTreeLeaf) {
411
- if (hx < tree.right.hash) {
416
+ if (right.kind === TernaryTreeKind.ternaryTreeLeaf) {
417
+ if (hx < right.hash) {
412
418
  return false;
413
419
  }
414
- if (tree.right.hash === hx) {
415
- tree = tree.right;
416
- continue whileLoop; // notice, it jumps to while loop
420
+ if (right.hash === hx) {
421
+ tree = right;
422
+ continue whileLoop;
417
423
  }
418
424
  }
419
425
  else {
420
- if (hx < tree.right.minHash) {
426
+ if (hx < right.minHash) {
421
427
  return false;
422
428
  }
423
- if (hx <= tree.right.maxHash) {
424
- tree = tree.right;
425
- continue whileLoop; // notice, it jumps to while loop
429
+ if (hx <= right.maxHash) {
430
+ tree = right;
431
+ continue whileLoop;
426
432
  }
427
433
  }
428
434
  return false;
@@ -434,77 +440,81 @@ export function mapGetDefault(originalTree, item, v0) {
434
440
  let tree = originalTree;
435
441
  whileLoop: while (tree != null) {
436
442
  if (tree.kind === TernaryTreeKind.ternaryTreeLeaf) {
437
- let size = tree.elements.length;
443
+ // Cache elements array to avoid repeated property access
444
+ const elements = tree.elements;
445
+ const size = elements.length;
438
446
  for (let i = 0; i < size; i++) {
439
- let pair = tree.elements[i];
440
- if (dataEqual(pair[0], item)) {
441
- return pair[1];
447
+ if (dataEqual(elements[i][0], item)) {
448
+ return elements[i][1];
442
449
  }
443
450
  }
444
451
  return v0;
445
452
  }
446
- // echo "looking for: ", hx, " ", item, " in ", tree.formatInline
447
- if (tree.left == null) {
453
+ // Cache tree children to avoid repeated property access
454
+ const left = tree.left;
455
+ if (left == null) {
448
456
  return v0;
449
457
  }
450
- if (tree.left.kind == TernaryTreeKind.ternaryTreeLeaf) {
451
- if (hx < tree.left.hash) {
458
+ if (left.kind == TernaryTreeKind.ternaryTreeLeaf) {
459
+ if (hx < left.hash) {
452
460
  return v0;
453
461
  }
454
- if (tree.left.hash === hx) {
455
- tree = tree.left;
456
- continue whileLoop; // notice, it jumps to while loop
462
+ if (left.hash === hx) {
463
+ tree = left;
464
+ continue whileLoop;
457
465
  }
458
466
  }
459
467
  else {
460
- if (hx < tree.left.minHash) {
468
+ if (hx < left.minHash) {
461
469
  return v0;
462
470
  }
463
- if (hx <= tree.left.maxHash) {
464
- tree = tree.left;
465
- continue whileLoop; // notice, it jumps to while loop
471
+ if (hx <= left.maxHash) {
472
+ tree = left;
473
+ continue whileLoop;
466
474
  }
467
475
  }
468
- if (tree.middle == null) {
476
+ const middle = tree.middle;
477
+ if (middle == null) {
469
478
  return v0;
470
479
  }
471
- if (tree.middle.kind == TernaryTreeKind.ternaryTreeLeaf) {
472
- if (hx < tree.middle.hash) {
480
+ if (middle.kind == TernaryTreeKind.ternaryTreeLeaf) {
481
+ if (hx < middle.hash) {
473
482
  return v0;
474
483
  }
475
- if (tree.middle.hash === hx) {
476
- tree = tree.middle;
477
- continue whileLoop; // notice, it jumps to while loop
484
+ if (middle.hash === hx) {
485
+ tree = middle;
486
+ continue whileLoop;
478
487
  }
479
488
  }
480
489
  else {
481
- if (hx < tree.middle.minHash) {
490
+ if (hx < middle.minHash) {
482
491
  return v0;
483
492
  }
484
- if (hx <= tree.middle.maxHash) {
485
- tree = tree.middle;
486
- continue whileLoop; // notice, it jumps to while loop
493
+ if (hx <= middle.maxHash) {
494
+ tree = middle;
495
+ continue whileLoop;
487
496
  }
488
497
  }
489
- if (tree.right == null) {
498
+ const right = tree.right;
499
+ if (right == null) {
490
500
  return v0;
491
501
  }
492
- if (tree.right.kind == TernaryTreeKind.ternaryTreeLeaf) {
493
- if (hx < tree.right.hash) {
502
+ if (right.kind == TernaryTreeKind.ternaryTreeLeaf) {
503
+ if (hx < right.hash) {
494
504
  return v0;
495
505
  }
496
- if (tree.right.hash === hx) {
497
- tree = tree.right;
498
- continue whileLoop; // notice, it jumps to while loop
506
+ if (right.hash === hx) {
507
+ tree = right;
508
+ continue whileLoop;
499
509
  }
500
510
  }
501
511
  else {
502
- if (hx < tree.right.minHash) {
512
+ if (hx < right.minHash) {
503
513
  return v0;
504
514
  }
505
- if (hx <= tree.right.maxHash) {
506
- tree = tree.right;
507
- continue whileLoop; // notice, it jumps to while loop
515
+ if (hx <= right.maxHash) {
516
+ tree = right;
517
+ continue whileLoop;
508
518
  }
509
519
  }
510
520
  return v0;
@@ -1025,28 +1035,38 @@ export function dissocMap(tree, key) {
1025
1035
  function collectToPairsArray(acc, tree) {
1026
1036
  if (tree != null) {
1027
1037
  if (tree.kind === TernaryTreeKind.ternaryTreeLeaf) {
1028
- for (let i = 0; i < tree.elements.length; i++) {
1029
- let pair = tree.elements[i];
1030
- acc.push(pair);
1038
+ // Cache elements array and use batch push for better performance
1039
+ const elements = tree.elements;
1040
+ const length = elements.length;
1041
+ for (let i = 0; i < length; i++) {
1042
+ acc.push(elements[i]);
1031
1043
  }
1032
1044
  }
1033
1045
  else {
1034
- if (tree.left != null) {
1035
- collectToPairsArray(acc, tree.left);
1046
+ // Cache children to avoid repeated property access
1047
+ const left = tree.left;
1048
+ const middle = tree.middle;
1049
+ const right = tree.right;
1050
+ if (left != null) {
1051
+ collectToPairsArray(acc, left);
1036
1052
  }
1037
- if (tree.middle != null) {
1038
- collectToPairsArray(acc, tree.middle);
1053
+ if (middle != null) {
1054
+ collectToPairsArray(acc, middle);
1039
1055
  }
1040
- if (tree.right != null) {
1041
- collectToPairsArray(acc, tree.right);
1056
+ if (right != null) {
1057
+ collectToPairsArray(acc, right);
1042
1058
  }
1043
1059
  }
1044
1060
  }
1045
1061
  }
1046
1062
  /** similar to `toPairs`, but using Array.push directly */
1047
1063
  export function toPairsArray(tree) {
1048
- let result = [];
1049
- collectToPairsArray(result, tree);
1064
+ // Pre-allocate array with known size for better performance
1065
+ const totalSize = mapLen(tree);
1066
+ let result = new Array(totalSize);
1067
+ let idx = { value: 0 };
1068
+ // Use the more efficient collectHashSortedArray instead
1069
+ collectHashSortedArray(tree, result, idx);
1050
1070
  return result;
1051
1071
  }
1052
1072
  export function* toPairs(tree) {
@@ -1254,10 +1274,13 @@ export function mapMapValues(tree, f) {
1254
1274
  }
1255
1275
  switch (tree.kind) {
1256
1276
  case TernaryTreeKind.ternaryTreeLeaf: {
1257
- let newElements = new Array(tree.elements.length);
1258
- let size = tree.elements.length;
1277
+ // Cache elements array and pre-allocate result
1278
+ const elements = tree.elements;
1279
+ const size = elements.length;
1280
+ let newElements = new Array(size);
1259
1281
  for (let idx = 0; idx < size; idx++) {
1260
- newElements[idx] = [tree.elements[idx][0], f(tree.elements[idx][1])];
1282
+ const element = elements[idx];
1283
+ newElements[idx] = [element[0], f(element[1])];
1261
1284
  }
1262
1285
  let result = {
1263
1286
  kind: TernaryTreeKind.ternaryTreeLeaf,
@@ -0,0 +1 @@
1
+ export declare function runDetailedListPerformanceTests(): void;
@@ -0,0 +1,165 @@
1
+ import { initTernaryTreeList, initTernaryTreeListFromRange, listGet, assocList, insert, listLen, indexOf, listToItems, prepend, concat, slice, } from "./list.mjs";
2
+ function createTestListData(size) {
3
+ const data = [];
4
+ for (let i = 0; i < size; i++) {
5
+ data.push(i);
6
+ }
7
+ return data;
8
+ }
9
+ function measureTime(name, fn) {
10
+ const start = performance.now();
11
+ const result = fn();
12
+ const end = performance.now();
13
+ const duration = end - start;
14
+ console.log(`${name}: ${duration.toFixed(2)}ms`);
15
+ return [result, duration];
16
+ }
17
+ function repeatTest(times, fn) {
18
+ let totalTime = 0;
19
+ for (let i = 0; i < times; i++) {
20
+ const start = performance.now();
21
+ fn();
22
+ const end = performance.now();
23
+ totalTime += end - start;
24
+ }
25
+ return totalTime / times;
26
+ }
27
+ export function runDetailedListPerformanceTests() {
28
+ console.log("\n=== Detailed List Performance Tests ===");
29
+ console.log("Testing various operations with different list sizes to show optimization benefits\n");
30
+ const sizes = [500, 2000, 8000, 16000];
31
+ for (const size of sizes) {
32
+ console.log(`\n--- Performance Analysis: ${size} elements ---`);
33
+ const testData = createTestListData(size);
34
+ // Test list creation - should benefit from reduced overhead
35
+ const [treeList, creationTime] = measureTime(`Create list (${size})`, () => {
36
+ return initTernaryTreeList(testData);
37
+ });
38
+ // Test range creation
39
+ measureTime(`Range init (${size})`, () => {
40
+ return initTernaryTreeListFromRange(testData, 0, size);
41
+ });
42
+ // Test random access patterns - benefits from cached size calculations
43
+ const accessCount = Math.min(1000, size);
44
+ const indices = Array.from({ length: accessCount }, () => Math.floor(Math.random() * size));
45
+ const avgAccessTime = repeatTest(3, () => {
46
+ for (const idx of indices) {
47
+ listGet(treeList, idx);
48
+ }
49
+ });
50
+ console.log(`Random access avg (${accessCount} ops): ${avgAccessTime.toFixed(2)}ms`);
51
+ // Test sequential access
52
+ const avgSeqTime = repeatTest(3, () => {
53
+ for (let i = 0; i < Math.min(size, 1000); i++) {
54
+ listGet(treeList, i);
55
+ }
56
+ });
57
+ console.log(`Sequential access avg (${Math.min(size, 1000)} ops): ${avgSeqTime.toFixed(2)}ms`);
58
+ // Test structural modifications - heavily benefits from cached calculations
59
+ const modCount = Math.min(100, size / 10);
60
+ const avgAssocTime = repeatTest(3, () => {
61
+ let list = treeList;
62
+ for (let i = 0; i < modCount; i++) {
63
+ const idx = Math.floor(Math.random() * size);
64
+ list = assocList(list, idx, idx * 2);
65
+ }
66
+ return list;
67
+ });
68
+ console.log(`Assoc operations avg (${modCount} ops): ${avgAssocTime.toFixed(2)}ms`);
69
+ // Test insertions
70
+ const insertCount = Math.min(50, size / 20);
71
+ const avgInsertTime = repeatTest(3, () => {
72
+ let list = treeList;
73
+ for (let i = 0; i < insertCount; i++) {
74
+ const idx = Math.min(Math.floor(Math.random() * (size + i)), listLen(list));
75
+ list = insert(list, idx, i + size);
76
+ }
77
+ return list;
78
+ });
79
+ console.log(`Insert operations avg (${insertCount} ops): ${avgInsertTime.toFixed(2)}ms`);
80
+ // Test search operations - benefits from cached property access
81
+ const searchCount = Math.min(100, size / 10);
82
+ const searchValues = testData.slice(0, searchCount);
83
+ const avgSearchTime = repeatTest(3, () => {
84
+ for (const value of searchValues) {
85
+ indexOf(treeList, value);
86
+ }
87
+ });
88
+ console.log(`Search operations avg (${searchCount} ops): ${avgSearchTime.toFixed(2)}ms`);
89
+ // Test slicing - benefits from cached size calculations
90
+ const sliceCount = Math.min(20, size / 50);
91
+ const avgSliceTime = repeatTest(3, () => {
92
+ for (let i = 0; i < sliceCount; i++) {
93
+ const start = Math.floor((Math.random() * size) / 2);
94
+ const end = start + Math.floor((Math.random() * size) / 4);
95
+ slice(treeList, start, Math.min(end, size));
96
+ }
97
+ });
98
+ console.log(`Slice operations avg (${sliceCount} ops): ${avgSliceTime.toFixed(2)}ms`);
99
+ // Iteration performance
100
+ const avgIterTime = repeatTest(3, () => {
101
+ let count = 0;
102
+ for (const item of listToItems(treeList)) {
103
+ count++;
104
+ }
105
+ return count;
106
+ });
107
+ console.log(`Iterator traversal avg (${size} items): ${avgIterTime.toFixed(2)}ms`);
108
+ // Complex operation chains - should show cumulative benefits
109
+ measureTime(`Complex chain (${Math.floor(size / 100)} ops)`, () => {
110
+ let list = treeList;
111
+ const ops = Math.floor(size / 100);
112
+ for (let i = 0; i < ops; i++) {
113
+ // Prepend
114
+ list = prepend(list, -i);
115
+ // Random assoc
116
+ if (listLen(list) > 10) {
117
+ const idx = Math.floor(Math.random() * Math.min(listLen(list), 100));
118
+ list = assocList(list, idx, i * 1000);
119
+ }
120
+ // Random access
121
+ if (listLen(list) > 5) {
122
+ listGet(list, Math.floor(Math.random() * Math.min(listLen(list), 50)));
123
+ }
124
+ }
125
+ return list;
126
+ });
127
+ console.log("");
128
+ }
129
+ // Stress test for deep operations
130
+ console.log("=== Stress Tests ===");
131
+ const largeList = initTernaryTreeList(createTestListData(50000));
132
+ measureTime("Deep list access stress", () => {
133
+ const samples = 1000;
134
+ for (let i = 0; i < samples; i++) {
135
+ const idx = Math.floor(Math.random() * 50000);
136
+ listGet(largeList, idx);
137
+ }
138
+ });
139
+ measureTime("Deep list modification stress", () => {
140
+ let list = largeList;
141
+ for (let i = 0; i < 100; i++) {
142
+ const idx = Math.floor(Math.random() * listLen(list));
143
+ list = assocList(list, idx, i * 10000);
144
+ }
145
+ return list;
146
+ });
147
+ // Memory efficiency test
148
+ console.log("\n=== Memory Efficiency Tests ===");
149
+ measureTime("Large tree creation (100k)", () => {
150
+ return initTernaryTreeList(createTestListData(100000));
151
+ });
152
+ // Test concatenation performance
153
+ console.log("\n=== Concatenation Performance ===");
154
+ const list1 = initTernaryTreeList(createTestListData(5000));
155
+ const list2 = initTernaryTreeList(createTestListData(5000));
156
+ const list3 = initTernaryTreeList(createTestListData(5000));
157
+ const list4 = initTernaryTreeList(createTestListData(5000));
158
+ measureTime("Concat 4 large lists (5k each)", () => {
159
+ return concat(concat(concat(list1, list2), list3), list4);
160
+ });
161
+ console.log("\n✅ Performance testing completed!");
162
+ }
163
+ if (import.meta.url === `file://${process.argv[1]}`) {
164
+ runDetailedListPerformanceTests();
165
+ }
@@ -0,0 +1 @@
1
+ export declare function runListPerformanceTests(): void;