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