@fluidframework/matrix 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.0
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/.eslintrc.js +20 -21
- package/.mocharc.js +2 -2
- package/README.md +21 -21
- package/api-extractor.json +2 -2
- package/bench/bsp-set-optimizations.md +5 -5
- package/bench/src/index.ts +38 -20
- package/bench/src/read/map.ts +16 -10
- package/bench/src/read/nativearray.ts +16 -10
- package/bench/src/read/test.ts +17 -19
- package/bench/src/read/tiled.ts +240 -181
- package/bench/src/util.ts +19 -18
- package/bench/tsconfig.json +8 -13
- package/dist/bspSet.d.ts.map +1 -1
- package/dist/bspSet.js.map +1 -1
- package/dist/handlecache.d.ts.map +1 -1
- package/dist/handlecache.js +1 -3
- package/dist/handlecache.js.map +1 -1
- package/dist/handletable.d.ts.map +1 -1
- package/dist/handletable.js +7 -3
- package/dist/handletable.js.map +1 -1
- package/dist/matrix.d.ts +1 -1
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +28 -19
- package/dist/matrix.js.map +1 -1
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/permutationvector.d.ts.map +1 -1
- package/dist/permutationvector.js +15 -8
- package/dist/permutationvector.js.map +1 -1
- package/dist/productSet.d.ts.map +1 -1
- package/dist/productSet.js +6 -3
- package/dist/productSet.js.map +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js.map +1 -1
- package/dist/sparsearray2d.d.ts.map +1 -1
- package/dist/sparsearray2d.js +12 -12
- package/dist/sparsearray2d.js.map +1 -1
- package/dist/split.d.ts.map +1 -1
- package/dist/split.js +5 -3
- package/dist/split.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/undoprovider.d.ts.map +1 -1
- package/dist/undoprovider.js.map +1 -1
- package/lib/bspSet.d.ts.map +1 -1
- package/lib/bspSet.js.map +1 -1
- package/lib/handlecache.d.ts.map +1 -1
- package/lib/handlecache.js +1 -3
- package/lib/handlecache.js.map +1 -1
- package/lib/handletable.d.ts.map +1 -1
- package/lib/handletable.js +7 -3
- package/lib/handletable.js.map +1 -1
- package/lib/matrix.d.ts +1 -1
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.js +29 -20
- package/lib/matrix.js.map +1 -1
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/permutationvector.d.ts.map +1 -1
- package/lib/permutationvector.js +15 -8
- package/lib/permutationvector.js.map +1 -1
- package/lib/productSet.d.ts.map +1 -1
- package/lib/productSet.js +6 -3
- package/lib/productSet.js.map +1 -1
- package/lib/range.d.ts.map +1 -1
- package/lib/range.js.map +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/serialization.d.ts.map +1 -1
- package/lib/serialization.js.map +1 -1
- package/lib/sparsearray2d.d.ts.map +1 -1
- package/lib/sparsearray2d.js +12 -12
- package/lib/sparsearray2d.js.map +1 -1
- package/lib/split.d.ts.map +1 -1
- package/lib/split.js +5 -3
- package/lib/split.js.map +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/undoprovider.d.ts.map +1 -1
- package/lib/undoprovider.js +1 -1
- package/lib/undoprovider.js.map +1 -1
- package/package.json +52 -52
- package/prettier.config.cjs +1 -1
- package/src/bspSet.ts +507 -434
- package/src/handlecache.ts +114 -112
- package/src/handletable.ts +66 -62
- package/src/matrix.ts +781 -710
- package/src/ops.ts +11 -11
- package/src/packageVersion.ts +1 -1
- package/src/permutationvector.ts +425 -368
- package/src/productSet.ts +852 -788
- package/src/range.ts +8 -8
- package/src/runtime.ts +35 -35
- package/src/serialization.ts +13 -9
- package/src/sparsearray2d.ts +196 -192
- package/src/split.ts +111 -90
- package/src/types.ts +3 -3
- package/src/undoprovider.ts +161 -144
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
package/src/bspSet.ts
CHANGED
|
@@ -13,19 +13,19 @@ export const pair = <S, T = S>(left: S, right: T): Pair<S, T> => [left, right];
|
|
|
13
13
|
const ObjectAssign: typeof Object.assign = __assign;
|
|
14
14
|
|
|
15
15
|
export enum SetKind {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Dense,
|
|
17
|
+
Empty,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface TreeNode<Left, Right> {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
readonly left: Left;
|
|
22
|
+
readonly right: Right;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
interface KeyNode<Key, Exact> {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
readonly key: Key;
|
|
27
|
+
readonly pathKey: Key;
|
|
28
|
+
readonly isExact: Exact;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export type Empty = SetKind.Empty;
|
|
@@ -56,18 +56,21 @@ type KeyExact<Key> = KeyNode<Key, true>;
|
|
|
56
56
|
type KeyUndefined = KeyNode<undefined, false>;
|
|
57
57
|
type KeyDefined<Key> = KeyUnexact<Key> | KeyExact<Key>;
|
|
58
58
|
|
|
59
|
-
type BalancePropertyHelper<Key, Left, Right> = TreeNode<
|
|
59
|
+
type BalancePropertyHelper<Key, Left, Right> = TreeNode<
|
|
60
|
+
UntypedSparse<Key> | Left,
|
|
61
|
+
UntypedSparse<Key> | Right
|
|
62
|
+
>;
|
|
60
63
|
|
|
61
64
|
type TreeDefined<Key> =
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
| BalancePropertyHelper<Key, Empty, Dense>
|
|
66
|
+
| BalancePropertyHelper<Key, Dense, Empty>;
|
|
64
67
|
type TreeUndefined = TreeNode<undefined, undefined>;
|
|
65
68
|
|
|
66
69
|
type KeyDefinednessProperty<Key> = KeyDefined<Key> | KeyUndefined;
|
|
67
70
|
|
|
68
71
|
export type UntypedSparse<Key> =
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
| (KeyDefinednessProperty<Key> & TreeDefined<Key>)
|
|
73
|
+
| (KeyDefined<Key> & TreeUndefined);
|
|
71
74
|
/** The term *untyped* refers to the tree representation of a BSP set. Because BSP set trees are only compared in terms
|
|
72
75
|
* of their structure, we need to ensure that cuts occur at the same exact points across all possible sets. This is
|
|
73
76
|
* enforced by the fact, that at construction time, we attach an `Id` to each BSP set and only allow operations to
|
|
@@ -81,569 +84,639 @@ export type UntypedBspSet<Key> = Empty | Dense | UntypedSparse<Key>;
|
|
|
81
84
|
|
|
82
85
|
/** A set is considred *sparse*, if we know that w.r.t. to it's bounds it is neither empty, nor dense. */
|
|
83
86
|
interface Sparse<Key extends Cachable<Key>, Id> {
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
setOperations: SetOperations<Key, Id>;
|
|
88
|
+
root: UntypedSparse<Key>;
|
|
86
89
|
}
|
|
87
|
-
export type BspSet<Key extends Cachable<Key>, Id> =
|
|
88
|
-
| Empty
|
|
89
|
-
| Dense
|
|
90
|
-
| Sparse<Key, Id>;
|
|
90
|
+
export type BspSet<Key extends Cachable<Key>, Id> = Empty | Dense | Sparse<Key, Id>;
|
|
91
91
|
|
|
92
92
|
export interface KeyCache<T> {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
depth?: number;
|
|
94
|
+
split?: Pair<Pair<CachedKey<T>, number>>;
|
|
95
95
|
}
|
|
96
96
|
export type CachedKey<T> = T & KeyCache<T>;
|
|
97
97
|
export type Cachable<T> = Disjoint<keyof T, keyof KeyCache<T>>;
|
|
98
98
|
|
|
99
|
-
export type Disjoint<T, U> = [T, U] extends [Exclude<T, U>, Exclude<U, T>]
|
|
100
|
-
? any
|
|
101
|
-
: never;
|
|
99
|
+
export type Disjoint<T, U> = [T, U] extends [Exclude<T, U>, Exclude<U, T>] ? any : never;
|
|
102
100
|
export type RequireAtLeastOne<T> = {
|
|
103
|
-
|
|
104
|
-
Partial<Pick<T, Exclude<keyof T, K>>>;
|
|
101
|
+
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>;
|
|
105
102
|
}[keyof T];
|
|
106
103
|
|
|
107
104
|
/** This is a concrete set operations implementation, tagged with an arbitrary id. */
|
|
108
105
|
export interface SetOperations<Key extends Cachable<Key>, Id> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
106
|
+
/** Id here is just a phantom type, so that we can associate the various set instances together */
|
|
107
|
+
readonly id: Id;
|
|
108
|
+
|
|
109
|
+
/** Split the key into two. This will only be called when the current key is incomparable with an element.
|
|
110
|
+
* E.g. this would never be called if the key is already a 1x1 rectangle. */
|
|
111
|
+
readonly split: (key: CachedKey<Key>) => Pair<Pair<CachedKey<Key>, number>>;
|
|
112
|
+
|
|
113
|
+
/** Tells, if a given key can be split further */
|
|
114
|
+
readonly canSplit: (key: CachedKey<Key>) => boolean;
|
|
115
|
+
|
|
116
|
+
/** Tells if two keys overlap at all. */
|
|
117
|
+
readonly meets: (key1: Key, key2: Key) => boolean;
|
|
118
|
+
|
|
119
|
+
/** Intersect the keys, if it is possible to exactly respresent their intersection.
|
|
120
|
+
* An implementation is never required to compute the intersection as this is just an optimization.
|
|
121
|
+
* Precondition: It is guaranteed that the keys meet and that they are incomparable.
|
|
122
|
+
*/
|
|
123
|
+
readonly intersect: (key1: Key, key2: Key) => Key | undefined;
|
|
124
|
+
|
|
125
|
+
/** Unions the keys, if it is possible to exactly represent their union.
|
|
126
|
+
* An implementation is never required to compute the union as this is just an optimization.
|
|
127
|
+
* Precondition: It is guaranteed that the keys are incomparable.
|
|
128
|
+
*/
|
|
129
|
+
readonly union: (key1: Key, key2: Key) => Key | undefined;
|
|
130
|
+
|
|
131
|
+
/** Computes the set difference between two keys, if it is possible to exactly represent their set difference.
|
|
132
|
+
* An implementation is never required to compute the difference as this is just an optimization.
|
|
133
|
+
* Precondition: It is guaranteed that the keys meet.
|
|
134
|
+
*/
|
|
135
|
+
readonly except: (key1: Key, key2: Key) => Key | undefined;
|
|
136
|
+
|
|
137
|
+
/** Compare two keys */
|
|
138
|
+
readonly compare: (key1: Key, key2: Key) => -1 | 0 | 1 | undefined;
|
|
139
|
+
|
|
140
|
+
/** The top element of the set. */
|
|
141
|
+
readonly top: Key;
|
|
145
142
|
}
|
|
146
143
|
|
|
147
|
-
export const cacheKeySplitting =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
144
|
+
export const cacheKeySplitting =
|
|
145
|
+
<Key>(
|
|
146
|
+
splitFunction: (key: CachedKey<Key>) => Pair<Pair<CachedKey<Key>, number>>,
|
|
147
|
+
top: CachedKey<Key>,
|
|
148
|
+
maxDepth: number = 10,
|
|
149
|
+
) =>
|
|
150
|
+
(key: CachedKey<Key>) => {
|
|
151
|
+
if (key.split !== undefined) {
|
|
152
|
+
return key.split;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const split = splitFunction(key);
|
|
156
|
+
const depth = key === top ? 0 : key.depth;
|
|
157
|
+
if (depth !== undefined && depth < maxDepth) {
|
|
158
|
+
key.split = split;
|
|
159
|
+
split[0][0].depth = depth + 1;
|
|
160
|
+
split[1][0].depth = depth + 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return split;
|
|
164
|
+
};
|
|
166
165
|
|
|
167
166
|
export function fromUntyped<Key extends Cachable<Key>, Id>(
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
setOperations: SetOperations<Key, Id>,
|
|
168
|
+
root: UntypedBspSet<Key>,
|
|
170
169
|
): BspSet<Key, Id> {
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
if (root === empty || root === dense) {
|
|
171
|
+
return root;
|
|
172
|
+
}
|
|
173
|
+
return { setOperations, root };
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
const sparse = <Key, Exact, Left, Right>(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
left: Left,
|
|
178
|
+
right: Right,
|
|
179
|
+
pathKey: Key,
|
|
180
|
+
key: Key,
|
|
181
|
+
isExact: Exact,
|
|
181
182
|
) => ({ key, pathKey, left, right, isExact });
|
|
182
183
|
|
|
183
|
-
function fromKey<Key>(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
): UntypedSparse<Key> {
|
|
188
|
-
if (isExact) {
|
|
189
|
-
return sparse(undefined, undefined, pathKey, key, true as const);
|
|
190
|
-
}
|
|
184
|
+
function fromKey<Key>(pathKey: Key, key: Key, isExact: boolean): UntypedSparse<Key> {
|
|
185
|
+
if (isExact) {
|
|
186
|
+
return sparse(undefined, undefined, pathKey, key, true as const);
|
|
187
|
+
}
|
|
191
188
|
|
|
192
|
-
|
|
189
|
+
return sparse(undefined, undefined, pathKey, key, false as const);
|
|
193
190
|
}
|
|
194
191
|
|
|
195
192
|
export function lazy<Key extends Cachable<Key>, Id>(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
setOperations: SetOperations<Key, Id>,
|
|
194
|
+
pathKey: Key,
|
|
195
|
+
key: Key,
|
|
199
196
|
): UntypedBspSet<Key> {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
197
|
+
if (!setOperations.meets(pathKey, key)) {
|
|
198
|
+
return empty;
|
|
199
|
+
}
|
|
200
|
+
const cmp = setOperations.compare(pathKey, key);
|
|
201
|
+
if (cmp !== undefined) {
|
|
202
|
+
if (cmp <= 0) {
|
|
203
|
+
return dense;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return fromKey(pathKey, key, true);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// this is not exactly necessary, but increases the amount of exact nodes and thus we can often
|
|
210
|
+
// prune earlier.
|
|
211
|
+
// Also, having intersect always work guarantees exact nodes, thus allowing for more efficient
|
|
212
|
+
// storage and computation.
|
|
213
|
+
const newKey = setOperations.intersect(pathKey, key);
|
|
214
|
+
|
|
215
|
+
if (newKey !== undefined) {
|
|
216
|
+
return fromKey(pathKey, newKey, true);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return fromKey(pathKey, key, false);
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export function createFromKey<Key extends Cachable<Key>, Id>(
|
|
222
|
-
|
|
223
|
+
uncachedSetOperations: SetOperations<Key, Id>,
|
|
223
224
|
) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
),
|
|
230
|
-
};
|
|
231
|
-
return (key: Key) =>
|
|
232
|
-
fromUntyped(setOperations, lazy(setOperations, setOperations.top, key));
|
|
225
|
+
const setOperations = {
|
|
226
|
+
...uncachedSetOperations,
|
|
227
|
+
split: cacheKeySplitting(uncachedSetOperations.split, uncachedSetOperations.top),
|
|
228
|
+
};
|
|
229
|
+
return (key: Key) => fromUntyped(setOperations, lazy(setOperations, setOperations.top, key));
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
function unionExact<Key extends Cachable<Key>, Id>(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
setOperations: SetOperations<Key, Id>,
|
|
234
|
+
left: UntypedBspSet<Key> & KeyExact<Key>,
|
|
235
|
+
right: UntypedBspSet<Key> & KeyExact<Key>,
|
|
239
236
|
) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
237
|
+
const { pathKey, key: leftKey } = left;
|
|
238
|
+
const { key: rightKey } = right;
|
|
239
|
+
|
|
240
|
+
const cmp = setOperations.compare(leftKey, rightKey);
|
|
241
|
+
if (cmp !== undefined) {
|
|
242
|
+
return cmp < 0 ? right : left;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const combinedKey = setOperations.union(leftKey, rightKey);
|
|
246
|
+
if (combinedKey !== undefined) {
|
|
247
|
+
const combinedCmp = setOperations.compare(combinedKey, pathKey);
|
|
248
|
+
if (combinedCmp !== undefined && combinedCmp === 0) {
|
|
249
|
+
return dense;
|
|
250
|
+
}
|
|
251
|
+
return fromKey(pathKey, combinedKey, true);
|
|
252
|
+
}
|
|
253
|
+
return undefined;
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
/** This is an local combination, not a proper union. We use it to have simpler code elsewhere */
|
|
258
257
|
function combineChildren<Key>(
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
left: UntypedBspSet<Key>,
|
|
259
|
+
right: UntypedBspSet<Key>,
|
|
261
260
|
): Empty | Dense | (UntypedSparse<Key> & TreeDefined<Key>) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
261
|
+
if (left === empty) {
|
|
262
|
+
if (right === empty) {
|
|
263
|
+
return empty;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return sparse(left, right, undefined, undefined, false as const);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (right === empty) {
|
|
270
|
+
return sparse(left, right, undefined, undefined, false as const);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (left === dense) {
|
|
274
|
+
if (right === dense) {
|
|
275
|
+
return dense;
|
|
276
|
+
}
|
|
277
|
+
return sparse(left, right, undefined, undefined, false as const);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return sparse(left, right, undefined, undefined, false as const);
|
|
282
281
|
}
|
|
283
282
|
|
|
284
283
|
function materialize<Key extends Cachable<Key>, Id>(
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
setOperations: SetOperations<Key, Id>,
|
|
285
|
+
set: UntypedSparse<Key>,
|
|
287
286
|
): UntypedSparse<Key> & TreeDefined<Key> {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
if (set.left !== undefined) {
|
|
288
|
+
return set;
|
|
289
|
+
}
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
291
|
+
const [[left], [right]] = setOperations.split(set.pathKey);
|
|
292
|
+
const lChild = lazy(setOperations, left, set.key);
|
|
293
|
+
const rChild = lazy(setOperations, right, set.key);
|
|
295
294
|
|
|
296
|
-
|
|
295
|
+
const res = combineChildren<Key>(lChild, rChild);
|
|
297
296
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
if (res === empty || res === dense) {
|
|
298
|
+
throw new Error("incorrect set operations implementation");
|
|
299
|
+
}
|
|
301
300
|
|
|
302
|
-
|
|
303
|
-
|
|
301
|
+
// first check, that res actually has the desired type
|
|
302
|
+
const typeCheck: TreeDefined<Key> = res;
|
|
304
303
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
304
|
+
const setAlias: UntypedSparse<Key> = set;
|
|
305
|
+
return ObjectAssign(setAlias, {
|
|
306
|
+
left: typeCheck.left,
|
|
307
|
+
right: typeCheck.right,
|
|
308
|
+
});
|
|
310
309
|
}
|
|
311
310
|
|
|
312
311
|
export function unionUntyped<Key extends Cachable<Key>, Id>(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
setOperations: SetOperations<Key, Id>,
|
|
313
|
+
left: UntypedBspSet<Key>,
|
|
314
|
+
right: UntypedBspSet<Key>,
|
|
316
315
|
): UntypedBspSet<Key> {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
316
|
+
if (right === empty) {
|
|
317
|
+
return left;
|
|
318
|
+
}
|
|
319
|
+
if (right === dense) {
|
|
320
|
+
return right;
|
|
321
|
+
}
|
|
322
|
+
if (left === empty) {
|
|
323
|
+
return right;
|
|
324
|
+
}
|
|
325
|
+
if (left === dense) {
|
|
326
|
+
return left;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (left.isExact && right.isExact) {
|
|
330
|
+
const res = unionExact<Key, Id>(setOperations, left, right);
|
|
331
|
+
if (res !== undefined) {
|
|
332
|
+
return res;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const newLeft = materialize<Key, Id>(setOperations, left);
|
|
337
|
+
const newRight = materialize<Key, Id>(setOperations, right);
|
|
338
|
+
|
|
339
|
+
const lChild = unionUntyped(setOperations, newLeft.left, newRight.left);
|
|
340
|
+
const rChild = unionUntyped(setOperations, newLeft.right, newRight.right);
|
|
341
|
+
|
|
342
|
+
return combineChildren(lChild, rChild);
|
|
334
343
|
}
|
|
335
344
|
|
|
336
345
|
export function union<Key extends Cachable<Key>, Id>(
|
|
337
|
-
|
|
338
|
-
|
|
346
|
+
left: BspSet<Key, Id>,
|
|
347
|
+
right: BspSet<Key, Id>,
|
|
339
348
|
): BspSet<Key, Id> {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
+
if (right === empty) {
|
|
350
|
+
return left;
|
|
351
|
+
}
|
|
352
|
+
if (right === dense) {
|
|
353
|
+
return right;
|
|
354
|
+
}
|
|
355
|
+
if (left === empty) {
|
|
356
|
+
return right;
|
|
357
|
+
}
|
|
358
|
+
if (left === dense) {
|
|
359
|
+
return left;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return fromUntyped(
|
|
363
|
+
left.setOperations,
|
|
364
|
+
unionUntyped<Key, Id>(left.setOperations, left.root, right.root),
|
|
365
|
+
);
|
|
349
366
|
}
|
|
350
367
|
|
|
351
368
|
function intersectExact<Key extends Cachable<Key>, Id>(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
369
|
+
setOperations: SetOperations<Key, Id>,
|
|
370
|
+
left: UntypedBspSet<Key> & KeyExact<Key>,
|
|
371
|
+
right: UntypedBspSet<Key> & KeyExact<Key>,
|
|
355
372
|
) {
|
|
356
|
-
|
|
357
|
-
|
|
373
|
+
const { pathKey, key: leftKey } = left;
|
|
374
|
+
const { key: rightKey } = right;
|
|
358
375
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
376
|
+
if (!setOperations.meets(leftKey, rightKey)) {
|
|
377
|
+
return empty;
|
|
378
|
+
}
|
|
362
379
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
380
|
+
const cmp = setOperations.compare(leftKey, rightKey);
|
|
381
|
+
if (cmp !== undefined) {
|
|
382
|
+
return cmp < 0 ? left : right;
|
|
383
|
+
}
|
|
367
384
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
385
|
+
const combinedKey = setOperations.intersect(leftKey, rightKey);
|
|
386
|
+
if (combinedKey !== undefined) {
|
|
387
|
+
return fromKey(pathKey, combinedKey, true);
|
|
388
|
+
}
|
|
372
389
|
|
|
373
|
-
|
|
390
|
+
return undefined;
|
|
374
391
|
}
|
|
375
392
|
|
|
376
393
|
export function intersectUntyped<Key extends Cachable<Key>, Id>(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
394
|
+
setOperations: SetOperations<Key, Id>,
|
|
395
|
+
left: UntypedBspSet<Key>,
|
|
396
|
+
right: UntypedBspSet<Key>,
|
|
380
397
|
): UntypedBspSet<Key> {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
if (left === empty) {
|
|
399
|
+
return left;
|
|
400
|
+
}
|
|
401
|
+
if (right === empty) {
|
|
402
|
+
return right;
|
|
403
|
+
}
|
|
404
|
+
if (left === dense) {
|
|
405
|
+
return right;
|
|
406
|
+
}
|
|
407
|
+
if (right === dense) {
|
|
408
|
+
return left;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (left.isExact && right.isExact) {
|
|
412
|
+
const res = intersectExact<Key, Id>(setOperations, left, right);
|
|
413
|
+
if (res !== undefined) {
|
|
414
|
+
return res;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const newLeft = materialize<Key, Id>(setOperations, left);
|
|
419
|
+
const newRight = materialize<Key, Id>(setOperations, right);
|
|
420
|
+
|
|
421
|
+
const lChild = intersectUntyped(setOperations, newLeft.left, newRight.left);
|
|
422
|
+
const rChild = intersectUntyped(setOperations, newLeft.right, newRight.right);
|
|
423
|
+
|
|
424
|
+
return combineChildren(lChild, rChild);
|
|
402
425
|
}
|
|
403
426
|
|
|
404
427
|
export function intersect<Key extends Cachable<Key>, Id>(
|
|
405
|
-
|
|
406
|
-
|
|
428
|
+
left: BspSet<Key, Id>,
|
|
429
|
+
right: BspSet<Key, Id>,
|
|
407
430
|
): BspSet<Key, Id> {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
431
|
+
if (left === empty) {
|
|
432
|
+
return left;
|
|
433
|
+
}
|
|
434
|
+
if (right === empty) {
|
|
435
|
+
return right;
|
|
436
|
+
}
|
|
437
|
+
if (left === dense) {
|
|
438
|
+
return right;
|
|
439
|
+
}
|
|
440
|
+
if (right === dense) {
|
|
441
|
+
return left;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return fromUntyped(
|
|
445
|
+
left.setOperations,
|
|
446
|
+
intersectUntyped<Key, Id>(left.setOperations, left.root, right.root),
|
|
447
|
+
);
|
|
417
448
|
}
|
|
418
449
|
|
|
419
450
|
export function meetsUntyped<Key extends Cachable<Key>, Id>(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
451
|
+
setOperations: SetOperations<Key, Id>,
|
|
452
|
+
left: UntypedBspSet<Key>,
|
|
453
|
+
right: UntypedBspSet<Key>,
|
|
423
454
|
): boolean {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
455
|
+
if (left === empty || right === empty) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
if (left === dense || right === dense) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (left.isExact && right.isExact) {
|
|
462
|
+
return setOperations.meets(left.key, right.key);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const newLeft = materialize<Key, Id>(setOperations, left);
|
|
466
|
+
const newRight = materialize<Key, Id>(setOperations, right);
|
|
467
|
+
|
|
468
|
+
return (
|
|
469
|
+
meetsUntyped(setOperations, newLeft.left, newRight.left) ||
|
|
470
|
+
meetsUntyped(setOperations, newLeft.right, newRight.right)
|
|
471
|
+
);
|
|
435
472
|
}
|
|
436
473
|
|
|
437
474
|
export function meets<Key extends Cachable<Key>, Id>(
|
|
438
|
-
|
|
439
|
-
|
|
475
|
+
left: BspSet<Key, Id>,
|
|
476
|
+
right: BspSet<Key, Id>,
|
|
440
477
|
): boolean {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
478
|
+
if (left === empty || right === empty) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
if (left === dense || right === dense) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
return meetsUntyped<Key, Id>(left.setOperations, left.root, right.root);
|
|
444
485
|
}
|
|
445
486
|
|
|
446
487
|
function exceptExact<Key extends Cachable<Key>, Id>(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
488
|
+
setOperations: SetOperations<Key, Id>,
|
|
489
|
+
left: UntypedSparse<Key> & KeyExact<Key>,
|
|
490
|
+
right: KeyExact<Key>,
|
|
450
491
|
) {
|
|
451
|
-
|
|
452
|
-
|
|
492
|
+
const { pathKey, key: leftKey } = left;
|
|
493
|
+
const { key: rightKey } = right;
|
|
453
494
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
495
|
+
if (!setOperations.meets(leftKey, rightKey)) {
|
|
496
|
+
return left;
|
|
497
|
+
}
|
|
457
498
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
499
|
+
const combinedKey = setOperations.except(leftKey, rightKey);
|
|
500
|
+
if (combinedKey !== undefined) {
|
|
501
|
+
return fromKey(pathKey, combinedKey, true);
|
|
502
|
+
}
|
|
462
503
|
|
|
463
|
-
|
|
504
|
+
return undefined;
|
|
464
505
|
}
|
|
465
506
|
|
|
466
507
|
export function exceptUntyped<Key extends Cachable<Key>, Id>(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
508
|
+
setOperations: SetOperations<Key, Id>,
|
|
509
|
+
left: UntypedBspSet<Key>,
|
|
510
|
+
right: UntypedBspSet<Key>,
|
|
470
511
|
): UntypedBspSet<Key> {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
512
|
+
if (left === empty) {
|
|
513
|
+
return left;
|
|
514
|
+
}
|
|
515
|
+
if (right === empty) {
|
|
516
|
+
return left;
|
|
517
|
+
}
|
|
518
|
+
if (right === dense) {
|
|
519
|
+
return empty;
|
|
520
|
+
}
|
|
521
|
+
if (left === dense) {
|
|
522
|
+
const newRight_inner = materialize<Key, Id>(setOperations, right);
|
|
523
|
+
const lChild_inner = exceptUntyped(setOperations, dense, newRight_inner.left);
|
|
524
|
+
const rChild_inner = exceptUntyped(setOperations, dense, newRight_inner.right);
|
|
525
|
+
return combineChildren(lChild_inner, rChild_inner);
|
|
526
|
+
}
|
|
527
|
+
if (left.isExact && right.isExact) {
|
|
528
|
+
const res = exceptExact<Key, Id>(setOperations, left, right);
|
|
529
|
+
if (res !== undefined) {
|
|
530
|
+
return res;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const newLeft = materialize<Key, Id>(setOperations, left);
|
|
534
|
+
const newRight = materialize<Key, Id>(setOperations, right);
|
|
535
|
+
|
|
536
|
+
const lChild = exceptUntyped(setOperations, newLeft.left, newRight.left);
|
|
537
|
+
const rChild = exceptUntyped(setOperations, newLeft.right, newRight.right);
|
|
538
|
+
|
|
539
|
+
return combineChildren(lChild, rChild);
|
|
491
540
|
}
|
|
492
541
|
|
|
493
542
|
export function except<Key extends Cachable<Key>, Id>(
|
|
494
|
-
|
|
495
|
-
|
|
543
|
+
left: BspSet<Key, Id>,
|
|
544
|
+
right: BspSet<Key, Id>,
|
|
496
545
|
): BspSet<Key, Id> {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
546
|
+
if (left === empty) {
|
|
547
|
+
return left;
|
|
548
|
+
}
|
|
549
|
+
if (right === empty) {
|
|
550
|
+
return left;
|
|
551
|
+
}
|
|
552
|
+
if (right === dense) {
|
|
553
|
+
return empty;
|
|
554
|
+
}
|
|
555
|
+
if (left === dense) {
|
|
556
|
+
return fromUntyped(
|
|
557
|
+
right.setOperations,
|
|
558
|
+
exceptUntyped<Key, Id>(right.setOperations, left, right.root),
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return fromUntyped(
|
|
563
|
+
left.setOperations,
|
|
564
|
+
exceptUntyped<Key, Id>(left.setOperations, left.root, right.root),
|
|
565
|
+
);
|
|
511
566
|
}
|
|
512
567
|
|
|
513
568
|
const compareExact = <Key extends Cachable<Key>, Id>(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
569
|
+
setOperations: SetOperations<Key, Id>,
|
|
570
|
+
left: KeyExact<Key>,
|
|
571
|
+
right: KeyExact<Key>,
|
|
517
572
|
) => setOperations.compare(left.key, right.key);
|
|
518
573
|
|
|
519
|
-
export function combineCmp(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
574
|
+
export function combineCmp(left: -1 | 0 | 1 | undefined, right: -1 | 0 | 1 | undefined) {
|
|
575
|
+
if (left === undefined || right === undefined) {
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
if (left === 0) {
|
|
579
|
+
return right;
|
|
580
|
+
}
|
|
581
|
+
if (right === 0) {
|
|
582
|
+
return left;
|
|
583
|
+
}
|
|
584
|
+
return left === right ? left : undefined;
|
|
527
585
|
}
|
|
528
586
|
|
|
529
587
|
export function compareUntyped<Key extends Cachable<Key>, Id>(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
588
|
+
setOperations: SetOperations<Key, Id>,
|
|
589
|
+
left: UntypedBspSet<Key>,
|
|
590
|
+
right: UntypedBspSet<Key>,
|
|
533
591
|
): -1 | 0 | 1 | undefined {
|
|
534
|
-
|
|
592
|
+
if (left === right) {
|
|
593
|
+
return 0;
|
|
594
|
+
}
|
|
535
595
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
596
|
+
if (left === empty) {
|
|
597
|
+
return -1;
|
|
598
|
+
}
|
|
539
599
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
600
|
+
if (right === empty) {
|
|
601
|
+
return 1;
|
|
602
|
+
}
|
|
543
603
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
604
|
+
if (left === dense) {
|
|
605
|
+
if (right === dense) {
|
|
606
|
+
return 0;
|
|
607
|
+
}
|
|
548
608
|
|
|
549
|
-
|
|
550
|
-
|
|
609
|
+
return 1;
|
|
610
|
+
}
|
|
551
611
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
612
|
+
if (right === dense) {
|
|
613
|
+
return -1;
|
|
614
|
+
}
|
|
555
615
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
616
|
+
if (left.isExact && right.isExact) {
|
|
617
|
+
return compareExact(setOperations, left, right);
|
|
618
|
+
}
|
|
559
619
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
620
|
+
const newLeft = materialize<Key, Id>(setOperations, left);
|
|
621
|
+
const newRight = materialize<Key, Id>(setOperations, right);
|
|
622
|
+
const lCmp = compareUntyped(setOperations, newLeft.left, newRight.left);
|
|
623
|
+
if (lCmp === undefined) {
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
564
626
|
|
|
565
|
-
|
|
627
|
+
const rCmp = compareUntyped(setOperations, newLeft.right, newRight.right);
|
|
566
628
|
|
|
567
|
-
|
|
629
|
+
if (rCmp === undefined) {
|
|
630
|
+
return undefined;
|
|
631
|
+
}
|
|
568
632
|
|
|
569
|
-
|
|
633
|
+
return combineCmp(lCmp, rCmp);
|
|
570
634
|
}
|
|
571
635
|
|
|
572
636
|
export function compare<Key extends Cachable<Key>, Id>(
|
|
573
|
-
|
|
574
|
-
|
|
637
|
+
left: BspSet<Key, Id>,
|
|
638
|
+
right: BspSet<Key, Id>,
|
|
575
639
|
) {
|
|
576
|
-
|
|
640
|
+
if (left === right) {
|
|
641
|
+
return 0;
|
|
642
|
+
}
|
|
577
643
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
644
|
+
if (left === empty) {
|
|
645
|
+
return -1;
|
|
646
|
+
}
|
|
581
647
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
648
|
+
if (right === empty) {
|
|
649
|
+
return 1;
|
|
650
|
+
}
|
|
585
651
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
652
|
+
if (left === dense) {
|
|
653
|
+
if (right === dense) {
|
|
654
|
+
return 0;
|
|
655
|
+
}
|
|
590
656
|
|
|
591
|
-
|
|
592
|
-
|
|
657
|
+
return 1;
|
|
658
|
+
}
|
|
593
659
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
660
|
+
if (right === dense) {
|
|
661
|
+
return -1;
|
|
662
|
+
}
|
|
597
663
|
|
|
598
|
-
|
|
664
|
+
return compareUntyped<Key, Id>(left.setOperations, left.root, right.root);
|
|
599
665
|
}
|
|
600
666
|
|
|
601
667
|
export const symmetricDiff = <Key extends Cachable<Key>, Id>(
|
|
602
|
-
|
|
603
|
-
|
|
668
|
+
left: BspSet<Key, Id>,
|
|
669
|
+
right: BspSet<Key, Id>,
|
|
604
670
|
) => union(except(left, right), except(right, left));
|
|
605
671
|
|
|
606
|
-
export const complement = <Key extends Cachable<Key>, Id>(
|
|
607
|
-
|
|
608
|
-
) => except(dense, set);
|
|
672
|
+
export const complement = <Key extends Cachable<Key>, Id>(set: BspSet<Key, Id>) =>
|
|
673
|
+
except(dense, set);
|
|
609
674
|
|
|
610
675
|
function getNodeCountUntyped<T>(set: UntypedBspSet<T> | undefined): number {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
676
|
+
if (set === undefined || set === empty || set === dense) {
|
|
677
|
+
return 0;
|
|
678
|
+
}
|
|
614
679
|
|
|
615
|
-
|
|
680
|
+
return getNodeCountUntyped(set.left) + getNodeCountUntyped(set.right) + 1;
|
|
616
681
|
}
|
|
617
682
|
|
|
618
|
-
export function getNodeCount<Key extends Cachable<Key>, Id>(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
683
|
+
export function getNodeCount<Key extends Cachable<Key>, Id>(set: BspSet<Key, Id>) {
|
|
684
|
+
if (set === empty || set === dense) {
|
|
685
|
+
return 0;
|
|
686
|
+
}
|
|
687
|
+
return getNodeCountUntyped(set.root);
|
|
623
688
|
}
|
|
624
689
|
|
|
625
690
|
function forEachKeyUntyped<Key extends Cachable<Key>, Id>(
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
691
|
+
setOperations: SetOperations<Key, Id>,
|
|
692
|
+
set: UntypedBspSet<Key>,
|
|
693
|
+
f: (key: Key) => boolean,
|
|
629
694
|
): boolean {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
695
|
+
function loop(pathKey: Key, set_inner: UntypedBspSet<Key>): boolean {
|
|
696
|
+
if (set_inner === empty) {
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
if (set_inner === dense) {
|
|
700
|
+
return f(pathKey);
|
|
701
|
+
}
|
|
702
|
+
if (set_inner.isExact) {
|
|
703
|
+
return f(set_inner.key);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const newSet = materialize<Key, Id>(setOperations, set_inner);
|
|
707
|
+
const [[left], [right]] = setOperations.split(pathKey);
|
|
708
|
+
return loop(left, newSet.left) && loop(right, newSet.right);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return loop(setOperations.top, set);
|
|
641
712
|
}
|
|
642
713
|
|
|
643
714
|
export function forEachKey<Key extends Cachable<Key>, Id>(
|
|
644
|
-
|
|
645
|
-
|
|
715
|
+
set: Empty | Sparse<Key, Id>,
|
|
716
|
+
f: (key: Key) => boolean,
|
|
646
717
|
): boolean {
|
|
647
|
-
|
|
648
|
-
|
|
718
|
+
if (set === empty) {
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
return forEachKeyUntyped(set.setOperations, set.root, f);
|
|
649
722
|
}
|