@bablr/btree 0.3.1 → 0.4.1
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/enhanceable.js +163 -68
- package/lib/index.js +4 -2
- package/package.json +3 -3
package/lib/enhanceable.js
CHANGED
|
@@ -16,6 +16,18 @@ export const buildModule = (
|
|
|
16
16
|
return nodes.map(getSize).reduce((a, b) => a + b, 0);
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
const getHeight = (tree) => {
|
|
20
|
+
let height = 0;
|
|
21
|
+
let node = tree;
|
|
22
|
+
|
|
23
|
+
while (Array.isArray(node)) {
|
|
24
|
+
height++;
|
|
25
|
+
node = getValues(node)[0];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return height;
|
|
29
|
+
};
|
|
30
|
+
|
|
19
31
|
const treeFrom = (...values) => {
|
|
20
32
|
if (!statsReducer && values.length <= NODE_SIZE) {
|
|
21
33
|
return freeze(values);
|
|
@@ -30,11 +42,12 @@ export const buildModule = (
|
|
|
30
42
|
|
|
31
43
|
const buildStats = (values) => {
|
|
32
44
|
let sums = values.reduce(statsReducer, getSumsInitial());
|
|
33
|
-
finalizeSums(sums);
|
|
45
|
+
sums = finalizeSums(sums);
|
|
34
46
|
return sums;
|
|
35
47
|
};
|
|
36
48
|
|
|
37
49
|
const treeFromValues = (values) => {
|
|
50
|
+
if (values.includes(undefined)) throw new Error();
|
|
38
51
|
if (!statsReducer && values.length <= NODE_SIZE) {
|
|
39
52
|
return isArray(values[0]) ? freeze([sumNodes(values), freeze(values)]) : freeze(values);
|
|
40
53
|
} else {
|
|
@@ -91,42 +104,101 @@ export const buildModule = (
|
|
|
91
104
|
};
|
|
92
105
|
|
|
93
106
|
const setValuesAt = (idx, node, value) => {
|
|
94
|
-
const isLeaf = isLeafNode(node);
|
|
95
107
|
const values = getValues(node);
|
|
96
108
|
|
|
97
|
-
if (
|
|
109
|
+
if (!Number.isFinite(idx)) throw new Error();
|
|
98
110
|
|
|
99
|
-
if (!
|
|
111
|
+
if (!value == null) {
|
|
100
112
|
throw new Error();
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const newValues = [...values];
|
|
115
|
+
const newValues = values.slice();
|
|
106
116
|
newValues[idx] = value;
|
|
107
117
|
return treeFromValues(newValues);
|
|
108
118
|
};
|
|
109
119
|
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
if (!isArray(tree)) throw new Error();
|
|
120
|
+
const concat = (first, second) => {
|
|
121
|
+
if (!isArray(first) || !isArray(second)) throw new Error();
|
|
113
122
|
|
|
114
|
-
let
|
|
123
|
+
let firstHeight = getHeight(first);
|
|
124
|
+
let secondHeight = getHeight(second);
|
|
125
|
+
let firstValues = getValues(first);
|
|
126
|
+
let secondValues = getValues(second);
|
|
115
127
|
|
|
116
|
-
if (
|
|
128
|
+
if (firstHeight === secondHeight) {
|
|
129
|
+
if (firstValues.length + secondValues.length < NODE_SIZE) {
|
|
130
|
+
return treeFromValues([...firstValues, ...secondValues]);
|
|
131
|
+
} else {
|
|
132
|
+
return treeFromValues([first, second]);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
let targetDepth = Math.abs(firstHeight - secondHeight);
|
|
136
|
+
let path =
|
|
137
|
+
firstHeight > secondHeight
|
|
138
|
+
? findPath(Infinity, first, targetDepth)
|
|
139
|
+
: findPath(0, second, targetDepth);
|
|
140
|
+
|
|
141
|
+
let { node, index } = path.value;
|
|
142
|
+
|
|
143
|
+
let pushout = firstHeight > secondHeight ? second : first;
|
|
144
|
+
|
|
145
|
+
let values = getValues(node);
|
|
146
|
+
|
|
147
|
+
for (;;) {
|
|
148
|
+
if (pushout) {
|
|
149
|
+
values = values.slice();
|
|
150
|
+
|
|
151
|
+
let finiteIndex = index === Infinity ? values.length : index;
|
|
152
|
+
|
|
153
|
+
if (!isFinite(finiteIndex)) throw new Error();
|
|
154
|
+
if (values.length + getValues(pushout).length > NODE_SIZE) {
|
|
155
|
+
values.splice(finiteIndex, 0, pushout);
|
|
156
|
+
const { leftValues, rightValues } = splitValues(values);
|
|
157
|
+
|
|
158
|
+
pushout = treeFromValues(leftValues);
|
|
159
|
+
node = treeFromValues(rightValues);
|
|
160
|
+
} else {
|
|
161
|
+
values.splice(finiteIndex, 0, pushout);
|
|
162
|
+
node = setValues(node, values);
|
|
163
|
+
pushout = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (path.size === 1) {
|
|
168
|
+
if (pushout) {
|
|
169
|
+
return treeFromValues([pushout, node]);
|
|
170
|
+
} else {
|
|
171
|
+
return node;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const poppedNode = node;
|
|
176
|
+
path = path.pop();
|
|
177
|
+
({ node, index } = path.value);
|
|
178
|
+
|
|
179
|
+
node = setValuesAt(index, node, poppedNode);
|
|
180
|
+
values = getValues(node);
|
|
181
|
+
path = path.replace({ node, index });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const addAt = (idx, tree, value) => {
|
|
187
|
+
if (idx < 0 || !Number.isFinite(idx)) throw new Error('invalid argument');
|
|
188
|
+
if (!isArray(tree)) throw new Error();
|
|
189
|
+
if (!value) throw new Error();
|
|
117
190
|
|
|
118
191
|
let path = findPath(idx, tree);
|
|
119
192
|
|
|
120
193
|
let { node, index } = path.value;
|
|
121
194
|
|
|
122
|
-
// left pushout vs right pushout?
|
|
123
195
|
let pushout = value;
|
|
124
196
|
|
|
125
197
|
let values = getValues(node);
|
|
126
198
|
|
|
127
199
|
for (;;) {
|
|
128
200
|
if (pushout) {
|
|
129
|
-
values =
|
|
201
|
+
values = values.slice();
|
|
130
202
|
|
|
131
203
|
let finiteIndex = index === Infinity ? values.length : index;
|
|
132
204
|
|
|
@@ -163,7 +235,8 @@ export const buildModule = (
|
|
|
163
235
|
};
|
|
164
236
|
|
|
165
237
|
const push = (tree, value) => {
|
|
166
|
-
|
|
238
|
+
let size = getSize(tree);
|
|
239
|
+
return size ? addAt(size, tree, value) : treeFromValues([value]);
|
|
167
240
|
};
|
|
168
241
|
|
|
169
242
|
const collapses = (size) => {
|
|
@@ -178,15 +251,16 @@ export const buildModule = (
|
|
|
178
251
|
return collapses(getValues(node).length - 1);
|
|
179
252
|
};
|
|
180
253
|
|
|
181
|
-
const
|
|
182
|
-
let path = findPath(
|
|
254
|
+
const removeAt = (idx, tree) => {
|
|
255
|
+
let path = findPath(idx, tree);
|
|
183
256
|
|
|
184
257
|
let { node, index } = path.value;
|
|
185
258
|
|
|
186
|
-
const initialValues =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
259
|
+
const initialValues = [...getValues(node)];
|
|
260
|
+
|
|
261
|
+
initialValues.splice(index, 1);
|
|
262
|
+
|
|
263
|
+
let returnValue = !isFinite(node[0]) ? initialValues : treeFromValues(initialValues);
|
|
190
264
|
|
|
191
265
|
for (;;) {
|
|
192
266
|
let values = getValues(returnValue);
|
|
@@ -204,7 +278,7 @@ export const buildModule = (
|
|
|
204
278
|
let targetSiblingIndex = targetSibling && (prevSibling ? parentIndex - 1 : parentIndex + 1);
|
|
205
279
|
|
|
206
280
|
if (targetSibling) {
|
|
207
|
-
let targetValues =
|
|
281
|
+
let targetValues = getValues(targetSibling).slice();
|
|
208
282
|
|
|
209
283
|
const donationIdx = targetSibling === prevSibling ? targetValues.length - 1 : 0;
|
|
210
284
|
const donated = targetValues[donationIdx];
|
|
@@ -215,7 +289,7 @@ export const buildModule = (
|
|
|
215
289
|
index: targetSiblingIndex,
|
|
216
290
|
};
|
|
217
291
|
|
|
218
|
-
values =
|
|
292
|
+
values = values.slice();
|
|
219
293
|
|
|
220
294
|
values.splice(targetSibling === prevSibling ? values.length : 0, 0, donated);
|
|
221
295
|
|
|
@@ -224,8 +298,8 @@ export const buildModule = (
|
|
|
224
298
|
}
|
|
225
299
|
|
|
226
300
|
if (path.size === 1) {
|
|
227
|
-
if (
|
|
228
|
-
returnValue = returnValue
|
|
301
|
+
if (getSize(returnValue) <= NODE_SIZE) {
|
|
302
|
+
returnValue = setValues(returnValue, getValues(returnValue).flat());
|
|
229
303
|
}
|
|
230
304
|
return returnValue;
|
|
231
305
|
}
|
|
@@ -233,23 +307,33 @@ export const buildModule = (
|
|
|
233
307
|
path = path.pop();
|
|
234
308
|
({ node, index } = path.value);
|
|
235
309
|
|
|
236
|
-
values =
|
|
310
|
+
values = getValues(node).slice();
|
|
237
311
|
|
|
238
312
|
values.splice(index, 1, returnValue);
|
|
239
313
|
|
|
240
314
|
if (adjustSibling) {
|
|
241
315
|
const { index, node } = adjustSibling;
|
|
242
|
-
|
|
316
|
+
if (node) {
|
|
317
|
+
values.splice(index, 1, node);
|
|
318
|
+
} else {
|
|
319
|
+
values.splice(index, 1);
|
|
320
|
+
}
|
|
243
321
|
}
|
|
244
322
|
|
|
245
323
|
returnValue = node = setValues(node, values);
|
|
246
324
|
}
|
|
247
325
|
};
|
|
248
326
|
|
|
327
|
+
const pop = (tree) => {
|
|
328
|
+
return removeAt(-1, tree);
|
|
329
|
+
};
|
|
330
|
+
|
|
249
331
|
const isValidNode = (node) => {
|
|
250
332
|
if (!isArray(node)) return false;
|
|
251
333
|
const values = getValues(node);
|
|
252
|
-
|
|
334
|
+
if (!isArray(values) || values.length > NODE_SIZE) return false; // ;
|
|
335
|
+
|
|
336
|
+
return !node[0] || isFinite(node[0]) || ['object', 'string'].includes(typeof node[0]);
|
|
253
337
|
};
|
|
254
338
|
|
|
255
339
|
const assertValidNode = (node) => {
|
|
@@ -272,10 +356,6 @@ export const buildModule = (
|
|
|
272
356
|
return isFinite(node[0]) || statsReducer ? treeFromValues(values) : freeze(values);
|
|
273
357
|
};
|
|
274
358
|
|
|
275
|
-
const isLeafNode = (node) => {
|
|
276
|
-
return !isArray(getValues(node)[0]);
|
|
277
|
-
};
|
|
278
|
-
|
|
279
359
|
function* traverse(tree) {
|
|
280
360
|
let states = emptyStack.push({ node: tree, i: 0 });
|
|
281
361
|
|
|
@@ -284,23 +364,23 @@ export const buildModule = (
|
|
|
284
364
|
stack: while (states.size) {
|
|
285
365
|
const s = states.value;
|
|
286
366
|
const { node } = s;
|
|
287
|
-
const isLeaf = isLeafNode(node);
|
|
288
367
|
|
|
289
368
|
const values = getValues(node);
|
|
290
369
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
} else {
|
|
296
|
-
for (let { i } = s; s.i < values.length; ) {
|
|
297
|
-
const node = values[i];
|
|
370
|
+
for (let { i } = s; s.i < values.length; ) {
|
|
371
|
+
const value = values[i];
|
|
372
|
+
if (isArray(value)) {
|
|
373
|
+
let node = value;
|
|
298
374
|
assertValidNode(node);
|
|
299
375
|
states = states.push({ node, i: 0 });
|
|
300
376
|
i = ++s.i;
|
|
301
377
|
continue stack;
|
|
378
|
+
} else {
|
|
379
|
+
yield value;
|
|
380
|
+
i = ++s.i;
|
|
302
381
|
}
|
|
303
382
|
}
|
|
383
|
+
|
|
304
384
|
states = states.pop();
|
|
305
385
|
}
|
|
306
386
|
}
|
|
@@ -317,20 +397,13 @@ export const buildModule = (
|
|
|
317
397
|
}
|
|
318
398
|
};
|
|
319
399
|
|
|
320
|
-
|
|
321
|
-
const increment = backwards ? -1 : 1;
|
|
322
|
-
for (let i = backwards ? count - 1 : 0; backwards ? i >= 0 : i < count; i += increment) {
|
|
323
|
-
yield i;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const findPath = (idx, tree) => {
|
|
400
|
+
const findPath = (idx, tree, depth = Infinity) => {
|
|
328
401
|
if (idx == null) throw new Error();
|
|
329
402
|
|
|
330
403
|
let path = emptyStack;
|
|
331
404
|
|
|
332
405
|
let treeSum = getSize(tree);
|
|
333
|
-
let currentIdx = idx < 0 ? treeSum : 0;
|
|
406
|
+
let currentIdx = idx < 0 ? treeSum - 1 : 0;
|
|
334
407
|
let direction = idx < 0 ? -1 : 1;
|
|
335
408
|
let targetIdx = idx < 0 ? treeSum + idx : idx;
|
|
336
409
|
|
|
@@ -338,33 +411,52 @@ export const buildModule = (
|
|
|
338
411
|
stack: while (node) {
|
|
339
412
|
assertValidNode(node);
|
|
340
413
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
414
|
+
const values = getValues(node);
|
|
415
|
+
let candidateNode;
|
|
416
|
+
|
|
417
|
+
let backwards = idx < 0;
|
|
418
|
+
const increment = backwards ? -1 : 1;
|
|
419
|
+
for (
|
|
420
|
+
let i = backwards ? values.length - 1 : 0;
|
|
421
|
+
backwards ? i >= 0 : i < values.length;
|
|
422
|
+
i += increment
|
|
423
|
+
) {
|
|
424
|
+
let value = values[i];
|
|
425
|
+
if (isArray(value) && path.size < depth - 1) {
|
|
426
|
+
candidateNode = value;
|
|
354
427
|
|
|
355
|
-
for (i of indexes(values.length, idx < 0)) {
|
|
356
|
-
candidateNode = values[i];
|
|
357
428
|
const sum = getSize(candidateNode);
|
|
429
|
+
|
|
358
430
|
const nextCount = currentIdx + sum * direction;
|
|
359
|
-
if (
|
|
431
|
+
if (
|
|
432
|
+
(backwards ? nextCount <= targetIdx : nextCount > targetIdx) ||
|
|
433
|
+
(backwards ? nextCount < 0 : nextCount >= treeSum)
|
|
434
|
+
) {
|
|
360
435
|
path = path.push({ index: i, node });
|
|
361
436
|
node = candidateNode;
|
|
362
437
|
continue stack;
|
|
363
438
|
} else {
|
|
364
439
|
currentIdx += sum * direction;
|
|
365
440
|
}
|
|
441
|
+
} else {
|
|
442
|
+
const sum = getSize(value);
|
|
443
|
+
const nextCount = currentIdx + sum * direction;
|
|
444
|
+
if (!isFinite(targetIdx)) {
|
|
445
|
+
return path.push({ index: targetIdx, node });
|
|
446
|
+
} else if (currentIdx === targetIdx) {
|
|
447
|
+
return path.push({ index: i, node });
|
|
448
|
+
} else if (
|
|
449
|
+
backwards
|
|
450
|
+
? nextCount < targetIdx || nextCount < 0
|
|
451
|
+
: nextCount > targetIdx || nextCount >= treeSum
|
|
452
|
+
) {
|
|
453
|
+
break;
|
|
454
|
+
} else {
|
|
455
|
+
currentIdx += direction;
|
|
456
|
+
}
|
|
366
457
|
}
|
|
367
458
|
}
|
|
459
|
+
return path.push({ index: backwards ? -Infinity : Infinity, node });
|
|
368
460
|
}
|
|
369
461
|
|
|
370
462
|
return null;
|
|
@@ -404,22 +496,25 @@ export const buildModule = (
|
|
|
404
496
|
|
|
405
497
|
return {
|
|
406
498
|
buildModule,
|
|
407
|
-
treeFrom,
|
|
408
|
-
|
|
499
|
+
btreeFrom: treeFrom,
|
|
500
|
+
from: treeFrom,
|
|
501
|
+
btreeFromValues: treeFromValues,
|
|
502
|
+
fromValues: treeFromValues,
|
|
409
503
|
findBalancePoint,
|
|
410
504
|
splitValues,
|
|
411
505
|
collapses,
|
|
412
506
|
nodeCollapses,
|
|
413
507
|
nodeCanDonate,
|
|
414
508
|
pop,
|
|
509
|
+
removeAt,
|
|
415
510
|
push,
|
|
416
511
|
addAt,
|
|
512
|
+
concat,
|
|
417
513
|
isValidNode,
|
|
418
514
|
assertValidNode,
|
|
419
515
|
getValues,
|
|
420
516
|
getSums,
|
|
421
517
|
setValues,
|
|
422
|
-
isLeafNode,
|
|
423
518
|
traverse,
|
|
424
519
|
getSize,
|
|
425
520
|
findPath,
|
package/lib/index.js
CHANGED
|
@@ -3,7 +3,10 @@ import { defaultNodeSize, buildModule } from './enhanceable.js';
|
|
|
3
3
|
export { defaultNodeSize };
|
|
4
4
|
|
|
5
5
|
export const {
|
|
6
|
-
|
|
6
|
+
btreeFrom,
|
|
7
|
+
from,
|
|
8
|
+
btreeFromValues,
|
|
9
|
+
fromValues,
|
|
7
10
|
findBalancePoint,
|
|
8
11
|
splitValues,
|
|
9
12
|
collapses,
|
|
@@ -17,7 +20,6 @@ export const {
|
|
|
17
20
|
getValues,
|
|
18
21
|
getSums,
|
|
19
22
|
setValues,
|
|
20
|
-
isLeafNode,
|
|
21
23
|
traverse,
|
|
22
24
|
getSize,
|
|
23
25
|
findPath,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bablr/btree",
|
|
3
3
|
"description": "Functional utilities for working with btrees such as those used in agAST",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"test": "mocha test/*.test.js"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@iter-tools/imm-stack": "1.
|
|
19
|
+
"@iter-tools/imm-stack": "1.2.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#
|
|
22
|
+
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#c97bfa4b3663f8378e9b3e42bb5a41e685406cf9",
|
|
23
23
|
"enhanced-resolve": "^5.12.0",
|
|
24
24
|
"eslint": "^8.32.0",
|
|
25
25
|
"eslint-import-resolver-enhanced-resolve": "^1.0.5",
|