@bablr/btree 0.4.0 → 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 +148 -55
- package/lib/index.js +0 -1
- package/package.json +2 -2
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,32 +104,94 @@ 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
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
|
-
if (isLeaf && isArray(value) && node.length) throw new Error();
|
|
104
|
-
|
|
105
115
|
const newValues = values.slice();
|
|
106
116
|
newValues[idx] = value;
|
|
107
117
|
return treeFromValues(newValues);
|
|
108
118
|
};
|
|
109
119
|
|
|
120
|
+
const concat = (first, second) => {
|
|
121
|
+
if (!isArray(first) || !isArray(second)) throw new Error();
|
|
122
|
+
|
|
123
|
+
let firstHeight = getHeight(first);
|
|
124
|
+
let secondHeight = getHeight(second);
|
|
125
|
+
let firstValues = getValues(first);
|
|
126
|
+
let secondValues = getValues(second);
|
|
127
|
+
|
|
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
|
+
|
|
110
186
|
const addAt = (idx, tree, value) => {
|
|
111
187
|
if (idx < 0 || !Number.isFinite(idx)) throw new Error('invalid argument');
|
|
112
188
|
if (!isArray(tree)) throw new Error();
|
|
113
|
-
if (
|
|
189
|
+
if (!value) throw new Error();
|
|
114
190
|
|
|
115
191
|
let path = findPath(idx, tree);
|
|
116
192
|
|
|
117
193
|
let { node, index } = path.value;
|
|
118
194
|
|
|
119
|
-
// left pushout vs right pushout?
|
|
120
195
|
let pushout = value;
|
|
121
196
|
|
|
122
197
|
let values = getValues(node);
|
|
@@ -160,7 +235,8 @@ export const buildModule = (
|
|
|
160
235
|
};
|
|
161
236
|
|
|
162
237
|
const push = (tree, value) => {
|
|
163
|
-
|
|
238
|
+
let size = getSize(tree);
|
|
239
|
+
return size ? addAt(size, tree, value) : treeFromValues([value]);
|
|
164
240
|
};
|
|
165
241
|
|
|
166
242
|
const collapses = (size) => {
|
|
@@ -175,15 +251,16 @@ export const buildModule = (
|
|
|
175
251
|
return collapses(getValues(node).length - 1);
|
|
176
252
|
};
|
|
177
253
|
|
|
178
|
-
const
|
|
179
|
-
let path = findPath(
|
|
254
|
+
const removeAt = (idx, tree) => {
|
|
255
|
+
let path = findPath(idx, tree);
|
|
180
256
|
|
|
181
257
|
let { node, index } = path.value;
|
|
182
258
|
|
|
183
|
-
const initialValues =
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
259
|
+
const initialValues = [...getValues(node)];
|
|
260
|
+
|
|
261
|
+
initialValues.splice(index, 1);
|
|
262
|
+
|
|
263
|
+
let returnValue = !isFinite(node[0]) ? initialValues : treeFromValues(initialValues);
|
|
187
264
|
|
|
188
265
|
for (;;) {
|
|
189
266
|
let values = getValues(returnValue);
|
|
@@ -221,8 +298,8 @@ export const buildModule = (
|
|
|
221
298
|
}
|
|
222
299
|
|
|
223
300
|
if (path.size === 1) {
|
|
224
|
-
if (
|
|
225
|
-
returnValue = returnValue
|
|
301
|
+
if (getSize(returnValue) <= NODE_SIZE) {
|
|
302
|
+
returnValue = setValues(returnValue, getValues(returnValue).flat());
|
|
226
303
|
}
|
|
227
304
|
return returnValue;
|
|
228
305
|
}
|
|
@@ -247,10 +324,16 @@ export const buildModule = (
|
|
|
247
324
|
}
|
|
248
325
|
};
|
|
249
326
|
|
|
327
|
+
const pop = (tree) => {
|
|
328
|
+
return removeAt(-1, tree);
|
|
329
|
+
};
|
|
330
|
+
|
|
250
331
|
const isValidNode = (node) => {
|
|
251
332
|
if (!isArray(node)) return false;
|
|
252
333
|
const values = getValues(node);
|
|
253
|
-
|
|
334
|
+
if (!isArray(values) || values.length > NODE_SIZE) return false; // ;
|
|
335
|
+
|
|
336
|
+
return !node[0] || isFinite(node[0]) || ['object', 'string'].includes(typeof node[0]);
|
|
254
337
|
};
|
|
255
338
|
|
|
256
339
|
const assertValidNode = (node) => {
|
|
@@ -273,10 +356,6 @@ export const buildModule = (
|
|
|
273
356
|
return isFinite(node[0]) || statsReducer ? treeFromValues(values) : freeze(values);
|
|
274
357
|
};
|
|
275
358
|
|
|
276
|
-
const isLeafNode = (node) => {
|
|
277
|
-
return !isArray(getValues(node)[0]);
|
|
278
|
-
};
|
|
279
|
-
|
|
280
359
|
function* traverse(tree) {
|
|
281
360
|
let states = emptyStack.push({ node: tree, i: 0 });
|
|
282
361
|
|
|
@@ -285,23 +364,23 @@ export const buildModule = (
|
|
|
285
364
|
stack: while (states.size) {
|
|
286
365
|
const s = states.value;
|
|
287
366
|
const { node } = s;
|
|
288
|
-
const isLeaf = isLeafNode(node);
|
|
289
367
|
|
|
290
368
|
const values = getValues(node);
|
|
291
369
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
} else {
|
|
297
|
-
for (let { i } = s; s.i < values.length; ) {
|
|
298
|
-
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;
|
|
299
374
|
assertValidNode(node);
|
|
300
375
|
states = states.push({ node, i: 0 });
|
|
301
376
|
i = ++s.i;
|
|
302
377
|
continue stack;
|
|
378
|
+
} else {
|
|
379
|
+
yield value;
|
|
380
|
+
i = ++s.i;
|
|
303
381
|
}
|
|
304
382
|
}
|
|
383
|
+
|
|
305
384
|
states = states.pop();
|
|
306
385
|
}
|
|
307
386
|
}
|
|
@@ -318,13 +397,13 @@ export const buildModule = (
|
|
|
318
397
|
}
|
|
319
398
|
};
|
|
320
399
|
|
|
321
|
-
const findPath = (idx, tree) => {
|
|
400
|
+
const findPath = (idx, tree, depth = Infinity) => {
|
|
322
401
|
if (idx == null) throw new Error();
|
|
323
402
|
|
|
324
403
|
let path = emptyStack;
|
|
325
404
|
|
|
326
405
|
let treeSum = getSize(tree);
|
|
327
|
-
let currentIdx = idx < 0 ? treeSum : 0;
|
|
406
|
+
let currentIdx = idx < 0 ? treeSum - 1 : 0;
|
|
328
407
|
let direction = idx < 0 ? -1 : 1;
|
|
329
408
|
let targetIdx = idx < 0 ? treeSum + idx : idx;
|
|
330
409
|
|
|
@@ -332,39 +411,52 @@ export const buildModule = (
|
|
|
332
411
|
stack: while (node) {
|
|
333
412
|
assertValidNode(node);
|
|
334
413
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
let backwards = idx < 0;
|
|
350
|
-
const increment = backwards ? -1 : 1;
|
|
351
|
-
for (
|
|
352
|
-
let i = backwards ? values.length - 1 : 0;
|
|
353
|
-
backwards ? i >= 0 : i < values.length;
|
|
354
|
-
i += increment
|
|
355
|
-
) {
|
|
356
|
-
candidateNode = values[i];
|
|
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;
|
|
427
|
+
|
|
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;
|
|
@@ -414,14 +506,15 @@ export const buildModule = (
|
|
|
414
506
|
nodeCollapses,
|
|
415
507
|
nodeCanDonate,
|
|
416
508
|
pop,
|
|
509
|
+
removeAt,
|
|
417
510
|
push,
|
|
418
511
|
addAt,
|
|
512
|
+
concat,
|
|
419
513
|
isValidNode,
|
|
420
514
|
assertValidNode,
|
|
421
515
|
getValues,
|
|
422
516
|
getSums,
|
|
423
517
|
setValues,
|
|
424
|
-
isLeafNode,
|
|
425
518
|
traverse,
|
|
426
519
|
getSize,
|
|
427
520
|
findPath,
|
package/lib/index.js
CHANGED
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.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
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
22
|
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#c97bfa4b3663f8378e9b3e42bb5a41e685406cf9",
|