@fluidframework/matrix 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.3.1.0.125672

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