@fluidframework/matrix 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191457

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +4 -3
  3. package/dist/handlecache.js +3 -3
  4. package/dist/handlecache.js.map +1 -1
  5. package/dist/handletable.d.ts +8 -1
  6. package/dist/handletable.d.ts.map +1 -1
  7. package/dist/handletable.js +11 -3
  8. package/dist/handletable.js.map +1 -1
  9. package/dist/matrix.d.ts.map +1 -1
  10. package/dist/matrix.js +17 -18
  11. package/dist/matrix.js.map +1 -1
  12. package/dist/packageVersion.d.ts +1 -1
  13. package/dist/packageVersion.js +1 -1
  14. package/dist/packageVersion.js.map +1 -1
  15. package/dist/permutationvector.d.ts.map +1 -1
  16. package/dist/permutationvector.js +11 -8
  17. package/dist/permutationvector.js.map +1 -1
  18. package/dist/serialization.js +2 -2
  19. package/dist/serialization.js.map +1 -1
  20. package/dist/sparsearray2d.d.ts.map +1 -1
  21. package/dist/sparsearray2d.js +1 -0
  22. package/dist/sparsearray2d.js.map +1 -1
  23. package/dist/undoprovider.js +8 -9
  24. package/dist/undoprovider.js.map +1 -1
  25. package/lib/handlecache.js +1 -1
  26. package/lib/handlecache.js.map +1 -1
  27. package/lib/handletable.d.ts +8 -1
  28. package/lib/handletable.d.ts.map +1 -1
  29. package/lib/handletable.js +11 -3
  30. package/lib/handletable.js.map +1 -1
  31. package/lib/matrix.d.ts.map +1 -1
  32. package/lib/matrix.js +3 -4
  33. package/lib/matrix.js.map +1 -1
  34. package/lib/packageVersion.d.ts +1 -1
  35. package/lib/packageVersion.js +1 -1
  36. package/lib/packageVersion.js.map +1 -1
  37. package/lib/permutationvector.d.ts.map +1 -1
  38. package/lib/permutationvector.js +6 -3
  39. package/lib/permutationvector.js.map +1 -1
  40. package/lib/serialization.js +1 -1
  41. package/lib/serialization.js.map +1 -1
  42. package/lib/sparsearray2d.d.ts.map +1 -1
  43. package/lib/sparsearray2d.js +1 -0
  44. package/lib/sparsearray2d.js.map +1 -1
  45. package/lib/undoprovider.js +5 -6
  46. package/lib/undoprovider.js.map +1 -1
  47. package/package.json +27 -31
  48. package/src/handlecache.ts +1 -1
  49. package/src/handletable.ts +22 -2
  50. package/src/matrix.ts +2 -1
  51. package/src/packageVersion.ts +1 -1
  52. package/src/permutationvector.ts +3 -4
  53. package/src/serialization.ts +1 -1
  54. package/src/sparsearray2d.ts +1 -0
  55. package/src/undoprovider.ts +1 -1
  56. package/dist/bspSet.d.ts +0 -125
  57. package/dist/bspSet.d.ts.map +0 -1
  58. package/dist/bspSet.js +0 -425
  59. package/dist/bspSet.js.map +0 -1
  60. package/dist/productSet.d.ts +0 -61
  61. package/dist/productSet.d.ts.map +0 -1
  62. package/dist/productSet.js +0 -679
  63. package/dist/productSet.js.map +0 -1
  64. package/dist/split.d.ts +0 -41
  65. package/dist/split.d.ts.map +0 -1
  66. package/dist/split.js +0 -127
  67. package/dist/split.js.map +0 -1
  68. package/lib/bspSet.d.ts +0 -125
  69. package/lib/bspSet.d.ts.map +0 -1
  70. package/lib/bspSet.js +0 -402
  71. package/lib/bspSet.js.map +0 -1
  72. package/lib/productSet.d.ts +0 -61
  73. package/lib/productSet.d.ts.map +0 -1
  74. package/lib/productSet.js +0 -665
  75. package/lib/productSet.js.map +0 -1
  76. package/lib/split.d.ts +0 -41
  77. package/lib/split.d.ts.map +0 -1
  78. package/lib/split.js +0 -116
  79. package/lib/split.js.map +0 -1
  80. package/src/bspSet.ts +0 -722
  81. package/src/productSet.ts +0 -1038
  82. package/src/split.ts +0 -171
package/src/productSet.ts DELETED
@@ -1,1038 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import {
7
- BspSet,
8
- empty,
9
- dense,
10
- Empty,
11
- Dense,
12
- combineCmp,
13
- SetOperations,
14
- intersectUntyped,
15
- compareUntyped,
16
- Cachable,
17
- UntypedBspSet,
18
- unionUntyped,
19
- exceptUntyped,
20
- lazy,
21
- UntypedSparse,
22
- fromUntyped,
23
- meetsUntyped,
24
- Pair,
25
- } from "./bspSet";
26
-
27
- type Restrict<T, Props extends (keyof T)[]> = { [Prop in Props[number]]: T[Prop] };
28
-
29
- export type Product<T> = {
30
- readonly [dim in keyof T]: T[dim] extends BspSet<infer _TKey, infer _TId> ? T[dim] : never;
31
- };
32
-
33
- type UntypedProduct<T> = {
34
- readonly [dim in keyof T]?: T[dim] extends BspSet<infer TKey, infer _TId>
35
- ? UntypedSparse<TKey>
36
- : never;
37
- };
38
-
39
- type Probabilities<T> = { readonly [dim in keyof T]?: number };
40
-
41
- type ProductOperations<T> = {
42
- readonly [dim in keyof T]: T[dim] extends BspSet<infer TKey, infer TId>
43
- ? SetOperations<TKey, TId>
44
- : never;
45
- };
46
-
47
- /** Given a cartesian product, a subspace is a subset of said space, such that it is also a cartesian product
48
- * and all the dimensions form subsets of the original cartesian product.
49
- *
50
- * This is a generalized notion of something like hyper-rectangles. Examples include:
51
- * - Rectangles
52
- * - Individual cells in a grid
53
- * - Rectangular ranges with missing rows and/or columns
54
- *
55
- * For the actual definition and properties, please read the document about *Operations on Cartesian Products*.
56
- */
57
- interface Subspace<T> {
58
- readonly isSubspace: true;
59
- // isCoSubspace: boolean;
60
- readonly bounds: UntypedProduct<T>;
61
- }
62
-
63
- // type CoSubspace<T> = {
64
- // isSubspace: boolean;
65
- // isCoSubspace: true;
66
- // subspace: Product<T>;
67
- // };
68
-
69
- interface Union<T> {
70
- readonly isSubspace: false;
71
- // readonly isCoSubspace: false;
72
- readonly left: UntypedSparseProduct<T>;
73
- readonly right: UntypedSparseProduct<T>;
74
- readonly bounds: UntypedProduct<T>;
75
- readonly subspaceCount: number;
76
- }
77
-
78
- type UntypedSparseProduct<T> = Subspace<T> | Union<T>; // | CoSubspace<T>;
79
-
80
- interface SparseProduct<T> {
81
- readonly productOperations: ProductOperations<T>;
82
- readonly root: UntypedSparseProduct<T>;
83
- }
84
-
85
- interface Box<T> {
86
- box: UntypedProduct<T>;
87
- probabilities: Probabilities<T>;
88
- children?: Pair<Box<T>>;
89
- depth: number;
90
- }
91
-
92
- const tops: { [poKey in string]?: Box<unknown> } = {};
93
- const top = <T>(productOperations: ProductOperations<T>): Box<T> => {
94
- const dims: [keyof T, ProductOperations<T>[keyof T]["id"]][] = [];
95
- for (const dimStr of Object.keys(productOperations)) {
96
- const dim = dimStr as keyof T;
97
- dims.push([dim, productOperations[dim].id]);
98
- }
99
-
100
- const poKey = JSON.stringify(dims.sort());
101
- let currTop = tops[poKey];
102
- if (currTop === undefined) {
103
- currTop = { box: {}, probabilities: {}, depth: 1 };
104
- tops[poKey] = currTop;
105
- }
106
-
107
- return currTop;
108
- };
109
-
110
- function subspace<T>(bounds: UntypedProduct<T>): Subspace<T> | Dense {
111
- let isDense = true;
112
- for (const dim of Object.keys(bounds)) {
113
- if (Object.prototype.hasOwnProperty.call(bounds, dim)) {
114
- isDense = false;
115
- break;
116
- }
117
- }
118
- if (isDense) {
119
- return dense;
120
- }
121
- return { isSubspace: true as const, bounds };
122
- }
123
-
124
- function getUntypedSubspaceCount<T>(set: UntypedSparseProduct<T>) {
125
- if (set.isSubspace) {
126
- return 1;
127
- }
128
- return set.subspaceCount;
129
- }
130
-
131
- const union = <T>(
132
- left: UntypedSparseProduct<T>,
133
- right: UntypedSparseProduct<T>,
134
- bounds: UntypedProduct<T>,
135
- ): Union<T> => ({
136
- isSubspace: false as const,
137
- left,
138
- right,
139
- bounds,
140
- subspaceCount: getUntypedSubspaceCount(left) + getUntypedSubspaceCount(right),
141
- });
142
-
143
- function sparseProduct<T>(
144
- productOperations: ProductOperations<T>,
145
- root: UntypedSparseProduct<T> | Dense,
146
- ): SparseProduct<T> | Dense {
147
- if (root === dense) {
148
- return root;
149
- }
150
- if (root.isSubspace) {
151
- let hasSparseDimensions = false;
152
- for (const dim of Object.keys(root.bounds)) {
153
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
154
- hasSparseDimensions = true;
155
- break;
156
- }
157
- }
158
- if (!hasSparseDimensions) {
159
- return dense;
160
- }
161
- }
162
- return { productOperations, root };
163
- }
164
-
165
- type UntypedProductSet<T> = Empty | Dense | UntypedSparseProduct<T>;
166
- export type ProductSet<T> = Empty | Dense | SparseProduct<T>;
167
-
168
- function toBspSet<T>(set: UntypedBspSet<T> | undefined) {
169
- if (set === undefined) {
170
- return dense;
171
- }
172
- return set;
173
- }
174
-
175
- /** An object that contains all downcasts. The operations in here are generally of the kind that need dynamic
176
- * features, i.e. iterate over the object properties somehow. Their *usage* can be considered safe, but they warrant
177
- * more careful review whenever a change occurs, because we get less support from the type system and we are
178
- * downcasting. */
179
- const unsafe = {
180
- unzip<T>(product: Product<T>): ProductSet<T> {
181
- const productOperations: { [dim in keyof T]?: SetOperations<unknown, unknown> } = {};
182
- const root: { [dim in keyof T]?: UntypedSparse<unknown> } = {};
183
- for (const dimStr of Object.keys(product)) {
184
- const dim = dimStr as keyof T;
185
- if (Object.prototype.hasOwnProperty.call(product, dim)) {
186
- const set: BspSet<unknown, unknown> = product[dim];
187
- if (set === empty) {
188
- return empty;
189
- }
190
- if (set === dense) {
191
- continue;
192
- }
193
-
194
- productOperations[dim] = set.setOperations;
195
- root[dim] = set.root;
196
- }
197
- }
198
-
199
- return sparseProduct(
200
- productOperations as ProductOperations<T>,
201
- subspace(root as UntypedProduct<T>),
202
- );
203
- },
204
-
205
- combineProduct<T>(
206
- productOperations: ProductOperations<T>,
207
- left: UntypedProduct<T>,
208
- right: UntypedProduct<T>,
209
- combineFunc: <Key extends Cachable<Key>, Id>(
210
- setOperations: SetOperations<Key, Id>,
211
- left: UntypedBspSet<Key>,
212
- right: UntypedBspSet<Key>,
213
- ) => UntypedBspSet<Key>,
214
- ) {
215
- const res: { [dim in keyof T]?: UntypedSparse<unknown> } = {};
216
- for (const dimStr of Object.keys(productOperations)) {
217
- const dim = dimStr as keyof T;
218
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
219
- const combined = combineFunc<unknown, unknown>(
220
- productOperations[dim],
221
- toBspSet(left[dim]),
222
- toBspSet(right[dim]),
223
- );
224
- if (combined === empty) {
225
- return combined;
226
- }
227
- if (combined === dense) {
228
- continue;
229
- }
230
- res[dim] = combined;
231
- }
232
- }
233
-
234
- return res as UntypedProduct<T>;
235
- },
236
-
237
- // eslint-disable-next-line @typescript-eslint/ban-types
238
- restrict<T extends object, Props extends (keyof T)[]>(object: T, ...props: Props) {
239
- const res: Partial<Restrict<T, Props>> = {};
240
- for (const key of props) {
241
- if (Object.prototype.hasOwnProperty.call(object, key)) {
242
- const prop = object[key];
243
- res[key] = prop;
244
- }
245
- }
246
-
247
- return res as Restrict<T, Props>;
248
- },
249
-
250
- fromUntypedProduct<T, Props extends (keyof T)[]>(
251
- productOperations: ProductOperations<Restrict<T, Props>>,
252
- bounds: UntypedProduct<Restrict<T, Props>>,
253
- dims: Props,
254
- ) {
255
- const product: { [dim in Props[number]]?: BspSet<unknown, unknown> } = {};
256
- for (const dim of dims) {
257
- const bound: UntypedSparse<unknown> | undefined = bounds[dim];
258
- product[dim] = fromUntyped(productOperations[dim], bound !== undefined ? bound : dense);
259
- }
260
- return product as Product<Restrict<T, Props>>;
261
- },
262
-
263
- denseProduct<T, Props extends (keyof T)[]>(dims: Props): Product<Restrict<T, Props>> {
264
- const top_inner: { [dim in Props[number]]?: Dense } = {};
265
- for (const dim of dims) {
266
- top_inner[dim] = dense;
267
- }
268
- return top_inner as Product<Restrict<T, Props>>;
269
- },
270
- };
271
-
272
- export const createFromProduct = unsafe.unzip.bind(unsafe);
273
-
274
- type Compatible<T, U> = { [dim in keyof T & keyof U]: T[dim] };
275
-
276
- function joinBounds<T>(
277
- productOperations: ProductOperations<T>,
278
- left: UntypedProduct<T>,
279
- right: UntypedProduct<T>,
280
- ) {
281
- const join = unsafe.combineProduct(productOperations, left, right, unionUntyped);
282
- if (join === empty) {
283
- throw new Error("broken invariant: the union of two non-empty products cannot be empty");
284
- }
285
-
286
- return join;
287
- }
288
-
289
- function compareSubspace<T>(
290
- productOperations: ProductOperations<T>,
291
- left: UntypedProduct<T>,
292
- right: UntypedProduct<T>,
293
- ) {
294
- let cmp: ReturnType<typeof combineCmp> = 0;
295
- for (const dimStr of Object.keys(productOperations)) {
296
- const dim = dimStr as keyof T;
297
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
298
- const lProj = toBspSet(left[dim]);
299
- const rProj = toBspSet(right[dim]);
300
- const setOperations = productOperations[dim];
301
-
302
- cmp = combineCmp(cmp, compareUntyped<unknown, unknown>(setOperations, lProj, rProj));
303
-
304
- if (cmp === undefined) {
305
- return undefined;
306
- }
307
- }
308
- }
309
-
310
- return cmp;
311
- }
312
-
313
- const tryUnionSubspaces = (() => {
314
- const cache: { left?: unknown; right?: unknown; res?: unknown } = {};
315
- return <T>(
316
- productOperations: ProductOperations<T>,
317
- left: Subspace<T>,
318
- right: Subspace<T>,
319
- ): Subspace<T> | Dense | Empty | undefined => {
320
- if (left === cache.left && right === cache.right) {
321
- return cache.res as ReturnType<typeof tryUnionSubspaces>;
322
- }
323
- cache.left = left;
324
- cache.right = right;
325
-
326
- const cmp = compareSubspace(productOperations, left.bounds, right.bounds);
327
- if (cmp !== undefined) {
328
- return (cache.res = cmp <= 0 ? right : left);
329
- }
330
- let differentDimension: keyof T | undefined;
331
-
332
- // because Object.keys only returns string[], we need to downcast
333
- const po_keys = Object.keys(productOperations) as (keyof T)[];
334
- for (const dim of po_keys) {
335
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
336
- const cmp_inner = compareUntyped<unknown, unknown>(
337
- productOperations[dim],
338
- toBspSet(left.bounds[dim]),
339
- toBspSet(right.bounds[dim]),
340
- );
341
- if (cmp_inner !== 0) {
342
- if (differentDimension !== undefined) {
343
- return (cache.res = undefined);
344
- }
345
-
346
- differentDimension = dim;
347
- }
348
- }
349
- }
350
-
351
- if (differentDimension !== undefined) {
352
- const newDim = unionUntyped<unknown, unknown>(
353
- productOperations[differentDimension],
354
- toBspSet(left.bounds[differentDimension]),
355
- toBspSet(right.bounds[differentDimension]),
356
- );
357
- if (newDim === empty) {
358
- return (cache.res = empty);
359
- }
360
- if (newDim === dense) {
361
- // we are actually deleting the `differentDimension`, so the variable
362
- // `deleted` must be there. Hence disabling the rule here.
363
- const { [differentDimension]: deleted, ...leftBoundsWithoutDifferentDimension } =
364
- left.bounds;
365
- return (cache.res = subspace<unknown>(leftBoundsWithoutDifferentDimension));
366
- }
367
-
368
- const newBounds: UntypedProduct<T> = {
369
- ...left.bounds,
370
- [differentDimension]: newDim,
371
- };
372
- return (cache.res = subspace(newBounds));
373
- }
374
-
375
- return (cache.res = undefined);
376
- };
377
- })();
378
-
379
- function combineChildren<T>(
380
- productOperations: ProductOperations<T>,
381
- left: UntypedProductSet<T>,
382
- right: UntypedProductSet<T>,
383
- ): UntypedProductSet<T> {
384
- if (right === empty) {
385
- return left;
386
- }
387
- if (right === dense) {
388
- return right;
389
- }
390
- if (left === empty) {
391
- return right;
392
- }
393
- if (left === dense) {
394
- return left;
395
- }
396
-
397
- if (!left.isSubspace || !right.isSubspace) {
398
- return union<T>(left, right, joinBounds(productOperations, left.bounds, right.bounds));
399
- }
400
-
401
- const combinedSubspace = tryUnionSubspaces<T>(productOperations, left, right);
402
-
403
- if (combinedSubspace !== undefined) {
404
- return combinedSubspace;
405
- }
406
- return union(left, right, joinBounds(productOperations, left.bounds, right.bounds));
407
- }
408
-
409
- function projectUntyped<T, Props extends (keyof T)[]>(
410
- productOperations: ProductOperations<T>,
411
- set: UntypedSparseProduct<T>,
412
- ...dims: Props
413
- ): UntypedProductSet<Restrict<T, Props>> {
414
- const bounds = unsafe.restrict(set.bounds, ...dims);
415
- if (set.isSubspace) {
416
- return subspace(bounds);
417
- }
418
-
419
- const lChild = projectUntyped(productOperations, set.left, ...dims);
420
- if (lChild === dense) {
421
- return dense;
422
- }
423
- const rChild = projectUntyped(productOperations, set.right, ...dims);
424
- return combineChildren(productOperations, lChild, rChild);
425
- }
426
-
427
- export function project<T, Props extends (keyof T)[]>(
428
- set: ProductSet<T>,
429
- ...dims: Props
430
- ): ProductSet<Restrict<T, Props>> {
431
- if (set === dense || set === empty) {
432
- return set;
433
- }
434
- const productOperations = unsafe.restrict(set.productOperations, ...dims);
435
- const root = projectUntyped(productOperations, set.root, ...dims);
436
- if (root === empty) {
437
- return root;
438
- }
439
- return sparseProduct(productOperations, root);
440
- }
441
-
442
- function splitBox<T>(productOperations: ProductOperations<T>, currentBox: Box<T>): Pair<Box<T>> {
443
- if (currentBox.children !== undefined) {
444
- return currentBox.children;
445
- }
446
- const { box, probabilities } = currentBox;
447
- let biggestDim: keyof T | undefined;
448
- let biggestDimKey;
449
- let currentProb = 0;
450
- // because Object.keys only returns string[], we need to downcast
451
- const po_keys = Object.keys(productOperations) as (keyof T)[];
452
- for (const dim of po_keys) {
453
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
454
- const prob: number | undefined = probabilities[dim];
455
- const setOperations_inner = productOperations[dim];
456
- if (prob === undefined) {
457
- biggestDim = dim;
458
- biggestDimKey = setOperations_inner.top;
459
- break;
460
- }
461
-
462
- if (prob > currentProb) {
463
- const dimensionSet = toBspSet(box[dim]);
464
- if (dimensionSet === empty) {
465
- throw new Error("the key split can never return empty");
466
- }
467
- let key = setOperations_inner.top;
468
- if (dimensionSet !== dense) {
469
- if (!dimensionSet.isExact) {
470
- throw new Error("the key can always be represented exactly");
471
- }
472
-
473
- key = dimensionSet.key;
474
- }
475
- if (!setOperations_inner.canSplit(key)) {
476
- continue;
477
- }
478
- biggestDim = dim;
479
- currentProb = prob;
480
- biggestDimKey = key;
481
- }
482
- }
483
- }
484
-
485
- if (biggestDim === undefined || biggestDimKey === undefined) {
486
- throw new Error("there has to be at least one dimension");
487
- }
488
-
489
- const setOperations = productOperations[biggestDim];
490
-
491
- const [[leftDim, leftProb], [rightDim, rightProb]] = setOperations.split(biggestDimKey);
492
- const res: ReturnType<typeof splitBox> = [
493
- {
494
- box: {
495
- ...box,
496
- [biggestDim]: lazy<unknown, unknown>(setOperations, setOperations.top, leftDim),
497
- },
498
- probabilities: { ...probabilities, [biggestDim]: leftProb },
499
- depth: currentBox.depth + 1,
500
- },
501
- {
502
- box: {
503
- ...box,
504
- [biggestDim]: lazy<unknown, unknown>(setOperations, setOperations.top, rightDim),
505
- },
506
- probabilities: { ...probabilities, [biggestDim]: rightProb },
507
- depth: currentBox.depth + 1,
508
- },
509
- ];
510
- if (currentBox.depth < 10) {
511
- currentBox.children = res;
512
- }
513
- return res;
514
- }
515
-
516
- function restrictByBounds<T>(
517
- productOperations: ProductOperations<T>,
518
- set: UntypedProductSet<T>,
519
- leftBounds: UntypedProduct<T>,
520
- rightBounds: UntypedProduct<T>,
521
- ): Pair<UntypedProductSet<T>> {
522
- if (set === empty) {
523
- return [empty, empty];
524
- }
525
- if (set === dense) {
526
- return [subspace(leftBounds), subspace(rightBounds)];
527
- }
528
- const cmp = compareSubspace(productOperations, set.bounds, leftBounds);
529
-
530
- // the set is fully contained in the left half, i.e. we know the pair.
531
- if (cmp !== undefined && cmp <= 0) {
532
- return [set, empty];
533
- }
534
-
535
- const newLeftBounds = unsafe.combineProduct(
536
- productOperations,
537
- set.bounds,
538
- leftBounds,
539
- intersectUntyped,
540
- );
541
-
542
- // if we know, that the left set is completely empty, then the whole set is in the right bounds.
543
- if (newLeftBounds === empty) {
544
- return [empty, set];
545
- }
546
-
547
- const newRightBounds = unsafe.combineProduct(
548
- productOperations,
549
- set.bounds,
550
- rightBounds,
551
- intersectUntyped,
552
- );
553
- if (set.isSubspace) {
554
- return [
555
- subspace(newLeftBounds),
556
- newRightBounds === empty ? empty : subspace(newRightBounds),
557
- ];
558
- }
559
-
560
- const [ll, lr] = restrictByBounds(productOperations, set.left, leftBounds, rightBounds);
561
- const [rl, rr] = restrictByBounds(productOperations, set.right, leftBounds, rightBounds);
562
- return [combineChildren(productOperations, ll, rl), combineChildren(productOperations, lr, rr)];
563
- }
564
-
565
- const splitByBox = <T>(
566
- productOperations: ProductOperations<T>,
567
- set: UntypedProductSet<T>,
568
- { box: leftBounds }: Box<T>,
569
- { box: rightBounds }: Box<T>,
570
- ): Pair<UntypedProductSet<T>, UntypedProductSet<T>> => {
571
- return restrictByBounds(productOperations, set, leftBounds, rightBounds);
572
- };
573
-
574
- function recurse<T>(
575
- productOperations: ProductOperations<T>,
576
- left: UntypedProductSet<T>,
577
- right: UntypedProductSet<T>,
578
- currentBox: Box<T>,
579
- visitFunc: (
580
- productOperations: ProductOperations<T>,
581
- left: UntypedProductSet<T>,
582
- right: UntypedProductSet<T>,
583
- box: Box<T>,
584
- ) => UntypedProductSet<T>,
585
- ) {
586
- const [leftBox, rightBox] = splitBox(productOperations, currentBox);
587
- const [ll, lr] = splitByBox(productOperations, left, leftBox, rightBox);
588
- const [rl, rr] = splitByBox(productOperations, right, leftBox, rightBox);
589
-
590
- const lChild = visitFunc(productOperations, ll, rl, leftBox);
591
- if (lChild === dense) {
592
- return dense;
593
- }
594
- const rChild = visitFunc(productOperations, lr, rr, rightBox);
595
- return combineChildren(productOperations, lChild, rChild);
596
- }
597
-
598
- function unionUntypedProduct<T>(
599
- productOperations: ProductOperations<T>,
600
- left: UntypedProductSet<T>,
601
- right: UntypedProductSet<T>,
602
- currentBox: Box<T>,
603
- ): UntypedProductSet<T> {
604
- if (right === empty) {
605
- return left;
606
- }
607
- if (left === empty) {
608
- return right;
609
- }
610
- if (left === dense) {
611
- return left;
612
- }
613
- if (right === dense) {
614
- return right;
615
- }
616
- if (left.isSubspace && right.isSubspace) {
617
- const combinedSubspace = tryUnionSubspaces(productOperations, left, right);
618
- if (combinedSubspace !== undefined) {
619
- return combinedSubspace;
620
- }
621
- }
622
-
623
- return recurse(productOperations, left, right, currentBox, unionUntypedProduct);
624
- }
625
-
626
- export function unionProduct<T extends Compatible<U, T>, U extends Compatible<T, U>>(
627
- left: ProductSet<T>,
628
- right: ProductSet<U>,
629
- ): ProductSet<T & U> {
630
- if (right === empty) {
631
- return left;
632
- }
633
- if (right === dense) {
634
- return right;
635
- }
636
- if (left === empty) {
637
- return right;
638
- }
639
- if (left === dense) {
640
- return left;
641
- }
642
- const productOperations = { ...left.productOperations, ...right.productOperations };
643
- const res = unionUntypedProduct(
644
- productOperations,
645
- left.root,
646
- right.root,
647
- top(productOperations),
648
- );
649
- if (res === empty) {
650
- return res;
651
- }
652
- return sparseProduct(productOperations, res);
653
- }
654
-
655
- function intersectUntypedProduct<T>(
656
- productOperations: ProductOperations<T>,
657
- left: UntypedProductSet<T>,
658
- right: UntypedProductSet<T>,
659
- currentBox: Box<T>,
660
- ): UntypedProductSet<T> {
661
- if (left === empty || right === empty) {
662
- return empty;
663
- }
664
- if (left === dense) {
665
- return right;
666
- }
667
- if (right === dense) {
668
- return left;
669
- }
670
- if (left.isSubspace && right.isSubspace) {
671
- const res = unsafe.combineProduct(
672
- productOperations,
673
- left.bounds,
674
- right.bounds,
675
- intersectUntyped,
676
- );
677
- if (res === empty) {
678
- return empty;
679
- }
680
- return subspace(res);
681
- }
682
-
683
- return recurse(productOperations, left, right, currentBox, intersectUntypedProduct);
684
- }
685
-
686
- export function intersectProduct<T extends Compatible<U, T>, U extends Compatible<T, U>>(
687
- left: ProductSet<T>,
688
- right: ProductSet<U>,
689
- ): ProductSet<T & U> {
690
- if (left === empty) {
691
- return left;
692
- }
693
- if (right === empty) {
694
- return right;
695
- }
696
- if (left === dense) {
697
- return right;
698
- }
699
- if (right === dense) {
700
- return left;
701
- }
702
-
703
- const productOperations = { ...left.productOperations, ...right.productOperations };
704
- const res = intersectUntypedProduct(
705
- productOperations,
706
- left.root,
707
- right.root,
708
- top(productOperations),
709
- );
710
- if (res === empty) {
711
- return res;
712
- }
713
- return sparseProduct(productOperations, res);
714
- }
715
-
716
- function tryExceptSubspaces<T>(
717
- productOperations: ProductOperations<T>,
718
- left: Subspace<T>,
719
- right: Subspace<T>,
720
- ): Subspace<T> | Dense | Empty | undefined {
721
- const cmp = compareSubspace(productOperations, left.bounds, right.bounds);
722
- if (cmp === 0 || cmp === -1) {
723
- return empty;
724
- }
725
- let notContainedDimension: keyof T | undefined;
726
- // because Object.keys only returns string[], we need to downcast
727
- const po_keys = Object.keys(productOperations) as (keyof T)[];
728
- for (const dim of po_keys) {
729
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
730
- const cmp_inner = compareUntyped<unknown, unknown>(
731
- productOperations[dim],
732
- toBspSet(left.bounds[dim]),
733
- toBspSet(right.bounds[dim]),
734
- );
735
- if (cmp_inner === undefined || cmp_inner > 0) {
736
- if (notContainedDimension !== undefined) {
737
- return undefined;
738
- }
739
-
740
- notContainedDimension = dim;
741
- }
742
- }
743
- }
744
-
745
- if (notContainedDimension !== undefined) {
746
- const newDim = exceptUntyped<unknown, unknown>(
747
- productOperations[notContainedDimension],
748
- toBspSet(left.bounds[notContainedDimension]),
749
- toBspSet(right.bounds[notContainedDimension]),
750
- );
751
-
752
- if (newDim === empty) {
753
- return empty;
754
- }
755
- if (newDim === dense) {
756
- // we are actually deleting the `differentDimension`, so the variable
757
- // `deleted` must be there. Hence disabling the rule here.
758
- const { [notContainedDimension]: deleted, ...leftBoundsWithoutDifferentDimension } =
759
- left.bounds;
760
- return subspace<unknown>(leftBoundsWithoutDifferentDimension);
761
- }
762
- const newBounds: UntypedProduct<T> = {
763
- ...left.bounds,
764
- [notContainedDimension]: newDim,
765
- };
766
- return subspace(newBounds);
767
- }
768
-
769
- return undefined;
770
- }
771
-
772
- function exceptUntypedProduct<T>(
773
- productOperations: ProductOperations<T>,
774
- left: UntypedProductSet<T>,
775
- right: UntypedProductSet<T>,
776
- currentBox: Box<T>,
777
- ): UntypedProductSet<T> {
778
- if (left === empty) {
779
- return left;
780
- }
781
- if (right === dense) {
782
- return empty;
783
- }
784
- if (right === empty) {
785
- return left;
786
- }
787
-
788
- if (left === dense) {
789
- return recurse(productOperations, left, right, currentBox, exceptUntypedProduct);
790
- }
791
-
792
- if (left.isSubspace && right.isSubspace) {
793
- const combinedSubspace = tryExceptSubspaces(productOperations, left, right);
794
- if (combinedSubspace !== undefined) {
795
- return combinedSubspace;
796
- }
797
- }
798
-
799
- return recurse(productOperations, left, right, currentBox, exceptUntypedProduct);
800
- }
801
-
802
- export function exceptProduct<T extends Compatible<U, T>, U extends Compatible<T, U>>(
803
- left: ProductSet<T>,
804
- right: ProductSet<U>,
805
- ): ProductSet<T & U> {
806
- if (left === empty) {
807
- return left;
808
- }
809
- if (right === empty) {
810
- return left;
811
- }
812
- if (right === dense) {
813
- return empty;
814
- }
815
- if (left === dense) {
816
- const res_inner = exceptUntypedProduct(
817
- right.productOperations,
818
- dense,
819
- right.root,
820
- top(right.productOperations),
821
- );
822
- if (res_inner === empty) {
823
- return res_inner;
824
- }
825
- return sparseProduct(right.productOperations, res_inner);
826
- }
827
-
828
- const productOperations = { ...left.productOperations, ...right.productOperations };
829
- const res = exceptUntypedProduct(
830
- productOperations,
831
- left.root,
832
- right.root,
833
- top(productOperations),
834
- );
835
- if (res === empty) {
836
- return res;
837
- }
838
- return sparseProduct(productOperations, res);
839
- }
840
-
841
- function compareUntypedProduct<T>(
842
- productOperations: ProductOperations<T>,
843
- left: UntypedProductSet<T>,
844
- right: UntypedProductSet<T>,
845
- boundingBox: Box<T>,
846
- ): -1 | 0 | 1 | undefined {
847
- if (left === right) {
848
- return 0;
849
- }
850
-
851
- if (left === empty) {
852
- return -1;
853
- }
854
-
855
- if (right === empty) {
856
- return 1;
857
- }
858
-
859
- if (left === dense) {
860
- if (right === dense) {
861
- return 0;
862
- }
863
-
864
- return 1;
865
- }
866
-
867
- if (right === dense) {
868
- return -1;
869
- }
870
-
871
- if (left.isSubspace) {
872
- if (right.isSubspace) {
873
- return compareSubspace(productOperations, left.bounds, right.bounds);
874
- }
875
- }
876
-
877
- const [leftBox, rightBox] = splitBox(productOperations, boundingBox);
878
- const [ll, lr] = splitByBox(productOperations, left, leftBox, rightBox);
879
- const [rl, rr] = splitByBox(productOperations, right, leftBox, rightBox);
880
-
881
- const leftCmp = compareUntypedProduct(productOperations, ll, rl, leftBox);
882
- if (leftCmp === undefined) {
883
- return undefined;
884
- }
885
- return combineCmp(leftCmp, compareUntypedProduct(productOperations, lr, rr, rightBox));
886
- }
887
-
888
- export function compareProduct<T extends Compatible<U, T>, U extends Compatible<T, U>>(
889
- left: ProductSet<T>,
890
- right: ProductSet<U>,
891
- ) {
892
- if (left === right) {
893
- return 0;
894
- }
895
-
896
- if (left === empty) {
897
- return -1;
898
- }
899
-
900
- if (right === empty) {
901
- return 1;
902
- }
903
-
904
- if (left === dense) {
905
- if (right === dense) {
906
- return 0;
907
- }
908
-
909
- return 1;
910
- }
911
-
912
- if (right === dense) {
913
- return -1;
914
- }
915
-
916
- const productOperations = { ...left.productOperations, ...right.productOperations };
917
- return compareUntypedProduct(productOperations, left.root, right.root, top(productOperations));
918
- }
919
-
920
- function meetsSubspace<T>(
921
- productOperations: ProductOperations<T>,
922
- left: UntypedProduct<T>,
923
- right: UntypedProduct<T>,
924
- ) {
925
- for (const dimStr of Object.keys(productOperations)) {
926
- const dim = dimStr as keyof T;
927
- if (Object.prototype.hasOwnProperty.call(productOperations, dim)) {
928
- const lProj = toBspSet(left[dim]);
929
- const rProj = toBspSet(right[dim]);
930
- const setOperations = productOperations[dim];
931
- if (!meetsUntyped<unknown, unknown>(setOperations, lProj, rProj)) {
932
- return false;
933
- }
934
- }
935
- }
936
-
937
- return true;
938
- }
939
-
940
- function meetsUntypedProduct<T>(
941
- productOperations: ProductOperations<T>,
942
- left: UntypedProductSet<T>,
943
- right: UntypedProductSet<T>,
944
- boundingBox: Box<T>,
945
- ): boolean {
946
- if (left === empty || right === empty) {
947
- return false;
948
- }
949
- if (left === dense || right === dense) {
950
- return true;
951
- }
952
- if (!meetsSubspace(productOperations, left.bounds, right.bounds)) {
953
- return false;
954
- }
955
- if (left.isSubspace && right.isSubspace) {
956
- return true;
957
- }
958
-
959
- const [leftBox, rightBox] = splitBox(productOperations, boundingBox);
960
- const [ll, lr] = splitByBox(productOperations, left, leftBox, rightBox);
961
- const [rl, rr] = splitByBox(productOperations, right, leftBox, rightBox);
962
-
963
- return (
964
- meetsUntypedProduct(productOperations, ll, rl, leftBox) ||
965
- meetsUntypedProduct(productOperations, lr, rr, rightBox)
966
- );
967
- }
968
-
969
- export function meetsProduct<T extends Compatible<U, T>, U extends Compatible<T, U>>(
970
- left: ProductSet<T>,
971
- right: ProductSet<U>,
972
- ) {
973
- if (left === empty || right === empty) {
974
- return false;
975
- }
976
- if (left === dense || right === dense) {
977
- return true;
978
- }
979
-
980
- const productOperations = { ...left.productOperations, ...right.productOperations };
981
- return meetsUntypedProduct(productOperations, left.root, right.root, top(productOperations));
982
- }
983
-
984
- export const complementProduct = <T>(set: ProductSet<T>) => exceptProduct(dense, set);
985
-
986
- export const symmetricDiffProduct = <T>(left: ProductSet<T>, right: ProductSet<T>) =>
987
- unionProduct(exceptProduct(left, right), exceptProduct(right, left));
988
-
989
- export function getSubspaces<T>(set: ProductSet<T>) {
990
- if (set === empty || set === dense) {
991
- return [];
992
- }
993
- const res: UntypedProduct<T>[] = [];
994
- function loop(root: UntypedSparseProduct<T>) {
995
- if (root.isSubspace) {
996
- res.push(root.bounds);
997
- return;
998
- }
999
-
1000
- loop(root.left);
1001
- loop(root.right);
1002
- }
1003
-
1004
- loop(set.root);
1005
- return res;
1006
- }
1007
-
1008
- export function forEachProduct<T, Props extends (keyof T)[]>(
1009
- set: ProductSet<T>,
1010
- f: (product: Product<Restrict<T, Props>>) => boolean,
1011
- ...dims: Props
1012
- ): boolean {
1013
- const newSet = project(set, ...dims);
1014
- if (newSet === empty) {
1015
- return true;
1016
- }
1017
- if (newSet === dense) {
1018
- return f(unsafe.denseProduct(dims));
1019
- }
1020
-
1021
- const { productOperations, root } = newSet;
1022
-
1023
- function loop(root_inner: UntypedSparseProduct<T>): boolean {
1024
- if (root_inner.isSubspace) {
1025
- return f(unsafe.fromUntypedProduct(productOperations, root_inner.bounds, dims));
1026
- }
1027
- return loop(root_inner.left) && loop(root_inner.right);
1028
- }
1029
-
1030
- return loop(root);
1031
- }
1032
-
1033
- export function getSubspaceCount<T>(set: ProductSet<T>) {
1034
- if (set === empty || set === dense) {
1035
- return 0;
1036
- }
1037
- return getUntypedSubspaceCount(set.root);
1038
- }