@mark1russell7/collections 0.9.2 → 0.9.3

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/README.md CHANGED
@@ -57,6 +57,21 @@ const tree = summaryTree(text, ["a", "b", "c"]);
57
57
  assert.equal(tree.summary, "abc");
58
58
  ```
59
59
 
60
+ The entry `./summary-tree` gives the same tree as plain functions over a plain record. A bundler then keeps only the functions that a program calls. The class `SummaryTree` wraps these functions, and its field `record` is the record.
61
+
62
+ ```ts
63
+ import assert from "node:assert/strict";
64
+ import * as Tree from "@mark1russell7/collections/summary-tree";
65
+
66
+ const text: Tree.SummarySpec<string, string> = {
67
+ monoid: { empty: "", combine: (a, b) => a + b },
68
+ measure: (leaf) => leaf,
69
+ };
70
+ const tree = Tree.splice(Tree.of(text, ["a", "b", "c"]), 1, 1, ["x", "y"]);
71
+ assert.equal(Tree.summary(tree), "axyc");
72
+ assert.deepEqual(Tree.locate(tree, (s) => s.length, 2), { index: 2, leaf: "y", offset: 0 });
73
+ ```
74
+
60
75
  ## Dependency graphs
61
76
 
62
77
  The entry `./dag` gives the levels of a graph, and a run of each level with a limit of concurrency.
@@ -106,6 +121,7 @@ The entry `./testing` gives the small checks of a contract suite: `holds`, `same
106
121
  | `@mark1russell7/collections` | Lists, sets, maps, queues, the summary tree, behaviors, channels, lazy operations, `keyBetween` and the other functions of ranks |
107
122
  | `@mark1russell7/collections/dag` | Levels, topological order, a run with a limit of concurrency, filters |
108
123
  | `@mark1russell7/collections/graph` | `hubBreadthFirstOrder` |
124
+ | `@mark1russell7/collections/summary-tree` | The summary tree as plain functions over a plain record |
109
125
  | `@mark1russell7/collections/testing` | The checks of a contract suite |
110
126
  | `@mark1russell7/collections/layers` | `compose` and the types of a stack of layers, in one file with no imports |
111
127
 
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The summary tree as plain functions over a plain record: a persistent sequence of leaves with a summary of each part.
3
+ *
4
+ * The tree is a balanced binary tree (AVL) with one leaf in each node. Each node keeps the summary of its subtree.
5
+ * A summary is a value of a monoid: the caller gives the monoid as data, with `empty` and `combine`. The order of the
6
+ * leaves is kept, so `combine` must be associative, but it can be non-commutative.
7
+ *
8
+ * A tree is a record of its spec and its root. Each function that changes the sequence gives a new record and keeps the
9
+ * old record. The two records share the nodes that did not change. Concatenation, a split at an index or at a summary
10
+ * search, an edit and a lookup are O(log n).
11
+ *
12
+ * Each function is a separate export, so a bundler keeps only the functions that a program calls. The class
13
+ * `SummaryTree` of the root entry wraps the same functions.
14
+ *
15
+ * The algorithms are the "join" algorithms of Blelloch, Ferizovic and Sun, "Just Join for Parallel Ordered Sets"
16
+ * (SPAA 2016).
17
+ *
18
+ * @example
19
+ * // The number of leaves and the total length of the strings
20
+ * import * as Tree from "@mark1russell7/collections/summary-tree";
21
+ * const spec = {
22
+ * monoid: { empty: { count: 0, length: 0 }, combine: (a, b) => ({ count: a.count + b.count, length: a.length + b.length }) },
23
+ * measure: (leaf: string) => ({ count: 1, length: leaf.length }),
24
+ * };
25
+ * const tree = Tree.of(spec, ["ab", "cde", "f"]);
26
+ * Tree.summary(tree); // { count: 3, length: 6 }
27
+ * const [left, right] = Tree.split(tree, (prefix) => prefix.length > 2); // left: ["ab"], right: ["cde", "f"]
28
+ */
29
+ /** A monoid as data: the identity value `empty` and an associative operation `combine`. */
30
+ interface SummaryMonoid<Summary> {
31
+ /** The summary of a sequence with no leaf. `combine(empty, s)` and `combine(s, empty)` are equal to `s`. */
32
+ readonly empty: Summary;
33
+ /** This function gives the summary of a sequence from the summaries of its two parts, left first. */
34
+ readonly combine: (left: Summary, right: Summary) => Summary;
35
+ }
36
+ /** The data that a tree needs: the monoid of the summaries and the summary of one leaf. */
37
+ interface SummarySpec<Leaf, Summary> {
38
+ readonly monoid: SummaryMonoid<Summary>;
39
+ /** This function gives the summary of one leaf. */
40
+ readonly measure: (leaf: Leaf) => Summary;
41
+ /**
42
+ * This optional function joins two neighbor leaves at a seam into one leaf, or gives `undefined` to keep both. A seam
43
+ * is the place where `concat`, `append`, `prepend`, `splice`, `insert` or `remove` puts two leaves next to each other.
44
+ * The tree calls it once at each seam, and not between the leaves that one call inserts. `set` does not call it.
45
+ */
46
+ readonly merge?: (left: Leaf, right: Leaf) => Leaf | undefined;
47
+ }
48
+ /** The result of a summary search: the first leaf whose prefix summary satisfies the predicate. */
49
+ interface SummarySearch<Leaf, Summary> {
50
+ /** The index of the leaf. */
51
+ readonly index: number;
52
+ readonly leaf: Leaf;
53
+ /** The summary of the leaves before the leaf. */
54
+ readonly before: Summary;
55
+ /** The summary of the leaves before the leaf, and of the leaf. */
56
+ readonly through: Summary;
57
+ }
58
+ /** The result of `locate`: the leaf that holds a position of an additive key, and the position in that leaf. */
59
+ interface SummaryLocation<Leaf> {
60
+ /** The index of the leaf. */
61
+ readonly index: number;
62
+ readonly leaf: Leaf;
63
+ /** The position in the leaf: the target minus the key of the leaves before the leaf. */
64
+ readonly offset: number;
65
+ }
66
+ /** One node of a tree. The functions do not change a node after they make it. */
67
+ interface SummaryNode<Leaf, Summary> {
68
+ readonly left: SummaryNode<Leaf, Summary> | null;
69
+ readonly leaf: Leaf;
70
+ /** The summary of the leaf of this node only. */
71
+ readonly own: Summary;
72
+ /** The summary of the leaves of the left subtree and the leaf of this node. A copy with the same left subtree and leaf keeps it. */
73
+ readonly inner: Summary;
74
+ readonly right: SummaryNode<Leaf, Summary> | null;
75
+ readonly height: number;
76
+ readonly size: number;
77
+ /** The summary of all the leaves of this subtree. */
78
+ readonly summary: Summary;
79
+ }
80
+ /**
81
+ * A tree as a plain record: its spec and its root. `of` makes one. A function that changes nothing gives the same
82
+ * record. Two trees join only when they have the same `spec` object.
83
+ */
84
+ interface SummaryTreeRecord<Leaf, Summary> {
85
+ readonly spec: SummarySpec<Leaf, Summary>;
86
+ /** The root node, or `null` for a tree with no leaf. */
87
+ readonly root: SummaryNode<Leaf, Summary> | null;
88
+ }
89
+ /**
90
+ * This function makes a persistent tree of leaves with a summary of each part. Use the same `spec` object for each
91
+ * tree that `concat` or `splice` joins.
92
+ */
93
+ declare function of<Leaf, Summary>(spec: SummarySpec<Leaf, Summary>, leaves?: Iterable<Leaf>): SummaryTreeRecord<Leaf, Summary>;
94
+ /** This function gives the number of leaves. */
95
+ declare function size<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): number;
96
+ /** This function tells if the tree has no leaf. */
97
+ declare function isEmpty<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): boolean;
98
+ /** This function gives the summary of all the leaves, in their order. An empty tree has the summary `monoid.empty`. */
99
+ declare function summary<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): Summary;
100
+ /** This function gives the height of the tree: 0 for an empty tree. The height is at most 1.45 log2(size + 2). */
101
+ declare function height<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): number;
102
+ /**
103
+ * This function gives the leaf at an index, or `undefined` when the index is not in the tree. A negative index counts
104
+ * from the end, as `Array.prototype.at` does.
105
+ */
106
+ declare function at<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, index: number): Leaf | undefined;
107
+ /** This function gives the summary of the leaves from `start` to `end`, with `end` not included. */
108
+ declare function summaryOf<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, start?: number, end?: number): Summary;
109
+ /** This function gives the tree of the leaves of `tree`, then the leaves of `other`. Two records must have the same spec. */
110
+ declare function concat<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, other: SummaryTreeRecord<Leaf, Summary> | Iterable<Leaf>): SummaryTreeRecord<Leaf, Summary>;
111
+ /** This function splits the tree into its first `index` leaves and the other leaves. */
112
+ declare function splitAt<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, index: number): [SummaryTreeRecord<Leaf, Summary>, SummaryTreeRecord<Leaf, Summary>];
113
+ /**
114
+ * This function splits the tree before the first leaf whose prefix summary satisfies the predicate. The prefix of a
115
+ * leaf includes the leaf. The predicate must be monotone: after it is true for a prefix, it is true for each longer
116
+ * prefix. When no prefix satisfies it, the right tree is empty.
117
+ */
118
+ declare function split<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, predicate: (prefix: Summary) => boolean): [SummaryTreeRecord<Leaf, Summary>, SummaryTreeRecord<Leaf, Summary>];
119
+ /**
120
+ * This function finds the first leaf whose prefix summary satisfies a monotone predicate, as `split` does. It changes
121
+ * nothing. It gives `undefined` when no prefix satisfies the predicate.
122
+ */
123
+ declare function search<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, predicate: (prefix: Summary) => boolean): SummarySearch<Leaf, Summary> | undefined;
124
+ /**
125
+ * This function finds the leaf that holds a position of an additive key, and it calls no `combine`. The key must be
126
+ * additive and not negative: `key(combine(a, b))` is `key(a) + key(b)`. The function descends by the key of each left
127
+ * subtree and of each leaf, and subtracts it from the target. It gives the first leaf whose prefix key is more than
128
+ * the target, or `undefined` when the target is not in `[0, key(summary))`.
129
+ *
130
+ * @example
131
+ * // The leaf and the offset in it of the code unit at position 1000 of a rope
132
+ * const at = Tree.locate(rope, (s) => s.length, 1000);
133
+ */
134
+ declare function locate<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, key: (summary: Summary) => number, target: number): SummaryLocation<Leaf> | undefined;
135
+ /** This function gives the tree of the leaves from `start` to `end`, with `end` not included. A negative index counts from the end. */
136
+ declare function slice<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, start?: number, end?: number): SummaryTreeRecord<Leaf, Summary>;
137
+ /**
138
+ * This function removes `deleteCount` leaves at `start` and puts the leaves of `inserted` there, as
139
+ * `Array.prototype.splice` does. It gives the new tree, not the removed leaves.
140
+ */
141
+ declare function splice<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, start: number, deleteCount?: number, inserted?: SummaryTreeRecord<Leaf, Summary> | Iterable<Leaf>): SummaryTreeRecord<Leaf, Summary>;
142
+ /** This function puts the leaves of `inserted` before the leaf at `index`. */
143
+ declare function insert<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, index: number, inserted: SummaryTreeRecord<Leaf, Summary> | Iterable<Leaf>): SummaryTreeRecord<Leaf, Summary>;
144
+ /** This function removes the leaves from `start` to `end`, with `end` not included. */
145
+ declare function remove<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, start: number, end?: number): SummaryTreeRecord<Leaf, Summary>;
146
+ /**
147
+ * This function replaces the leaf at an index. A negative index counts from the end, as `Array.prototype.with` does.
148
+ * An index that is not in the tree changes nothing. It copies only the path to the leaf, and it does not call
149
+ * `spec.merge`.
150
+ */
151
+ declare function set<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, index: number, leaf: Leaf): SummaryTreeRecord<Leaf, Summary>;
152
+ /** This function adds leaves after the last leaf. */
153
+ declare function append<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, ...leaves: Leaf[]): SummaryTreeRecord<Leaf, Summary>;
154
+ /** This function adds leaves before the first leaf. */
155
+ declare function prepend<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>, ...leaves: Leaf[]): SummaryTreeRecord<Leaf, Summary>;
156
+ /** This function gives the leaves in their order. */
157
+ declare function leaves<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): Generator<Leaf, void, undefined>;
158
+ /** This function gives the leaves in an array. */
159
+ declare function toArray<Leaf, Summary>(tree: SummaryTreeRecord<Leaf, Summary>): Leaf[];
160
+ export { split as C, toArray as D, summaryOf as E, splice as S, summary as T, remove as _, SummarySpec as a, size as b, at as c, insert as d, isEmpty as f, prepend as g, of as h, SummarySearch as i, concat as l, locate as m, SummaryMonoid as n, SummaryTreeRecord as o, leaves as p, SummaryNode as r, append as s, SummaryLocation as t, height as u, search as v, splitAt as w, slice as x, set as y };
161
+ //# sourceMappingURL=functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.d.ts","names":[],"sources":["../src/summary-tree/functions.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BiB,cAAc;;WAEpB,OAAO;;WAEP,UAAU,MAAM,SAAS,OAAO,YAAY;;;UAItC,YAAY,MAAM;WACxB,QAAQ,cAAc;;WAEtB,UAAU,MAAM,SAAS;;;;;;WAMzB,SAAS,MAAM,MAAM,OAAO,SAAS;;;UAI/B,cAAc,MAAM;;WAE1B;WACA,MAAM;;WAEN,QAAQ;;WAER,SAAS;;;UAIH,gBAAgB;;WAEtB;WACA,MAAM;;WAEN;;;UAIM,YAAY,MAAM;WACxB,MAAM,YAAY,MAAM;WACxB,MAAM;;WAEN,KAAK;;WAEL,OAAO;WACP,OAAO,YAAY,MAAM;WACzB;WACA;;WAEA,SAAS;;;;;;UAOH,kBAAkB,MAAM;WAC9B,MAAM,YAAY,MAAM;;WAExB,MAAM,YAAY,MAAM;;;;;;iBA0OnB,GAAG,MAAM,SAAS,MAAM,YAAY,MAAM,UAAU,SAAQ,SAAS,QAAa,kBAAkB,MAAM;;iBAM1G,KAAK,MAAM,SAAS,MAAM,kBAAkB,MAAM;;iBAKlD,QAAQ,MAAM,SAAS,MAAM,kBAAkB,MAAM;;iBAKrD,QAAQ,MAAM,SAAS,MAAM,kBAAkB,MAAM,WAAW;;iBAKhE,OAAO,MAAM,SAAS,MAAM,kBAAkB,MAAM;;;;;iBAQpD,GAAG,MAAM,SAAS,MAAM,kBAAkB,MAAM,UAAU,gBAAgB;;iBAM1E,UAAU,MAAM,SAAS,MAAM,kBAAkB,MAAM,UAAU,gBAAW,eAAkC;;iBAK9G,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,OAAO,kBAAkB,MAAM,WAAW,SAAS,QAClD,kBAAkB,MAAM;;iBAOX,QAAQ,MAAM,SAC5B,MAAM,kBAAkB,MAAM,UAC9B,iBACE,kBAAkB,MAAM,UAAU,kBAAkB,MAAM;;;;;;iBAU9C,MAAM,MAAM,SAC1B,MAAM,kBAAkB,MAAM,UAC9B,YAAY,QAAQ,uBAClB,kBAAkB,MAAM,UAAU,kBAAkB,MAAM;;;;;iBAS9C,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,YAAY,QAAQ,sBACnB,cAAc,MAAM;;;;;;;;;;;iBA8BP,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,MAAM,SAAS,oBACf,iBACC,gBAAgB;;iBAyBH,MAAM,MAAM,SAC1B,MAAM,kBAAkB,MAAM,UAC9B,gBACA,eACC,kBAAkB,MAAM;;;;;iBAcX,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,eACA,sBACA,WAAU,kBAAkB,MAAM,WAAW,SAAS,QACrD,kBAAkB,MAAM;;iBAkCX,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,eACA,UAAU,kBAAkB,MAAM,WAAW,SAAS,QACrD,kBAAkB,MAAM;;iBAKX,OAAO,MAAM,SAC3B,MAAM,kBAAkB,MAAM,UAC9B,eACA,eACC,kBAAkB,MAAM;;;;;;iBAWX,IAAI,MAAM,SAAS,MAAM,kBAAkB,MAAM,UAAU,eAAe,MAAM,OAAO,kBAAkB,MAAM;;iBAM/G,OAAO,MAAM,SAAS,MAAM,kBAAkB,MAAM,aAAa,QAAQ,SAAS,kBAAkB,MAAM;;iBAK1G,QAAQ,MAAM,SAAS,MAAM,kBAAkB,MAAM,aAAa,QAAQ,SAAS,kBAAkB,MAAM;;iBAO1G,OAAO,MAAM,SAAS,MAAM,kBAAkB,MAAM,WAAW,UAAU;;iBAgB1E,QAAQ,MAAM,SAAS,MAAM,kBAAkB,MAAM,WAAW"}
@@ -0,0 +1,414 @@
1
+ // src/summary-tree/functions.ts
2
+ function heightOf(tree) {
3
+ return tree === null ? 0 : tree.height;
4
+ }
5
+ function sizeOf(tree) {
6
+ return tree === null ? 0 : tree.size;
7
+ }
8
+ function summaryOfRoot(spec, tree) {
9
+ return tree === null ? spec.monoid.empty : tree.summary;
10
+ }
11
+ /** This function makes a node. Give `inner` only when it is the summary of `left` and `own`, to save one `combine`. */
12
+ function node(spec, left, leaf, own, right, inner) {
13
+ if (inner === void 0) inner = left === null ? own : spec.monoid.combine(left.summary, own);
14
+ return {
15
+ left,
16
+ leaf,
17
+ own,
18
+ inner,
19
+ right,
20
+ height: 1 + Math.max(heightOf(left), heightOf(right)),
21
+ size: sizeOf(left) + 1 + sizeOf(right),
22
+ summary: right === null ? inner : spec.monoid.combine(inner, right.summary)
23
+ };
24
+ }
25
+ /** This function copies a node with a new right subtree. It keeps the summary `inner`, so it calls `combine` once at most. */
26
+ function withRight(spec, n, right) {
27
+ return node(spec, n.left, n.leaf, n.own, right, n.inner);
28
+ }
29
+ function rotateLeft(spec, n) {
30
+ const r = n.right;
31
+ return node(spec, withRight(spec, n, r.left), r.leaf, r.own, r.right);
32
+ }
33
+ function rotateRight(spec, n) {
34
+ const l = n.left;
35
+ return withRight(spec, l, node(spec, l.right, n.leaf, n.own, n.right));
36
+ }
37
+ /** This function joins a tree that is higher by more than 1 with a leaf and a lower tree on its right. */
38
+ function joinRight(spec, left, leaf, own, right) {
39
+ const c = left.right;
40
+ if (heightOf(c) <= heightOf(right) + 1) {
41
+ const t = node(spec, c, leaf, own, right);
42
+ if (t.height <= heightOf(left.left) + 1) return withRight(spec, left, t);
43
+ return rotateLeft(spec, withRight(spec, left, rotateRight(spec, t)));
44
+ }
45
+ const t = joinRight(spec, c, leaf, own, right);
46
+ const joined = withRight(spec, left, t);
47
+ return t.height <= heightOf(left.left) + 1 ? joined : rotateLeft(spec, joined);
48
+ }
49
+ /** This function joins a lower tree on the left with a leaf and a tree that is higher by more than 1. */
50
+ function joinLeft(spec, left, leaf, own, right, inner) {
51
+ const c = right.left;
52
+ if (heightOf(c) <= heightOf(left) + 1) {
53
+ const t = node(spec, left, leaf, own, c, inner);
54
+ if (t.height <= heightOf(right.right) + 1) return node(spec, t, right.leaf, right.own, right.right);
55
+ return rotateRight(spec, node(spec, rotateLeft(spec, t), right.leaf, right.own, right.right));
56
+ }
57
+ const t = joinLeft(spec, left, leaf, own, c, inner);
58
+ const joined = node(spec, t, right.leaf, right.own, right.right);
59
+ return t.height <= heightOf(right.right) + 1 ? joined : rotateRight(spec, joined);
60
+ }
61
+ /**
62
+ * This function gives the tree of the leaves of `left`, then `leaf`, then the leaves of `right`. Give `inner` only
63
+ * when it is the summary of `left` and `own`.
64
+ */
65
+ function join(spec, left, leaf, own, right, inner) {
66
+ if (heightOf(left) > heightOf(right) + 1) return joinRight(spec, left, leaf, own, right);
67
+ if (heightOf(right) > heightOf(left) + 1) return joinLeft(spec, left, leaf, own, right, inner);
68
+ return node(spec, left, leaf, own, right, inner);
69
+ }
70
+ /** This function removes the last leaf of a tree, and gives the rest and the node of that leaf. */
71
+ function splitLast(spec, tree) {
72
+ if (tree.right === null) return [tree.left, tree];
73
+ const [rest, last] = splitLast(spec, tree.right);
74
+ return [join(spec, tree.left, tree.leaf, tree.own, rest, tree.inner), last];
75
+ }
76
+ function concatRoots(spec, left, right) {
77
+ if (left === null) return right;
78
+ if (right === null) return left;
79
+ const [rest, last] = splitLast(spec, left);
80
+ return join(spec, rest, last.leaf, last.own, right);
81
+ }
82
+ /** This function splits a tree into its first `index` leaves and the other leaves. */
83
+ function splitRoot(spec, tree, index) {
84
+ if (tree === null) return [null, null];
85
+ const before = sizeOf(tree.left);
86
+ if (index <= before) {
87
+ const [a, b] = splitRoot(spec, tree.left, index);
88
+ return [a, join(spec, b, tree.leaf, tree.own, tree.right)];
89
+ }
90
+ const [a, b] = splitRoot(spec, tree.right, index - before - 1);
91
+ return [join(spec, tree.left, tree.leaf, tree.own, a, tree.inner), b];
92
+ }
93
+ /** This function splits a tree before the first leaf whose prefix summary, after `acc`, satisfies the predicate. */
94
+ function splitRootBy(spec, tree, predicate, acc) {
95
+ if (tree === null) return [null, null];
96
+ const { combine } = spec.monoid;
97
+ const throughLeft = tree.left === null ? acc : combine(acc, tree.left.summary);
98
+ if (tree.left !== null && predicate(throughLeft)) {
99
+ const [a, b] = splitRootBy(spec, tree.left, predicate, acc);
100
+ return [a, join(spec, b, tree.leaf, tree.own, tree.right)];
101
+ }
102
+ const throughLeaf = combine(throughLeft, tree.own);
103
+ if (predicate(throughLeaf)) return [tree.left, join(spec, null, tree.leaf, tree.own, tree.right)];
104
+ const [a, b] = splitRootBy(spec, tree.right, predicate, throughLeaf);
105
+ return [join(spec, tree.left, tree.leaf, tree.own, a, tree.inner), b];
106
+ }
107
+ /** This function replaces the leaf at an index in the tree. It copies only the path to the leaf. */
108
+ function replaceAt(spec, tree, index, leaf, own) {
109
+ const before = sizeOf(tree.left);
110
+ if (index === before) return node(spec, tree.left, leaf, own, tree.right);
111
+ if (index < before) return node(spec, replaceAt(spec, tree.left, index, leaf, own), tree.leaf, tree.own, tree.right);
112
+ return withRight(spec, tree, replaceAt(spec, tree.right, index - before - 1, leaf, own));
113
+ }
114
+ /** This function puts one leaf before the leaf at an index. It copies only the path, and `join` keeps the balance. */
115
+ function insertAt(spec, tree, index, leaf, own) {
116
+ if (tree === null) return node(spec, null, leaf, own, null);
117
+ const before = sizeOf(tree.left);
118
+ if (index <= before) return join(spec, insertAt(spec, tree.left, index, leaf, own), tree.leaf, tree.own, tree.right);
119
+ return join(spec, tree.left, tree.leaf, tree.own, insertAt(spec, tree.right, index - before - 1, leaf, own), tree.inner);
120
+ }
121
+ /**
122
+ * This function replaces the leaf at an index with one or more leaves. It descends once.
123
+ * At the node of the leaf, it puts the first new leaf in the node.
124
+ * It puts the other new leaves at the start of the right subtree of that node.
125
+ */
126
+ function replaceSpan(spec, tree, index, leaves) {
127
+ const before = sizeOf(tree.left);
128
+ if (index < before) return join(spec, replaceSpan(spec, tree.left, index, leaves), tree.leaf, tree.own, tree.right);
129
+ if (index > before) return join(spec, tree.left, tree.leaf, tree.own, replaceSpan(spec, tree.right, index - before - 1, leaves), tree.inner);
130
+ let right = tree.right;
131
+ for (let i = 1; i < leaves.length; i++) right = insertAt(spec, right, i - 1, leaves[i], spec.measure(leaves[i]));
132
+ return join(spec, tree.left, leaves[0], spec.measure(leaves[0]), right);
133
+ }
134
+ /** This function removes the leaf at an index. It copies only the path, and `join` keeps the balance. */
135
+ function removeAt(spec, tree, index) {
136
+ const before = sizeOf(tree.left);
137
+ if (index === before) return concatRoots(spec, tree.left, tree.right);
138
+ if (index < before) return join(spec, removeAt(spec, tree.left, index), tree.leaf, tree.own, tree.right);
139
+ return join(spec, tree.left, tree.leaf, tree.own, removeAt(spec, tree.right, index - before - 1), tree.inner);
140
+ }
141
+ /** This function gives the leaf at an index of the tree. The index must be in the tree. */
142
+ function leafAt(tree, index) {
143
+ let n = tree;
144
+ for (;;) {
145
+ const before = sizeOf(n.left);
146
+ if (index === before) return n.leaf;
147
+ if (index < before) n = n.left;
148
+ else {
149
+ index -= before + 1;
150
+ n = n.right;
151
+ }
152
+ }
153
+ }
154
+ /** This function joins the leaves at `index` and `index + 1` when `spec.merge` gives one leaf for them. */
155
+ function seam(spec, tree, index) {
156
+ const { merge } = spec;
157
+ if (merge === void 0 || tree === null || index < 0 || index + 1 >= tree.size) return tree;
158
+ const merged = merge(leafAt(tree, index), leafAt(tree, index + 1));
159
+ if (merged === void 0) return tree;
160
+ return removeAt(spec, replaceAt(spec, tree, index, merged, spec.measure(merged)), index + 1);
161
+ }
162
+ /** This function makes a balanced tree of the leaves `items[lo]` to `items[hi - 1]`. */
163
+ function build(spec, items, lo, hi) {
164
+ if (lo >= hi) return null;
165
+ const mid = lo + hi >>> 1;
166
+ const leaf = items[mid];
167
+ return node(spec, build(spec, items, lo, mid), leaf, spec.measure(leaf), build(spec, items, mid + 1, hi));
168
+ }
169
+ /** The largest number of removed and inserted leaves for which `splice` copies paths instead of splitting the tree. */
170
+ const SMALL_EDIT = 8;
171
+ /** This function gives the index of a leaf, with a negative index counted from the end, or -1 when it is not in the tree. */
172
+ function indexOf(index, size) {
173
+ if (!Number.isInteger(index)) return -1;
174
+ const at = index < 0 ? size + index : index;
175
+ return at >= 0 && at < size ? at : -1;
176
+ }
177
+ function clamp(value, size) {
178
+ if (Number.isNaN(value)) return 0;
179
+ const whole = Math.trunc(value);
180
+ return whole < 0 ? Math.max(0, size + whole) : Math.min(whole, size);
181
+ }
182
+ /** This function gives the record with a new root, or the same record when the root does not change. */
183
+ function withRoot(tree, root) {
184
+ return root === tree.root ? tree : {
185
+ spec: tree.spec,
186
+ root
187
+ };
188
+ }
189
+ /** A record has no iterator. Each other value of the union is an iterable of leaves, a string too. */
190
+ function isRecord(value) {
191
+ return typeof value === "object" && value !== null && !(Symbol.iterator in value);
192
+ }
193
+ /** This function gives the root of a record of the same spec, or the root of a new tree of the leaves. */
194
+ function rootOf(spec, leaves) {
195
+ if (isRecord(leaves)) {
196
+ if (leaves.spec !== spec) throw new TypeError("SummaryTree: the two trees have different specs");
197
+ return leaves.root;
198
+ }
199
+ const items = [...leaves];
200
+ return build(spec, items, 0, items.length);
201
+ }
202
+ /**
203
+ * This function makes a persistent tree of leaves with a summary of each part. Use the same `spec` object for each
204
+ * tree that `concat` or `splice` joins.
205
+ */
206
+ function of(spec, leaves = []) {
207
+ const items = [...leaves];
208
+ return {
209
+ spec,
210
+ root: build(spec, items, 0, items.length)
211
+ };
212
+ }
213
+ /** This function gives the number of leaves. */
214
+ function size(tree) {
215
+ return sizeOf(tree.root);
216
+ }
217
+ /** This function tells if the tree has no leaf. */
218
+ function isEmpty(tree) {
219
+ return tree.root === null;
220
+ }
221
+ /** This function gives the summary of all the leaves, in their order. An empty tree has the summary `monoid.empty`. */
222
+ function summary(tree) {
223
+ return summaryOfRoot(tree.spec, tree.root);
224
+ }
225
+ /** This function gives the height of the tree: 0 for an empty tree. The height is at most 1.45 log2(size + 2). */
226
+ function height(tree) {
227
+ return heightOf(tree.root);
228
+ }
229
+ /**
230
+ * This function gives the leaf at an index, or `undefined` when the index is not in the tree. A negative index counts
231
+ * from the end, as `Array.prototype.at` does.
232
+ */
233
+ function at(tree, index) {
234
+ const i = indexOf(index, sizeOf(tree.root));
235
+ return i === -1 ? void 0 : leafAt(tree.root, i);
236
+ }
237
+ /** This function gives the summary of the leaves from `start` to `end`, with `end` not included. */
238
+ function summaryOf(tree, start = 0, end = sizeOf(tree.root)) {
239
+ return summary(slice(tree, start, end));
240
+ }
241
+ /** This function gives the tree of the leaves of `tree`, then the leaves of `other`. Two records must have the same spec. */
242
+ function concat(tree, other) {
243
+ const { spec } = tree;
244
+ return withRoot(tree, seam(spec, concatRoots(spec, tree.root, rootOf(spec, other)), sizeOf(tree.root) - 1));
245
+ }
246
+ /** This function splits the tree into its first `index` leaves and the other leaves. */
247
+ function splitAt(tree, index) {
248
+ const [left, right] = splitRoot(tree.spec, tree.root, clamp(index, sizeOf(tree.root)));
249
+ return [withRoot(tree, left), withRoot(tree, right)];
250
+ }
251
+ /**
252
+ * This function splits the tree before the first leaf whose prefix summary satisfies the predicate. The prefix of a
253
+ * leaf includes the leaf. The predicate must be monotone: after it is true for a prefix, it is true for each longer
254
+ * prefix. When no prefix satisfies it, the right tree is empty.
255
+ */
256
+ function split(tree, predicate) {
257
+ const [left, right] = splitRootBy(tree.spec, tree.root, predicate, tree.spec.monoid.empty);
258
+ return [withRoot(tree, left), withRoot(tree, right)];
259
+ }
260
+ /**
261
+ * This function finds the first leaf whose prefix summary satisfies a monotone predicate, as `split` does. It changes
262
+ * nothing. It gives `undefined` when no prefix satisfies the predicate.
263
+ */
264
+ function search(tree, predicate) {
265
+ const { combine, empty } = tree.spec.monoid;
266
+ let n = tree.root;
267
+ let acc = empty;
268
+ let index = 0;
269
+ while (n !== null) {
270
+ const throughLeft = n.left === null ? acc : combine(acc, n.left.summary);
271
+ if (n.left !== null && predicate(throughLeft)) {
272
+ n = n.left;
273
+ continue;
274
+ }
275
+ const through = combine(throughLeft, n.own);
276
+ if (predicate(through)) return {
277
+ index: index + sizeOf(n.left),
278
+ leaf: n.leaf,
279
+ before: throughLeft,
280
+ through
281
+ };
282
+ index += sizeOf(n.left) + 1;
283
+ acc = through;
284
+ n = n.right;
285
+ }
286
+ }
287
+ /**
288
+ * This function finds the leaf that holds a position of an additive key, and it calls no `combine`. The key must be
289
+ * additive and not negative: `key(combine(a, b))` is `key(a) + key(b)`. The function descends by the key of each left
290
+ * subtree and of each leaf, and subtracts it from the target. It gives the first leaf whose prefix key is more than
291
+ * the target, or `undefined` when the target is not in `[0, key(summary))`.
292
+ *
293
+ * @example
294
+ * // The leaf and the offset in it of the code unit at position 1000 of a rope
295
+ * const at = Tree.locate(rope, (s) => s.length, 1000);
296
+ */
297
+ function locate(tree, key, target) {
298
+ if (!(target >= 0)) return void 0;
299
+ let n = tree.root;
300
+ let index = 0;
301
+ let rest = target;
302
+ while (n !== null) {
303
+ if (n.left !== null) {
304
+ const left = key(n.left.summary);
305
+ if (rest < left) {
306
+ n = n.left;
307
+ continue;
308
+ }
309
+ rest -= left;
310
+ index += n.left.size;
311
+ }
312
+ const own = key(n.own);
313
+ if (rest < own) return {
314
+ index,
315
+ leaf: n.leaf,
316
+ offset: rest
317
+ };
318
+ rest -= own;
319
+ index += 1;
320
+ n = n.right;
321
+ }
322
+ }
323
+ /** This function gives the tree of the leaves from `start` to `end`, with `end` not included. A negative index counts from the end. */
324
+ function slice(tree, start = 0, end = sizeOf(tree.root)) {
325
+ const n = sizeOf(tree.root);
326
+ const from = clamp(start, n);
327
+ const to = clamp(end, n);
328
+ if (to <= from) return withRoot(tree, null);
329
+ const [, rest] = splitRoot(tree.spec, tree.root, from);
330
+ const [middle] = splitRoot(tree.spec, rest, to - from);
331
+ return withRoot(tree, middle);
332
+ }
333
+ /**
334
+ * This function removes `deleteCount` leaves at `start` and puts the leaves of `inserted` there, as
335
+ * `Array.prototype.splice` does. It gives the new tree, not the removed leaves.
336
+ */
337
+ function splice(tree, start, deleteCount = sizeOf(tree.root), inserted = []) {
338
+ const { spec } = tree;
339
+ const n = sizeOf(tree.root);
340
+ const from = clamp(start, n);
341
+ const count = Math.max(0, Math.min(Number.isNaN(deleteCount) ? 0 : Math.trunc(deleteCount), n - from));
342
+ const leaves = isRecord(inserted) ? void 0 : [...inserted];
343
+ const added = leaves === void 0 ? sizeOf(inserted.root) : leaves.length;
344
+ if (count === 0 && added === 0) return tree;
345
+ let root = tree.root;
346
+ if (leaves !== void 0 && count + added <= SMALL_EDIT) {
347
+ const replaced = Math.min(count, added);
348
+ if (count > 0 && added > count) {
349
+ for (let i = 0; i < count - 1; i++) root = replaceAt(spec, root, from + i, leaves[i], spec.measure(leaves[i]));
350
+ root = replaceSpan(spec, root, from + count - 1, leaves.slice(count - 1));
351
+ } else for (let i = 0; i < added; i++) {
352
+ const leaf = leaves[i];
353
+ const own = spec.measure(leaf);
354
+ root = i < replaced ? replaceAt(spec, root, from + i, leaf, own) : insertAt(spec, root, from + i, leaf, own);
355
+ }
356
+ for (let i = replaced; i < count; i++) root = removeAt(spec, root, from + added);
357
+ } else {
358
+ const middle = leaves === void 0 ? rootOf(spec, inserted) : build(spec, leaves, 0, leaves.length);
359
+ const [left, rest] = splitRoot(spec, root, from);
360
+ const [, right] = splitRoot(spec, rest, count);
361
+ root = concatRoots(spec, concatRoots(spec, left, middle), right);
362
+ }
363
+ if (added > 0) root = seam(spec, root, from + added - 1);
364
+ return withRoot(tree, seam(spec, root, from - 1));
365
+ }
366
+ /** This function puts the leaves of `inserted` before the leaf at `index`. */
367
+ function insert(tree, index, inserted) {
368
+ return splice(tree, index, 0, inserted);
369
+ }
370
+ /** This function removes the leaves from `start` to `end`, with `end` not included. */
371
+ function remove(tree, start, end = sizeOf(tree.root)) {
372
+ const n = sizeOf(tree.root);
373
+ const from = clamp(start, n);
374
+ return splice(tree, from, Math.max(0, clamp(end, n) - from));
375
+ }
376
+ /**
377
+ * This function replaces the leaf at an index. A negative index counts from the end, as `Array.prototype.with` does.
378
+ * An index that is not in the tree changes nothing. It copies only the path to the leaf, and it does not call
379
+ * `spec.merge`.
380
+ */
381
+ function set(tree, index, leaf) {
382
+ const i = indexOf(index, sizeOf(tree.root));
383
+ return i === -1 ? tree : withRoot(tree, replaceAt(tree.spec, tree.root, i, leaf, tree.spec.measure(leaf)));
384
+ }
385
+ /** This function adds leaves after the last leaf. */
386
+ function append(tree, ...leaves) {
387
+ return concat(tree, leaves);
388
+ }
389
+ /** This function adds leaves before the first leaf. */
390
+ function prepend(tree, ...leaves) {
391
+ const { spec } = tree;
392
+ return withRoot(tree, seam(spec, concatRoots(spec, rootOf(spec, leaves), tree.root), leaves.length - 1));
393
+ }
394
+ /** This function gives the leaves in their order. */
395
+ function* leaves(tree) {
396
+ const stack = [];
397
+ let n = tree.root;
398
+ while (n !== null || stack.length > 0) {
399
+ while (n !== null) {
400
+ stack.push(n);
401
+ n = n.left;
402
+ }
403
+ const top = stack.pop();
404
+ yield top.leaf;
405
+ n = top.right;
406
+ }
407
+ }
408
+ /** This function gives the leaves in an array. */
409
+ function toArray(tree) {
410
+ return [...leaves(tree)];
411
+ }
412
+ export { split as _, insert as a, summaryOf as b, locate as c, remove as d, search as f, splice as g, slice as h, height as i, of as l, size as m, at as n, isEmpty as o, set as p, concat as r, leaves as s, append as t, prepend as u, splitAt as v, toArray as x, summary as y };
413
+
414
+ //# sourceMappingURL=functions.js.map