recursive-set 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,278 +1,494 @@
1
- import createTree from 'functional-red-black-tree';
2
1
  /**
3
2
  * @module recursive-set
4
- * A mutable recursive set implementation.
5
- * Powered by functional Red-Black Trees for O(log n) operations and O(1) cloning.
3
+ * High-Performance Mutable Recursive Set backed by Sorted Arrays.
4
+ * Optimized for small sets, structural equality, and deterministic hashing.
6
5
  */
6
+ // === HASHING ENGINE ===
7
7
  /**
8
- * A lightweight wrapper for Tuples to enable Value-Equality in RecursiveSet.
9
- * Immutable by design.
8
+ * FNV-1a Hash implementation for strings.
9
+ * Constants inlined for V8 optimization.
10
10
  */
11
- export class Tuple {
12
- values;
13
- constructor(...values) {
14
- this.values = values;
11
+ function hashString(str) {
12
+ let hash = 0x811c9dc5;
13
+ for (let i = 0; i < str.length; i++) {
14
+ hash ^= str.charCodeAt(i);
15
+ hash = Math.imul(hash, 0x01000193);
15
16
  }
16
- get length() {
17
- return this.values.length;
17
+ return hash >>> 0;
18
+ }
19
+ /**
20
+ * Universal Hash Function.
21
+ * Calculates deterministic hashes for Primitives, Sequences (Order Dependent), and Sets.
22
+ */
23
+ function hashValue(val) {
24
+ if (typeof val === 'string')
25
+ return hashString(val);
26
+ if (typeof val === 'number')
27
+ return val | 0;
28
+ // Fast Path: Objects with cached hash
29
+ if (val instanceof RecursiveSet)
30
+ return val.hashCode;
31
+ if (val instanceof Tuple)
32
+ return val.hashCode;
33
+ // Arrays: Treated as sequences (Rolling Hash)
34
+ if (Array.isArray(val)) {
35
+ let h = 0;
36
+ for (let i = 0; i < val.length; i++) {
37
+ let v = val[i];
38
+ let vh = 0;
39
+ if (typeof v === 'string')
40
+ vh = hashString(v);
41
+ else
42
+ vh = hashValue(v);
43
+ h = Math.imul(31, h) + vh;
44
+ }
45
+ return h >>> 0;
18
46
  }
19
- get(index) {
20
- return this.values[index];
47
+ return 0;
48
+ }
49
+ // === COMPARATOR ===
50
+ /**
51
+ * High-performance comparator with hash short-circuiting.
52
+ * Order: Primitives (0) < Sequences (1) < Sets (2)
53
+ */
54
+ function compare(a, b) {
55
+ if (a === b)
56
+ return 0;
57
+ // 1. Hash Short-Circuit
58
+ // Using interface casting avoids runtime overhead of 'in' operator checks
59
+ const aH = a?.hashCode;
60
+ const bH = b?.hashCode;
61
+ const ha = (aH !== undefined) ? aH : hashValue(a);
62
+ const hb = (bH !== undefined) ? bH : hashValue(b);
63
+ if (ha !== hb)
64
+ return ha < hb ? -1 : 1;
65
+ // 2. Primitive Value Check
66
+ const typeA = typeof a;
67
+ const typeB = typeof b;
68
+ if (typeA === 'string' && typeB === 'string')
69
+ return a < b ? -1 : 1;
70
+ if (typeA === 'number' && typeB === 'number')
71
+ return a < b ? -1 : 1;
72
+ // 3. Structural Type Check
73
+ const isSetA = a instanceof RecursiveSet;
74
+ const isSetB = b instanceof RecursiveSet;
75
+ if (isSetA && isSetB) {
76
+ return a.compare(b);
21
77
  }
22
- *[Symbol.iterator]() {
23
- for (const val of this.values) {
24
- yield val;
25
- }
78
+ const isArrA = Array.isArray(a);
79
+ const isArrB = Array.isArray(b);
80
+ const isTupA = a instanceof Tuple;
81
+ const isTupB = b instanceof Tuple;
82
+ const isSeqA = isArrA || isTupA;
83
+ const isSeqB = isArrB || isTupB;
84
+ // Sort by Type Group if types differ
85
+ if (isSetA !== isSetB || isSeqA !== isSeqB) {
86
+ const scoreA = isSetA ? 2 : isSeqA ? 1 : 0;
87
+ const scoreB = isSetB ? 2 : isSeqB ? 1 : 0;
88
+ return scoreA - scoreB;
26
89
  }
27
- toString() {
28
- return `(${this.values.map(v => String(v)).join(', ')})`;
90
+ // 4. Sequence Comparison (Array/Tuple)
91
+ if (isSeqA && isSeqB) {
92
+ const valA = isTupA ? a.values : a;
93
+ const valB = isTupB ? b.values : b;
94
+ const len = valA.length;
95
+ if (len !== valB.length)
96
+ return len - valB.length;
97
+ for (let i = 0; i < len; i++) {
98
+ const diff = compare(valA[i], valB[i]);
99
+ if (diff !== 0)
100
+ return diff;
101
+ }
102
+ return 0;
29
103
  }
30
- [Symbol.for('nodejs.util.inspect.custom')]() {
31
- return this.toString();
104
+ // Fallback for safe types
105
+ return a < b ? -1 : 1;
106
+ }
107
+ // === CLASSES ===
108
+ /**
109
+ * Immutable wrapper for sequence values.
110
+ * Useful when strict typing for sequences is required.
111
+ */
112
+ export class Tuple {
113
+ values;
114
+ hashCode;
115
+ constructor(...values) {
116
+ this.values = values;
117
+ this.hashCode = hashValue(values);
32
118
  }
119
+ get length() { return this.values.length; }
120
+ get(i) { return this.values[i]; }
121
+ *[Symbol.iterator]() { yield* this.values; }
122
+ toString() { return `(${this.values.join(', ')})`; }
123
+ [Symbol.for('nodejs.util.inspect.custom')]() { return this.toString(); }
33
124
  }
125
+ /**
126
+ * A Set implementation that supports deep structural equality and efficient hashing.
127
+ * Internally backed by a sorted array for optimal CPU cache locality on small sets.
128
+ */
34
129
  export class RecursiveSet {
35
- _tree;
36
130
  /**
37
- * Static comparator for Red-Black Tree ordering.
38
- * Supports Primitives, RecursiveSets and Tuples.
39
- * REJECTS plain JS Objects and Arrays to enforce strict semantics.
131
+ * Internal storage. Public for inlining access within the module, but treated as private API.
40
132
  */
41
- static compare(a, b) {
42
- if (a === b)
43
- return 0;
44
- const isSetA = a instanceof RecursiveSet;
45
- const isSetB = b instanceof RecursiveSet;
46
- const isTupA = a instanceof Tuple;
47
- const isTupB = b instanceof Tuple;
48
- // Sort Order: Primitives (0) < Tuples (1) < Sets (2)
49
- const getTypeScore = (isSet, isTup) => {
50
- if (isSet)
51
- return 2;
52
- if (isTup)
53
- return 1;
54
- return 0;
55
- };
56
- const scoreA = getTypeScore(isSetA, isTupA);
57
- const scoreB = getTypeScore(isSetB, isTupB);
58
- if (scoreA !== scoreB)
59
- return scoreA < scoreB ? -1 : 1;
60
- // 1. Sets
61
- if (isSetA && isSetB) {
62
- const setA = a;
63
- const setB = b;
64
- if (setA.size !== setB.size)
65
- return setA.size < setB.size ? -1 : 1;
66
- let iterA = setA._tree.begin;
67
- let iterB = setB._tree.begin;
68
- while (iterA.valid && iterB.valid) {
69
- const cmp = RecursiveSet.compare(iterA.key, iterB.key);
70
- if (cmp !== 0)
71
- return cmp;
72
- iterA.next();
73
- iterB.next();
74
- }
75
- return 0;
133
+ _elements;
134
+ _hashCode = null;
135
+ static compare(a, b) { return compare(a, b); }
136
+ constructor(...elements) {
137
+ if (elements.length === 0) {
138
+ this._elements = [];
139
+ this._hashCode = 0;
76
140
  }
77
- // 2. Tuples
78
- if (isTupA && isTupB) {
79
- const tupA = a;
80
- const tupB = b;
81
- if (tupA.length !== tupB.length)
82
- return tupA.length < tupB.length ? -1 : 1;
83
- for (let i = 0; i < tupA.length; i++) {
84
- const cmp = RecursiveSet.compare(tupA.get(i), tupB.get(i));
85
- if (cmp !== 0)
86
- return cmp;
87
- }
88
- return 0;
141
+ else {
142
+ this._elements = elements;
143
+ this._elements.sort(compare);
144
+ this._unique();
89
145
  }
90
- // 3. Primitives (guaranteed by add() validation)
91
- const tA = typeof a;
92
- const tB = typeof b;
93
- if (tA !== tB)
94
- return tA > tB ? 1 : -1;
95
- // @ts-ignore
96
- if (a < b)
97
- return -1;
98
- // @ts-ignore
99
- if (a > b)
100
- return 1;
101
- return 0;
102
146
  }
103
- constructor(...elements) {
104
- this._tree = createTree(RecursiveSet.compare);
105
- for (const el of elements) {
106
- this.add(el);
147
+ _unique() {
148
+ const arr = this._elements;
149
+ const len = arr.length;
150
+ if (len < 2)
151
+ return;
152
+ let write = 1;
153
+ for (let read = 1; read < len; read++) {
154
+ if (compare(arr[read], arr[read - 1]) !== 0) {
155
+ arr[write++] = arr[read];
156
+ }
107
157
  }
158
+ arr.length = write;
108
159
  }
109
- // === Copy-on-Write Support ===
110
160
  /**
111
- * Creates a shallow copy of the set in O(1) time.
161
+ * Calculates the hash code for the set.
162
+ * Uses a rolling hash over sorted elements, ensuring determinstic results for equal sets.
112
163
  */
113
- clone() {
114
- const clone = new RecursiveSet();
115
- clone._tree = this._tree;
116
- return clone;
164
+ get hashCode() {
165
+ if (this._hashCode !== null)
166
+ return this._hashCode;
167
+ let h = 0;
168
+ const arr = this._elements;
169
+ const len = arr.length;
170
+ for (let i = 0; i < len; i++) {
171
+ h = Math.imul(31, h) + hashValue(arr[i]);
172
+ }
173
+ this._hashCode = h | 0;
174
+ return this._hashCode;
175
+ }
176
+ // Backward compatibility alias
177
+ getHashCode() { return this.hashCode; }
178
+ compare(other) {
179
+ if (this === other)
180
+ return 0;
181
+ const h1 = this.hashCode;
182
+ const h2 = other.hashCode;
183
+ if (h1 !== h2)
184
+ return h1 < h2 ? -1 : 1;
185
+ const arrA = this._elements;
186
+ const arrB = other._elements;
187
+ const len = arrA.length;
188
+ if (len !== arrB.length)
189
+ return len - arrB.length;
190
+ for (let i = 0; i < len; i++) {
191
+ const cmp = compare(arrA[i], arrB[i]);
192
+ if (cmp !== 0)
193
+ return cmp;
194
+ }
195
+ return 0;
196
+ }
197
+ get size() { return this._elements.length; }
198
+ isEmpty() { return this._elements.length === 0; }
199
+ has(element) {
200
+ const arr = this._elements;
201
+ const len = arr.length;
202
+ // Small Array Optimization: Linear Scan is faster than Binary Search overhead for N < 16
203
+ if (len < 16) {
204
+ for (let i = 0; i < len; i++) {
205
+ if (compare(arr[i], element) === 0)
206
+ return true;
207
+ }
208
+ return false;
209
+ }
210
+ let low = 0, high = len - 1;
211
+ while (low <= high) {
212
+ const mid = (low + high) >>> 1;
213
+ const cmp = compare(arr[mid], element);
214
+ if (cmp === 0)
215
+ return true;
216
+ if (cmp < 0)
217
+ low = mid + 1;
218
+ else
219
+ high = mid - 1;
220
+ }
221
+ return false;
117
222
  }
118
- // === Mutable Operations ===
119
223
  add(element) {
120
- // Validation
121
- if (typeof element === "number" && Number.isNaN(element)) {
224
+ // --- Validation Check ---
225
+ if (typeof element === 'object' && element !== null) {
226
+ const isSet = element instanceof RecursiveSet;
227
+ const isTup = element instanceof Tuple;
228
+ const isArr = Array.isArray(element);
229
+ if (!isSet && !isTup && !isArr) {
230
+ throw new Error("Plain Objects are not supported. Use Tuple, Array or RecursiveSet.");
231
+ }
232
+ }
233
+ else if (typeof element === "number" && Number.isNaN(element)) {
122
234
  throw new Error("NaN is not supported");
123
235
  }
124
- const isSet = element instanceof RecursiveSet;
125
- const isTup = element instanceof Tuple;
126
- const isObject = element !== null && typeof element === 'object' && !isSet && !isTup;
127
- if (isObject) {
128
- throw new Error("Plain Objects and Arrays are not supported. " +
129
- "Use Tuple for sequences or RecursiveSet for nested structures.");
236
+ // --- End Validation ---
237
+ const arr = this._elements;
238
+ const len = arr.length;
239
+ // Common Case: Appending a larger element (during ordered construction)
240
+ if (len > 0) {
241
+ const lastCmp = compare(arr[len - 1], element);
242
+ if (lastCmp < 0) {
243
+ arr.push(element);
244
+ this._hashCode = null;
245
+ return this;
246
+ }
247
+ if (lastCmp === 0)
248
+ return this;
249
+ }
250
+ else {
251
+ arr.push(element);
252
+ this._hashCode = null;
253
+ return this;
130
254
  }
131
- // Idempotency
132
- if (this.has(element))
255
+ // Small Array Strategy: Linear Scan + Splice
256
+ if (len < 16) {
257
+ for (let i = 0; i < len; i++) {
258
+ const cmp = compare(arr[i], element);
259
+ if (cmp === 0)
260
+ return this;
261
+ if (cmp > 0) {
262
+ arr.splice(i, 0, element);
263
+ this._hashCode = null;
264
+ return this;
265
+ }
266
+ }
133
267
  return this;
134
- this._tree = this._tree.insert(element, true);
268
+ }
269
+ // Large Array Strategy: Binary Search
270
+ let low = 0, high = len - 1, idx = 0;
271
+ while (low <= high) {
272
+ const mid = (low + high) >>> 1;
273
+ const cmp = compare(arr[mid], element);
274
+ if (cmp === 0)
275
+ return this;
276
+ if (cmp < 0) {
277
+ idx = mid + 1;
278
+ low = mid + 1;
279
+ }
280
+ else {
281
+ idx = mid;
282
+ high = mid - 1;
283
+ }
284
+ }
285
+ arr.splice(idx, 0, element);
286
+ this._hashCode = null;
135
287
  return this;
136
288
  }
137
289
  remove(element) {
138
- this._tree = this._tree.remove(element);
290
+ const arr = this._elements;
291
+ const len = arr.length;
292
+ if (len < 16) {
293
+ for (let i = 0; i < len; i++) {
294
+ if (compare(arr[i], element) === 0) {
295
+ arr.splice(i, 1);
296
+ this._hashCode = null;
297
+ return this;
298
+ }
299
+ }
300
+ return this;
301
+ }
302
+ let low = 0, high = len - 1;
303
+ while (low <= high) {
304
+ const mid = (low + high) >>> 1;
305
+ const cmp = compare(arr[mid], element);
306
+ if (cmp === 0) {
307
+ arr.splice(mid, 1);
308
+ this._hashCode = null;
309
+ return this;
310
+ }
311
+ if (cmp < 0)
312
+ low = mid + 1;
313
+ else
314
+ high = mid - 1;
315
+ }
139
316
  return this;
140
317
  }
141
318
  clear() {
142
- this._tree = createTree(RecursiveSet.compare);
319
+ this._elements = [];
320
+ this._hashCode = 0;
143
321
  return this;
144
322
  }
145
- // === Set Operations ===
323
+ clone() {
324
+ const s = new RecursiveSet();
325
+ s._elements = this._elements.slice();
326
+ s._hashCode = this._hashCode;
327
+ return s;
328
+ }
146
329
  union(other) {
147
- const result = this.clone();
148
- for (const el of other)
149
- result.add(el);
150
- return result;
330
+ const s = new RecursiveSet();
331
+ const arrA = this._elements;
332
+ const arrB = other._elements;
333
+ if (arrA.length === 0)
334
+ return other.clone();
335
+ if (arrB.length === 0)
336
+ return this.clone();
337
+ const res = [];
338
+ let i = 0, j = 0;
339
+ const lenA = arrA.length, lenB = arrB.length;
340
+ // Merge Sort Algorithm O(N + M)
341
+ while (i < lenA && j < lenB) {
342
+ const cmp = compare(arrA[i], arrB[j]);
343
+ if (cmp < 0)
344
+ res.push(arrA[i++]);
345
+ else if (cmp > 0)
346
+ res.push(arrB[j++]);
347
+ else {
348
+ res.push(arrA[i++]);
349
+ j++;
350
+ }
351
+ }
352
+ while (i < lenA)
353
+ res.push(arrA[i++]);
354
+ while (j < lenB)
355
+ res.push(arrB[j++]);
356
+ s._elements = res;
357
+ return s;
151
358
  }
152
359
  intersection(other) {
153
- const result = new RecursiveSet();
154
- const [smaller, larger] = this.size < other.size ? [this, other] : [other, this];
155
- for (const el of smaller) {
156
- if (larger.has(el)) {
157
- result.add(el);
360
+ const s = new RecursiveSet();
361
+ const arrA = this._elements;
362
+ const arrB = other._elements;
363
+ const res = [];
364
+ let i = 0, j = 0;
365
+ const lenA = arrA.length, lenB = arrB.length;
366
+ while (i < lenA && j < lenB) {
367
+ const cmp = compare(arrA[i], arrB[j]);
368
+ if (cmp < 0)
369
+ i++;
370
+ else if (cmp > 0)
371
+ j++;
372
+ else {
373
+ res.push(arrA[i++]);
374
+ j++;
158
375
  }
159
376
  }
160
- return result;
377
+ s._elements = res;
378
+ return s;
161
379
  }
162
380
  difference(other) {
163
- const result = new RecursiveSet();
164
- for (const el of this) {
165
- if (!other.has(el)) {
166
- result.add(el);
381
+ const s = new RecursiveSet();
382
+ const arrA = this._elements;
383
+ const arrB = other._elements;
384
+ const res = [];
385
+ let i = 0, j = 0;
386
+ const lenA = arrA.length, lenB = arrB.length;
387
+ while (i < lenA && j < lenB) {
388
+ const cmp = compare(arrA[i], arrB[j]);
389
+ if (cmp < 0)
390
+ res.push(arrA[i++]);
391
+ else if (cmp > 0)
392
+ j++;
393
+ else {
394
+ i++;
395
+ j++;
167
396
  }
168
397
  }
169
- return result;
398
+ while (i < lenA)
399
+ res.push(arrA[i++]);
400
+ s._elements = res;
401
+ return s;
170
402
  }
171
403
  symmetricDifference(other) {
172
- return this.difference(other).union(other.difference(this));
404
+ const s = new RecursiveSet();
405
+ const arrA = this._elements;
406
+ const arrB = other._elements;
407
+ const res = [];
408
+ let i = 0, j = 0;
409
+ const lenA = arrA.length, lenB = arrB.length;
410
+ while (i < lenA && j < lenB) {
411
+ const cmp = compare(arrA[i], arrB[j]);
412
+ if (cmp < 0)
413
+ res.push(arrA[i++]);
414
+ else if (cmp > 0)
415
+ res.push(arrB[j++]);
416
+ else {
417
+ i++;
418
+ j++;
419
+ }
420
+ }
421
+ while (i < lenA)
422
+ res.push(arrA[i++]);
423
+ while (j < lenB)
424
+ res.push(arrB[j++]);
425
+ s._elements = res;
426
+ return s;
173
427
  }
174
428
  powerset() {
175
429
  const n = this.size;
176
- if (n > 30)
177
- throw new Error("Powerset size exceeds 32-bit integer limit");
178
- const elements = [];
179
- this._tree.forEach((key) => { elements.push(key); });
430
+ if (n > 20)
431
+ throw new Error("Powerset too large");
180
432
  const subsets = [];
181
- for (let i = 0; i < (1 << n); i++) {
433
+ const max = 1 << n;
434
+ for (let i = 0; i < max; i++) {
182
435
  const subset = new RecursiveSet();
183
436
  for (let j = 0; j < n; j++) {
184
- if (i & (1 << j)) {
185
- subset.add(elements[j]);
186
- }
437
+ if (i & (1 << j))
438
+ subset._elements.push(this._elements[j]);
187
439
  }
188
440
  subsets.push(subset);
189
441
  }
190
442
  return new RecursiveSet(...subsets);
191
443
  }
192
- /**
193
- * Returns the Cartesian product as a set of Tuples.
194
- * Uses the Tuple class to ensure structural equality.
195
- */
196
444
  cartesianProduct(other) {
197
445
  const result = new RecursiveSet();
198
- for (const x of this) {
199
- for (const y of other) {
200
- result.add(new Tuple(x, y));
446
+ const arrA = this._elements;
447
+ const arrB = other._elements;
448
+ for (const x of arrA) {
449
+ for (const y of arrB) {
450
+ result._elements.push(new Tuple(x, y));
201
451
  }
202
452
  }
203
453
  return result;
204
454
  }
205
- // === Predicates ===
206
- has(element) {
207
- return this._tree.get(element) !== undefined;
208
- }
455
+ // Standard Set methods
209
456
  isSubset(other) {
210
457
  if (this.size > other.size)
211
458
  return false;
212
- for (const el of this) {
213
- if (!other.has(el))
459
+ let i = 0, j = 0;
460
+ const arrA = this._elements, arrB = other._elements;
461
+ while (i < arrA.length && j < arrB.length) {
462
+ const cmp = compare(arrA[i], arrB[j]);
463
+ if (cmp < 0)
214
464
  return false;
465
+ if (cmp > 0)
466
+ j++;
467
+ else {
468
+ i++;
469
+ j++;
470
+ }
215
471
  }
216
- return true;
217
- }
218
- isSuperset(other) {
219
- return other.isSubset(this);
220
- }
221
- isProperSubset(other) {
222
- return this.isSubset(other) && !this.equals(other);
223
- }
224
- isEmpty() {
225
- return this.size === 0;
226
- }
227
- equals(other) {
228
- return RecursiveSet.compare(this, other) === 0;
229
- }
230
- // === Utility ===
231
- get size() {
232
- return this._tree.length;
233
- }
234
- toSet() {
235
- const result = new Set();
236
- this._tree.forEach((key) => { result.add(key); });
237
- return result;
238
- }
239
- // Lazy Iterator
240
- *[Symbol.iterator]() {
241
- let iter = this._tree.begin;
242
- while (iter.valid) {
243
- yield iter.key;
244
- iter.next();
245
- }
472
+ return i === arrA.length;
246
473
  }
474
+ isSuperset(other) { return other.isSubset(this); }
475
+ isProperSubset(other) { return this.isSubset(other) && this.size < other.size; }
476
+ equals(other) { return this.compare(other) === 0; }
477
+ toSet() { return new Set(this._elements); }
478
+ *[Symbol.iterator]() { yield* this._elements; }
247
479
  toString() {
248
480
  if (this.isEmpty())
249
481
  return "∅";
250
- const elements = [];
251
- this._tree.forEach((key) => {
252
- if (key instanceof RecursiveSet) {
253
- elements.push(key.toString());
254
- }
255
- else if (key instanceof Tuple) {
256
- elements.push(key.toString());
257
- }
258
- else {
259
- elements.push(String(key));
260
- }
482
+ const elementsStr = this._elements.map(el => {
483
+ if (Array.isArray(el))
484
+ return `[${el.join(', ')}]`;
485
+ return String(el);
261
486
  });
262
- return `{${elements.join(", ")}}`;
263
- }
264
- [Symbol.for('nodejs.util.inspect.custom')]() {
265
- return this.toString();
487
+ return `{${elementsStr.join(', ')}}`;
266
488
  }
489
+ [Symbol.for('nodejs.util.inspect.custom')]() { return this.toString(); }
267
490
  }
268
- // === Helpers ===
269
- export function emptySet() {
270
- return new RecursiveSet();
271
- }
272
- export function singleton(element) {
273
- return new RecursiveSet(element);
274
- }
275
- export function fromIterable(iterable) {
276
- return new RecursiveSet(...iterable);
277
- }
491
+ export function emptySet() { return new RecursiveSet(); }
492
+ export function singleton(element) { return new RecursiveSet(element); }
493
+ export function fromIterable(iterable) { return new RecursiveSet(...iterable); }
278
494
  //# sourceMappingURL=index.js.map