@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.
@@ -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 (!isLeaf && !isArray(value)) {
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 (isArray(value)) throw new Error('cannot add arrays to btrees');
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
- return addAt(getSize(tree), tree, value);
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 pop = (tree) => {
179
- let path = findPath(-1, tree);
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 = freeze(getValues(node).slice(0, -1));
184
- let returnValue = isLeafNode(node)
185
- ? initialValues
186
- : freeze([sumNodes(initialValues), initialValues]);
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 (isFinite(returnValue[0]) && getSize(returnValue) <= NODE_SIZE) {
225
- returnValue = returnValue[1].flat();
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
- return isArray(values); // && values.length <= NODE_SIZE;
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
- if (isLeaf) {
293
- for (let i = 0; i < values.length; i++) {
294
- yield values[i];
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
- if (isLeafNode(node)) {
336
- const startIdx = idx < 0 ? currentIdx - getSize(node) : currentIdx;
337
- let index = isFinite(currentIdx) ? targetIdx - startIdx : currentIdx;
338
- if (index < 0) {
339
- index = -Infinity;
340
- } else if (index >= getSize(node)) {
341
- index = Infinity;
342
- }
343
- return path.push({ index, node });
344
- } else {
345
- const values = node[1];
346
- let candidateNode;
347
- let i;
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 (backwards ? nextCount <= targetIdx : nextCount > targetIdx || nextCount >= treeSum) {
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
@@ -20,7 +20,6 @@ export const {
20
20
  getValues,
21
21
  getSums,
22
22
  setValues,
23
- isLeafNode,
24
23
  traverse,
25
24
  getSize,
26
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.0",
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.1.0"
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",