discombobulator 1.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/package.json +16 -0
- package/src/articulation.mjs +61 -0
- package/src/articulationset.mjs +20 -0
- package/src/chord.mjs +82 -0
- package/src/cross.mjs +133 -0
- package/src/disco.mjs +14 -0
- package/src/element.mjs +51 -0
- package/src/group.mjs +70 -0
- package/src/groupset.mjs +259 -0
- package/src/instrument.mjs +105 -0
- package/src/instrumentset.mjs +20 -0
- package/src/irregulargroupset.mjs +224 -0
- package/src/note.mjs +155 -0
- package/src/noteset.mjs +21 -0
- package/src/pattern.mjs +52 -0
- package/src/patternset.mjs +20 -0
- package/src/patternsource.mjs +21 -0
- package/src/permute.mjs +63 -0
- package/src/regulargroupset.mjs +120 -0
- package/src/schedule.mjs +88 -0
- package/src/schema.mjs +108 -0
- package/src/set.mjs +1159 -0
- package/src/test/articulation.mjs +66 -0
- package/src/test/articulationset.mjs +43 -0
- package/src/test/binaryoperator.mjs +210 -0
- package/src/test/chord.mjs +292 -0
- package/src/test/concolour.mjs +70 -0
- package/src/test/difference.mjs +492 -0
- package/src/test/element.mjs +66 -0
- package/src/test/group.mjs +79 -0
- package/src/test/groupset.mjs +43 -0
- package/src/test/instrument.mjs +66 -0
- package/src/test/instrumentset.mjs +43 -0
- package/src/test/intersection.mjs +493 -0
- package/src/test/irregulargroupset.mjs +461 -0
- package/src/test/note.mjs +367 -0
- package/src/test/noteset.mjs +43 -0
- package/src/test/operator.mjs +63 -0
- package/src/test/pattern.mjs +113 -0
- package/src/test/patternset.mjs +43 -0
- package/src/test/patternsource.mjs +43 -0
- package/src/test/regulargroupset.mjs +290 -0
- package/src/test/repl.mjs +63 -0
- package/src/test/replinit.mjs +134 -0
- package/src/test/rotation.mjs +753 -0
- package/src/test/schedule.mjs +275 -0
- package/src/test/schema.mjs +333 -0
- package/src/test/set.mjs +1081 -0
- package/src/test/symmetricdifference.mjs +495 -0
- package/src/test/test.mjs +208 -0
- package/src/test/testing.mjs +63 -0
- package/src/test/union.mjs +495 -0
- package/src/util.mjs +21 -0
package/src/set.mjs
ADDED
|
@@ -0,0 +1,1159 @@
|
|
|
1
|
+
// Set
|
|
2
|
+
//
|
|
3
|
+
// Base class for sets and sequences.
|
|
4
|
+
|
|
5
|
+
import util from "util";
|
|
6
|
+
|
|
7
|
+
import { Element } from "./element.mjs";
|
|
8
|
+
|
|
9
|
+
export class Set extends Element {
|
|
10
|
+
elements = []; // Array of elements for concrete sets.
|
|
11
|
+
#isAbstract = false; // True if set is abstract, false if concrete.
|
|
12
|
+
#allowDuplicates = false; // True if duplicates are allowed (sequence), false if not (set).
|
|
13
|
+
#strict = true; // True if strict equality should be applied to elements in the set.
|
|
14
|
+
#allowedElementTypes = undefined; // Explicitly specified allowed element types for
|
|
15
|
+
// arbitrary sets (those created from Set directly rather than
|
|
16
|
+
// being inherited from it)
|
|
17
|
+
|
|
18
|
+
// Construct a Set.
|
|
19
|
+
//
|
|
20
|
+
// options may contain:
|
|
21
|
+
// isAbstract true if this set is abstract, otherwise it will be concrete.
|
|
22
|
+
// allowDuplicates true if this set may contain duplicates, making it a sequence.
|
|
23
|
+
// add an element or array of elements to be added to this set upon construction.
|
|
24
|
+
// strict true if strict equality comparisons should be made between elements.
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
super();
|
|
27
|
+
|
|
28
|
+
const defaults = {
|
|
29
|
+
isAbstract: false,
|
|
30
|
+
allowDuplicates: false,
|
|
31
|
+
add: undefined,
|
|
32
|
+
strict: true,
|
|
33
|
+
allowedElements: undefined,
|
|
34
|
+
};
|
|
35
|
+
options = { ...defaults, ...options };
|
|
36
|
+
|
|
37
|
+
this.#isAbstract = options.isAbstract;
|
|
38
|
+
this.#allowDuplicates = options.allowDuplicates;
|
|
39
|
+
this.#strict = options.strict;
|
|
40
|
+
this.#allowedElementTypes = options.allowedElements;
|
|
41
|
+
|
|
42
|
+
if (!this.isAbstract && options.add !== undefined) {
|
|
43
|
+
this.add(options.add);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Returns an array of allowed element types. MUST BE IMPLEMENTED IN SUB CLASS.
|
|
48
|
+
get allowedElements() {
|
|
49
|
+
return this.#allowedElementTypes;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get [Symbol.toStringTag]() {
|
|
53
|
+
return "Set";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get isAbstract() {
|
|
57
|
+
return this.#isAbstract;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get allowDuplicates() {
|
|
61
|
+
return this.#allowDuplicates;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get isStrict() {
|
|
65
|
+
return this.#strict;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get size() {
|
|
69
|
+
return this.elements.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If possible and necessary, promotes the given element to the same
|
|
73
|
+
// type as this element.
|
|
74
|
+
//
|
|
75
|
+
// If the given element is already of the same type as this one, then
|
|
76
|
+
// the given element is returned unchanged.
|
|
77
|
+
//
|
|
78
|
+
// If the element can be promoted by the element, then it is added to
|
|
79
|
+
// this set.
|
|
80
|
+
//
|
|
81
|
+
// If the element cannot be promoted, undefined is returned.
|
|
82
|
+
promote(element) {
|
|
83
|
+
if (!(element && element instanceof Element)) return undefined;
|
|
84
|
+
|
|
85
|
+
if (this.constructor === element.constructor) {
|
|
86
|
+
return element;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!this.isAbstract) {
|
|
90
|
+
const allowedElements = this.allowedElements;
|
|
91
|
+
|
|
92
|
+
// Allow any Element if no allowedElements specified.
|
|
93
|
+
if (!allowedElements) return element;
|
|
94
|
+
|
|
95
|
+
// Check the immediately allowable element types.
|
|
96
|
+
for (const elementClass of allowedElements) {
|
|
97
|
+
if (element instanceof elementClass) {
|
|
98
|
+
this.internalAdd(element);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Now try to follow up the promotion chain.
|
|
104
|
+
let promoted = undefined;
|
|
105
|
+
|
|
106
|
+
for (const elementClass of allowedElements) {
|
|
107
|
+
let proxy = new elementClass();
|
|
108
|
+
promoted = proxy.promote(element);
|
|
109
|
+
|
|
110
|
+
if (promoted !== undefined) {
|
|
111
|
+
this.internalAdd(promoted);
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
isEqual(other) {
|
|
121
|
+
if (!super.isEqual(other)) return false;
|
|
122
|
+
|
|
123
|
+
if (this.size !== other.size) return false;
|
|
124
|
+
|
|
125
|
+
if (this.isStrict) {
|
|
126
|
+
let otherIterator = other[Symbol.iterator]();
|
|
127
|
+
|
|
128
|
+
for (let element of this) {
|
|
129
|
+
let value = otherIterator.next().value;
|
|
130
|
+
|
|
131
|
+
if (!element.isEqual(value)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return this.isStrict || (this.isSubsetOf(other) && other.isSubsetOf(this));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#assertMutable() {
|
|
141
|
+
if (this.isAbstract) throw new Error("Attempt to modify immutable set");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
[util.inspect.custom](depth, opts) {
|
|
145
|
+
return `${this[Symbol.toStringTag]} {${util.inspect(this.elements)}}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Set generator/iterator. By default, just enumerate the elements in the set.
|
|
149
|
+
*[Symbol.iterator]() {
|
|
150
|
+
for (let element of this.elements) {
|
|
151
|
+
yield element;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// SetOf provides a canonical way of describing a set as a hierarchy of sets of
|
|
156
|
+
// other sets or primitive types (Elements). It allows unambiguous comparison of
|
|
157
|
+
// set type independent of the class inheritence hierarchy.
|
|
158
|
+
static SetOf = class extends Set {
|
|
159
|
+
#setof = undefined;
|
|
160
|
+
|
|
161
|
+
constructor(setof, deep = false) {
|
|
162
|
+
if (setof === undefined) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"SetOf requires a set of allowed elements or another SetOf"
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
super();
|
|
169
|
+
|
|
170
|
+
this.#setof = [];
|
|
171
|
+
|
|
172
|
+
if (Array.isArray(setof)) {
|
|
173
|
+
for (let elementType of setof) {
|
|
174
|
+
this.#initSetOf(elementType, deep);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
this.#initSetOf(setof, deep);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#initSetOf(elementType, deep = false) {
|
|
182
|
+
if (elementType !== undefined) {
|
|
183
|
+
let element = new elementType();
|
|
184
|
+
|
|
185
|
+
if (element instanceof Element) {
|
|
186
|
+
if (deep && typeof elementType === "function") {
|
|
187
|
+
if (element instanceof Set) {
|
|
188
|
+
this.#setof.push(new Set.SetOf(element.allowedElements, true));
|
|
189
|
+
} else {
|
|
190
|
+
this.#setof.push(elementType);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
this.#setof.push(elementType);
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`SetOf: elements must derive from Element: ${elementType}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
throw new Error("SetOf: element type undefined");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get setof() {
|
|
206
|
+
return this.#setof;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get [Symbol.toStringTag]() {
|
|
210
|
+
return "SetOf";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[util.inspect.custom](depth, opts) {
|
|
214
|
+
return `${this[Symbol.toStringTag]} {${util.inspect(this.#setof)}}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get name() {
|
|
218
|
+
return this[util.inspect.custom]();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
equals(other) {
|
|
222
|
+
let thisSetOf = this.#setof;
|
|
223
|
+
let otherSetOf = other.#setof;
|
|
224
|
+
|
|
225
|
+
if (thisSetOf.length != otherSetOf.length) return false;
|
|
226
|
+
|
|
227
|
+
let index = 0;
|
|
228
|
+
let otherElementType;
|
|
229
|
+
|
|
230
|
+
// Note: testing is done in lock step because SetOf are generated based on the
|
|
231
|
+
// static definition of the classes, so should always be in the same order.
|
|
232
|
+
for (let elementType of thisSetOf) {
|
|
233
|
+
otherElementType = otherSetOf[index++];
|
|
234
|
+
|
|
235
|
+
if (elementType instanceof Set.SetOf) {
|
|
236
|
+
if (
|
|
237
|
+
!(otherElementType instanceof Set.SetOf) ||
|
|
238
|
+
!elementType.equals(otherElementType)
|
|
239
|
+
) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
if (
|
|
244
|
+
otherElementType instanceof Set.SetOf ||
|
|
245
|
+
elementType !== otherElementType
|
|
246
|
+
) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
#setof = {};
|
|
257
|
+
|
|
258
|
+
asSetOf(deep = false) {
|
|
259
|
+
return this.#setof[deep] !== undefined
|
|
260
|
+
? this.#setof[deep]
|
|
261
|
+
: (this.#setof[deep] = new Set.SetOf(this.allowedElements, deep));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Validates that the other Set given is of the same type as this one
|
|
265
|
+
// by ensuring both sets allow the same set of element types.
|
|
266
|
+
validateOther(other) {
|
|
267
|
+
return this.asSetOf(true).equals(other.asSetOf(true));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Determines whether the given element is allowable in this set (even for derived Sets).
|
|
271
|
+
//
|
|
272
|
+
// An element is allowed if:
|
|
273
|
+
// * it is derived from Element, and
|
|
274
|
+
// * this set allows any element type (ie. does not define #allowedElements), or
|
|
275
|
+
// * the element is of a type in the allowed elements for this set, or
|
|
276
|
+
// * the element can be promoted to a type in the allowed elements for this set.
|
|
277
|
+
//
|
|
278
|
+
// If the element is allowed then a reference to itself, or a promoted version of
|
|
279
|
+
// itself, is returned. If the element is not allowed, undefined is returned.
|
|
280
|
+
isElementAllowed(element) {
|
|
281
|
+
if (!(element && element instanceof Element)) return undefined;
|
|
282
|
+
|
|
283
|
+
const allowedElements = this.allowedElements;
|
|
284
|
+
|
|
285
|
+
// Allow any Element if no allowedElements specified.
|
|
286
|
+
if (!allowedElements) return element;
|
|
287
|
+
|
|
288
|
+
// Check if element is an allowed element type.
|
|
289
|
+
for (const elementClass of allowedElements) {
|
|
290
|
+
if (element instanceof elementClass) {
|
|
291
|
+
return element;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// It's not an allowed element type, but might be promotable to an allowed type.
|
|
296
|
+
let promoted = undefined;
|
|
297
|
+
|
|
298
|
+
for (const elementClass of allowedElements) {
|
|
299
|
+
let proxy = new elementClass();
|
|
300
|
+
promoted = proxy.promote(element);
|
|
301
|
+
|
|
302
|
+
if (promoted !== undefined) {
|
|
303
|
+
return promoted;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Do the hard work of adding an element to this set, if it allows it.
|
|
311
|
+
// If the element already exists in the set and duplicates are not allowed,
|
|
312
|
+
// then the index of the existing element in the set is returned, otherwise
|
|
313
|
+
// -1 is returned. This allows derived classes to take custom action when
|
|
314
|
+
// an existing element is added. For example, Chord overides internalAdd to
|
|
315
|
+
// allow equivalent Notes to be merged.
|
|
316
|
+
//
|
|
317
|
+
// strict is passed through to isEqual, and if true ensures strict equality
|
|
318
|
+
// is applied. This is relevant for Notes, whose equality semantics varies
|
|
319
|
+
// depending on whether they are being stored in a Chord or NoteSet.
|
|
320
|
+
internalAdd(element) {
|
|
321
|
+
if (this.allowDuplicates || this.elements.length < 1) {
|
|
322
|
+
this.elements.push(element);
|
|
323
|
+
} else {
|
|
324
|
+
const found = this.elements.findIndex((e) =>
|
|
325
|
+
e.isEqual(element, this.isStrict)
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (found < 0) {
|
|
329
|
+
this.elements.push(element);
|
|
330
|
+
} else {
|
|
331
|
+
return found;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return -1;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Add an element to the set.
|
|
339
|
+
//
|
|
340
|
+
// The element must be an array of allowable elements,
|
|
341
|
+
// or an allowable Element type for the Set,
|
|
342
|
+
// or a Set, in which case any allowable elements it contains
|
|
343
|
+
// are added to this set.
|
|
344
|
+
add(element) {
|
|
345
|
+
this.#assertMutable();
|
|
346
|
+
|
|
347
|
+
if (Array.isArray(element)) {
|
|
348
|
+
for (let arrayElement of element) {
|
|
349
|
+
this.add(arrayElement);
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
let promoted = this.isElementAllowed(element);
|
|
353
|
+
|
|
354
|
+
if (promoted === undefined) {
|
|
355
|
+
if (element instanceof Set) {
|
|
356
|
+
for (let e of element) {
|
|
357
|
+
e = this.isElementAllowed(e);
|
|
358
|
+
|
|
359
|
+
if (e !== undefined) {
|
|
360
|
+
this.internalAdd(e);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
throw new Error("Attempt to add incorrect element type to set");
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
this.internalAdd(promoted);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Append the elements of the given set to this one. The other set must be equivalent to this one.
|
|
375
|
+
//
|
|
376
|
+
// Note that this is intended to be used with a concrete set, but will work with an abstract set.
|
|
377
|
+
// Just be aware that it will attempt to append every element of the other set, so if it is a large
|
|
378
|
+
// abstract set, this could involve considerable overhead. It is better to form a union with an
|
|
379
|
+
// abstract set.
|
|
380
|
+
append(set) {
|
|
381
|
+
this.#assertMutable();
|
|
382
|
+
|
|
383
|
+
if (!(set && set.constructor === this.constructor))
|
|
384
|
+
throw new Error("Attempt to append incorrect set type to set");
|
|
385
|
+
|
|
386
|
+
for (let element of set) {
|
|
387
|
+
this.internalAdd(element);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Clears all the elements from this Set.
|
|
394
|
+
// Only effective on concrete sets. Ignored on abstract sets.
|
|
395
|
+
clear() {
|
|
396
|
+
this.#assertMutable();
|
|
397
|
+
|
|
398
|
+
this.elements = [];
|
|
399
|
+
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Return a clone of this set.
|
|
404
|
+
//
|
|
405
|
+
// If deep is true and this set is mutable, each element will also be deeply cloned,
|
|
406
|
+
// otherwise the elements contained by this set will be copied by reference.
|
|
407
|
+
clone(deep = false, options = {}) {
|
|
408
|
+
let clone = new this.constructor({
|
|
409
|
+
isAbstract: this.#isAbstract,
|
|
410
|
+
allowDuplicates: this.#allowDuplicates,
|
|
411
|
+
strict: this.#strict,
|
|
412
|
+
allowedElements: this.#allowedElementTypes,
|
|
413
|
+
...options,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
if (!this.isAbstract && this.elements !== undefined) {
|
|
417
|
+
for (let element of this) {
|
|
418
|
+
if (deep && element.clone !== undefined) {
|
|
419
|
+
clone.add(element.clone(deep));
|
|
420
|
+
} else {
|
|
421
|
+
clone.add(element);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return clone;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Delete the specified element from the Set if it exists.
|
|
430
|
+
// Only effective on concrete sets. Ignored on abstract sets.
|
|
431
|
+
|
|
432
|
+
// Throws an Error if this set is immutable.
|
|
433
|
+
// Returns true of the element was found and deleted, false if it
|
|
434
|
+
// wasn't found.
|
|
435
|
+
delete(element) {
|
|
436
|
+
this.#assertMutable();
|
|
437
|
+
|
|
438
|
+
const found = this.elements.findIndex((e) => e.isEqual(element));
|
|
439
|
+
|
|
440
|
+
if (found >= 0) {
|
|
441
|
+
this.elements.splice(found, 1);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Return the element at the given index in the set.
|
|
449
|
+
elementAt(index = 0) {
|
|
450
|
+
if (index >= 0 && index < this.elements.length) return this.elements[index];
|
|
451
|
+
|
|
452
|
+
throw new Error("Invalid index");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Performs the given callbackFn on each element in the Set.
|
|
456
|
+
//
|
|
457
|
+
// The callbackFn is called as:
|
|
458
|
+
// callbackFn(element, element, this) to be consistent with Map and array.
|
|
459
|
+
forEach(callbackFn) {
|
|
460
|
+
for (let element of this) {
|
|
461
|
+
callbackFn(element, element, this);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Returns whether the given Set contains no elements in common with this one.
|
|
466
|
+
//
|
|
467
|
+
// The other Set must be the same type as this one.
|
|
468
|
+
isDisjointFrom(other) {
|
|
469
|
+
if (!this.validateOther(other))
|
|
470
|
+
throw new Error("Set.isDisjointFrom requires equivalent set types");
|
|
471
|
+
|
|
472
|
+
for (const e of this) {
|
|
473
|
+
if (other.has(e)) {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Returns whether this Set is a subset of another one.
|
|
482
|
+
//
|
|
483
|
+
// The other Set must be the same class as this one.
|
|
484
|
+
// Returns false if this set is empty.
|
|
485
|
+
isSubsetOf(other) {
|
|
486
|
+
if (!this.validateOther(other))
|
|
487
|
+
throw new Error("Set.isSubsetOf requires equivalent set types");
|
|
488
|
+
|
|
489
|
+
if (this.size === 0) return false;
|
|
490
|
+
|
|
491
|
+
for (const e of this) {
|
|
492
|
+
if (!other.has(e)) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Returns whether the given element exists in this Set.
|
|
501
|
+
//
|
|
502
|
+
// If element is actually a non-empty set of the same type as this one, then
|
|
503
|
+
// true is returned if it is a subset of this one, false is returned if it is
|
|
504
|
+
// empty or not a subset.
|
|
505
|
+
//
|
|
506
|
+
// Other elements may be promoted prior to searching.
|
|
507
|
+
has(element) {
|
|
508
|
+
if (this.size === 0) return false;
|
|
509
|
+
|
|
510
|
+
if (element.constructor === this.constructor) {
|
|
511
|
+
return element.size > 0 ? element.isSubsetOf(this) : false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const promoted = this.isElementAllowed(element);
|
|
515
|
+
|
|
516
|
+
return promoted !== undefined
|
|
517
|
+
? this.elements.findIndex((e) => e.isEqual(promoted)) >= 0
|
|
518
|
+
: false;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Returns the index of the element in the set, if it exists, -1 otherwise.
|
|
522
|
+
indexOf(element, after = -1) {
|
|
523
|
+
if (element === undefined || !(element instanceof Element)) {
|
|
524
|
+
throw new Error("indexOf requires an element derived from Element");
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const promoted = this.isElementAllowed(element);
|
|
528
|
+
|
|
529
|
+
let index = 0;
|
|
530
|
+
|
|
531
|
+
for (let e of this) {
|
|
532
|
+
if (index > after && e.isEqual(promoted)) return index;
|
|
533
|
+
|
|
534
|
+
++index;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return -1;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Operator creation methods----------------------------------------------
|
|
541
|
+
|
|
542
|
+
static createCross = undefined;
|
|
543
|
+
|
|
544
|
+
// Returns a Set that is the Cartesian product of this Set and the other Set.
|
|
545
|
+
//
|
|
546
|
+
// The other Set must be the same type as this one. The returned Set will also
|
|
547
|
+
cross(other) {
|
|
548
|
+
return Set.createCross(this, other);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Returns a Set that is the difference between this Set and the other Set.
|
|
552
|
+
//
|
|
553
|
+
// The other Set must be the same type as this one. The returned Set will also
|
|
554
|
+
// be the same class as this one.
|
|
555
|
+
difference(other) {
|
|
556
|
+
return new Set.Difference(this, other);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Returns a Set that is the intersection of this Set and the other Set.
|
|
560
|
+
//
|
|
561
|
+
// The other Set must be the same type as this one. The returned Set will also
|
|
562
|
+
// be the same class as this one.
|
|
563
|
+
intersection(other) {
|
|
564
|
+
return new Set.Intersection(this, other);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Returns an abstract set that contains the set of rotations of each element of this set.
|
|
568
|
+
//
|
|
569
|
+
// options may contain:
|
|
570
|
+
// rotateLeft If true rotation is performed to the left, otherwise to the right.
|
|
571
|
+
rotate(options = {}) {
|
|
572
|
+
return new Set.Rotation(this, this.constructor, {
|
|
573
|
+
...options,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Returns a Set that is the symmetric difference between this Set and the other Set.
|
|
578
|
+
// ie. the resultant Set contains all the elements from this Set and the other one that
|
|
579
|
+
// are not in both Sets.
|
|
580
|
+
//
|
|
581
|
+
// The other Set must be the same class as this one. The returned Set will also
|
|
582
|
+
// be the same class as this one.
|
|
583
|
+
symmetricDifference(other) {
|
|
584
|
+
return new Set.SymmetricDifference(this, other);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Returns a new Set of the same class as this one that is the union of this Set and the other one.
|
|
588
|
+
//
|
|
589
|
+
// The other Set must be the same class as this one. The returned Set will also
|
|
590
|
+
// be the same class as this one.
|
|
591
|
+
union(other) {
|
|
592
|
+
return new Set.Union(this, other);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Operator Definitions------------------------------------------------------
|
|
596
|
+
|
|
597
|
+
// Operator: Base class for set operators.
|
|
598
|
+
static Operator = class extends Set {
|
|
599
|
+
constructor(options = {}) {
|
|
600
|
+
options = { ...options, isAbstract: true };
|
|
601
|
+
|
|
602
|
+
super(options);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
get [Symbol.toStringTag]() {
|
|
606
|
+
return "Operator";
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
get smallestSet() {
|
|
610
|
+
return this.elements[0].size <= this.elements[1].size ? 0 : 1;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
inspect() {
|
|
614
|
+
let inspect = "";
|
|
615
|
+
|
|
616
|
+
for (let element of this) {
|
|
617
|
+
inspect += (inspect.length > 0 ? ", " : "") + util.inspect(element);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return inspect;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
[util.inspect.custom](depth, opts) {
|
|
624
|
+
return `${this[Symbol.toStringTag]} {${this.inspect()}}`;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
get size() {
|
|
628
|
+
throw new Error("Derived class does not implement size getter");
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
internalHas(element) {
|
|
632
|
+
throw new Error("Derived class does not implement has()");
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
has(element) {
|
|
636
|
+
if (this.isElementAllowed(element)) {
|
|
637
|
+
// The element is an allowed element as is, check to see if it matches.
|
|
638
|
+
return this.internalHas(element);
|
|
639
|
+
} else if (this.validateOther(element)) {
|
|
640
|
+
// The element is actually an equivalent set to this one and each
|
|
641
|
+
// item in element needs to be checked individually.
|
|
642
|
+
for (let item of element) {
|
|
643
|
+
if (!this.has(item)) return false;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
static UnaryOperator = class extends Set.Operator {
|
|
654
|
+
#setClass = undefined; // Class of elements represented in this rotation set.
|
|
655
|
+
|
|
656
|
+
// set The set to be operatoed on. The given set is promoted to setClass if possible.
|
|
657
|
+
// setClass The class expected for the set to be operated on (or a constructor).
|
|
658
|
+
constructor(set, setClass, options = {}) {
|
|
659
|
+
options = {
|
|
660
|
+
...options,
|
|
661
|
+
allowDuplicates: false,
|
|
662
|
+
allowedElements: [setClass],
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
if (!setClass || typeof setClass !== "function") {
|
|
666
|
+
throw new Error("Invalid argument - set class missing or invalid");
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
let promoted = new setClass();
|
|
670
|
+
set = promoted.promote(set);
|
|
671
|
+
|
|
672
|
+
if (!set || !(set instanceof Set)) {
|
|
673
|
+
throw new Error("Invalid argument - incompatible set class");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
super(options);
|
|
677
|
+
|
|
678
|
+
this.#setClass = setClass;
|
|
679
|
+
this.elements = [set];
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
get setClass() {
|
|
683
|
+
return this.#setClass;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
get set() {
|
|
687
|
+
return this.elements[0];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
[util.inspect.custom](depth, opts) {
|
|
691
|
+
let elements = "";
|
|
692
|
+
|
|
693
|
+
for (const element of this) {
|
|
694
|
+
elements += (elements.length == 0 ? "" : ", ") + util.inspect(element);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return `${this[Symbol.toStringTag]} {[${elements}]}`;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
get size() {
|
|
701
|
+
return this.set.size;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
internalHas(element) {
|
|
705
|
+
if (this.isElementAllowed(element)) {
|
|
706
|
+
// The element is an allowed element as is, check to see if it matches.
|
|
707
|
+
for (let item of this) {
|
|
708
|
+
if (item.isEqual(element)) return true;
|
|
709
|
+
}
|
|
710
|
+
} else if (this.validateOther(element)) {
|
|
711
|
+
// The element is actually an equivalent set to this one and each
|
|
712
|
+
// item in element needs to be checked individually.
|
|
713
|
+
for (let item of element) {
|
|
714
|
+
if (!this.has(item)) return false;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
static BinaryOperator = class extends Set.Operator {
|
|
725
|
+
#skipValidation = false;
|
|
726
|
+
|
|
727
|
+
constructor(set1, set2, options = {}) {
|
|
728
|
+
if (set1 === undefined || set2 === undefined) {
|
|
729
|
+
throw new Error("Operator requires both sets to be defined");
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
options = { skipValidation: false, ...options };
|
|
733
|
+
|
|
734
|
+
// Set operations are only valid on sets of the same type.
|
|
735
|
+
if (!options.skipValidation && !set1.validateOther(set2)) {
|
|
736
|
+
throw new Error("Invalid sets");
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
super(options);
|
|
740
|
+
|
|
741
|
+
this.#skipValidation = options.skipValidation;
|
|
742
|
+
|
|
743
|
+
this.elements = [set1, set2];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
get skipValidation() {
|
|
747
|
+
return this.#skipValidation;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
get allowedElements() {
|
|
751
|
+
// Note: the Operator constructor ensures that both sets allow the exact same element
|
|
752
|
+
// types, so we could use either one to determine which elements an operator allows.
|
|
753
|
+
// One exception is when skipValidation is used in construction. In this case, it would
|
|
754
|
+
// matter, however, the calling class will have to sort that out in those special
|
|
755
|
+
// circumstances.
|
|
756
|
+
return this.elements[0].allowedElements;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
get [Symbol.toStringTag]() {
|
|
760
|
+
return "BinaryOperator";
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
get smallestSet() {
|
|
764
|
+
return this.elements[0].size <= this.elements[1].size ? 0 : 1;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
inspect() {
|
|
768
|
+
let inspect = "";
|
|
769
|
+
|
|
770
|
+
for (let element of this) {
|
|
771
|
+
inspect += (inspect.length > 0 ? ", " : "") + util.inspect(element);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
return inspect;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
[util.inspect.custom](depth, opts) {
|
|
778
|
+
return `${this[Symbol.toStringTag]} {${this.inspect()}}`;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
clone(deep = false) {
|
|
782
|
+
const set1 = this.elements[0];
|
|
783
|
+
const set2 = this.elements[1];
|
|
784
|
+
|
|
785
|
+
const set1Clone = deep
|
|
786
|
+
? set1 !== undefined
|
|
787
|
+
? set1.clone(deep)
|
|
788
|
+
: undefined
|
|
789
|
+
: set1;
|
|
790
|
+
|
|
791
|
+
const set2Clone = deep
|
|
792
|
+
? set2 !== undefined
|
|
793
|
+
? set2.clone(deep)
|
|
794
|
+
: undefined
|
|
795
|
+
: set2;
|
|
796
|
+
|
|
797
|
+
let clone = new this.constructor(set1Clone, set2Clone, {
|
|
798
|
+
isAbstract: this.#isAbstract,
|
|
799
|
+
allowDuplicates: this.#allowDuplicates,
|
|
800
|
+
strict: this.#strict,
|
|
801
|
+
skipValidation: this.#skipValidation,
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
return clone;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
elementAt(index = 0) {
|
|
808
|
+
if (index >= 0 && index < this.size) {
|
|
809
|
+
for (let element of this) {
|
|
810
|
+
if (index-- == 0) {
|
|
811
|
+
return element;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
throw new Error("Invalid index");
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Difference: All elements in this set not in the other.
|
|
821
|
+
static Difference = class extends Set.BinaryOperator {
|
|
822
|
+
#calculatedSize = undefined;
|
|
823
|
+
|
|
824
|
+
constructor(set1, set2, options = {}) {
|
|
825
|
+
super(set1, set2, options);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
get [Symbol.toStringTag]() {
|
|
829
|
+
return "Difference";
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
*[Symbol.iterator]() {
|
|
833
|
+
for (let element of this.elements[0]) {
|
|
834
|
+
if (!this.elements[1].has(element)) {
|
|
835
|
+
yield element;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
#calculateSize() {
|
|
841
|
+
let size = 0;
|
|
842
|
+
|
|
843
|
+
for (let element of this.elements[0]) {
|
|
844
|
+
if (!this.elements[1].has(element)) {
|
|
845
|
+
++size;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
return size;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
get size() {
|
|
853
|
+
if (this.#calculatedSize === undefined) {
|
|
854
|
+
this.#calculatedSize = this.#calculateSize();
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
return this.#calculatedSize;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
internalHas(element) {
|
|
861
|
+
return this.elements[0].has(element) && !this.elements[1].has(element);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
// Intersection: All elements in this set and the other.
|
|
866
|
+
static Intersection = class extends Set.BinaryOperator {
|
|
867
|
+
#calculatedSize = undefined;
|
|
868
|
+
|
|
869
|
+
constructor(set1, set2, options = {}) {
|
|
870
|
+
super(set1, set2, options);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
get [Symbol.toStringTag]() {
|
|
874
|
+
return "Intersection";
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
*[Symbol.iterator]() {
|
|
878
|
+
const smallestSet = this.smallestSet;
|
|
879
|
+
const largestSet = 1 - smallestSet;
|
|
880
|
+
|
|
881
|
+
for (let element of this.elements[smallestSet]) {
|
|
882
|
+
if (this.elements[largestSet].has(element)) {
|
|
883
|
+
yield element;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
#calculateSize() {
|
|
889
|
+
let size = 0;
|
|
890
|
+
|
|
891
|
+
const smallestSet = this.smallestSet;
|
|
892
|
+
const largestSet = 1 - smallestSet;
|
|
893
|
+
|
|
894
|
+
for (let element of this.elements[smallestSet]) {
|
|
895
|
+
if (this.elements[largestSet].has(element)) {
|
|
896
|
+
++size;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return size;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
get size() {
|
|
904
|
+
if (this.#calculatedSize === undefined) {
|
|
905
|
+
this.#calculatedSize = this.#calculateSize();
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return this.#calculatedSize;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
has(element) {
|
|
912
|
+
const smallestSet = this.smallestSet;
|
|
913
|
+
const largestSet = 1 - smallestSet;
|
|
914
|
+
|
|
915
|
+
return (
|
|
916
|
+
this.elements[smallestSet].has(element) &&
|
|
917
|
+
this.elements[largestSet].has(element)
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// Rotation: Returns an abstract set that contains the set of rotations of each
|
|
923
|
+
// element of this set.
|
|
924
|
+
static Rotation = class extends Set.UnaryOperator {
|
|
925
|
+
#rotateLeft = true; // Rotate right if false.
|
|
926
|
+
|
|
927
|
+
// Rotation constructor.
|
|
928
|
+
//
|
|
929
|
+
// Generates an abstract set containing all the sets of rotations
|
|
930
|
+
// for a given set.
|
|
931
|
+
//
|
|
932
|
+
// set The set to be rotated. The given set is promoted to setClass if possible.
|
|
933
|
+
// setClass The class expected for the set to be rotated (or a constructor).
|
|
934
|
+
// options Options that affect this instance. May include those defined by Set, and
|
|
935
|
+
// rotateLeft If true the rotation is performed left-wise, otherwise
|
|
936
|
+
// it is performed right-wise.
|
|
937
|
+
constructor(set, setClass, options = {}) {
|
|
938
|
+
const defaults = { rotateLeft: true };
|
|
939
|
+
|
|
940
|
+
options = {
|
|
941
|
+
...defaults,
|
|
942
|
+
...options,
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
super(set, setClass, options);
|
|
946
|
+
|
|
947
|
+
this.#rotateLeft = options.rotateLeft;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
get rotateLeft() {
|
|
951
|
+
return this.#rotateLeft;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
get [Symbol.toStringTag]() {
|
|
955
|
+
return "Rotation";
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
*[Symbol.iterator]() {
|
|
959
|
+
const setSize = this.set.size;
|
|
960
|
+
|
|
961
|
+
let sets = setSize;
|
|
962
|
+
let elements = setSize;
|
|
963
|
+
let set = new this.set.constructor();
|
|
964
|
+
let skip = false;
|
|
965
|
+
|
|
966
|
+
while (sets > 0) {
|
|
967
|
+
for (let element of this.set) {
|
|
968
|
+
do {
|
|
969
|
+
if (this.rotateLeft && skip) {
|
|
970
|
+
skip = false;
|
|
971
|
+
} else {
|
|
972
|
+
skip = false;
|
|
973
|
+
|
|
974
|
+
set.add(element);
|
|
975
|
+
--elements;
|
|
976
|
+
|
|
977
|
+
if (elements === 0) {
|
|
978
|
+
yield set;
|
|
979
|
+
|
|
980
|
+
if (--sets > 0) {
|
|
981
|
+
set = new this.set.constructor();
|
|
982
|
+
elements = setSize;
|
|
983
|
+
skip = true;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
} while (skip && !this.rotateLeft);
|
|
988
|
+
|
|
989
|
+
if (sets === 0) break;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
clone(deep = false, options = {}) {
|
|
995
|
+
return new this.constructor(
|
|
996
|
+
deep ? this.set.clone(deep) : this.set, this.setClass, {
|
|
997
|
+
isAbstract: this.#isAbstract,
|
|
998
|
+
allowDuplicates: this.#allowDuplicates,
|
|
999
|
+
strict: this.#strict,
|
|
1000
|
+
allowedElements: this.#allowedElementTypes,
|
|
1001
|
+
...options,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
elementAt(index = 0) {
|
|
1006
|
+
let size = this.size;
|
|
1007
|
+
|
|
1008
|
+
if (index >= 0 && index < size) {
|
|
1009
|
+
const delta = this.rotateLeft ? 1 : size - 1;
|
|
1010
|
+
let at = this.rotateLeft ? index : (size - index) % size;
|
|
1011
|
+
let set = new this.set.constructor();
|
|
1012
|
+
let setSize = 0;
|
|
1013
|
+
|
|
1014
|
+
while (setSize < size) {
|
|
1015
|
+
set.add(this.set.elementAt(at));
|
|
1016
|
+
++setSize;
|
|
1017
|
+
|
|
1018
|
+
at = ++at % size;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return set;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
throw new Error("Invalid index");
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
// SymmetricDifference: All elements not shared by this set or the other.
|
|
1029
|
+
static SymmetricDifference = class extends Set.BinaryOperator {
|
|
1030
|
+
#calculatedSize = undefined;
|
|
1031
|
+
|
|
1032
|
+
constructor(set1, set2, options = {}) {
|
|
1033
|
+
super(set1, set2, options);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
get [Symbol.toStringTag]() {
|
|
1037
|
+
return "SymmetricDifference";
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
*[Symbol.iterator]() {
|
|
1041
|
+
for (let element of this.elements[0]) {
|
|
1042
|
+
if (!this.elements[1].has(element)) {
|
|
1043
|
+
yield element;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
for (let element of this.elements[1]) {
|
|
1048
|
+
if (!this.elements[0].has(element)) {
|
|
1049
|
+
yield element;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
#calculateSize() {
|
|
1055
|
+
let size = 0;
|
|
1056
|
+
|
|
1057
|
+
for (let element of this.elements[0]) {
|
|
1058
|
+
if (!this.elements[1].has(element)) {
|
|
1059
|
+
++size;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
for (let element of this.elements[1]) {
|
|
1064
|
+
if (!this.elements[0].has(element)) {
|
|
1065
|
+
++size;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return size;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
get size() {
|
|
1073
|
+
if (this.#calculatedSize === undefined) {
|
|
1074
|
+
this.#calculatedSize = this.#calculateSize();
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
return this.#calculatedSize;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
internalHas(element) {
|
|
1081
|
+
const thisHasIt = this.elements[0].has(element);
|
|
1082
|
+
const otherHasIt = this.elements[1].has(element);
|
|
1083
|
+
|
|
1084
|
+
return thisHasIt !== otherHasIt;
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// Union: All elements from this set and the other.
|
|
1089
|
+
static Union = class extends Set.BinaryOperator {
|
|
1090
|
+
constructor(set1, set2, options = {}) {
|
|
1091
|
+
super(set1, set2, options);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
get [Symbol.toStringTag]() {
|
|
1095
|
+
return "Union";
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
*[Symbol.iterator]() {
|
|
1099
|
+
yield* this.elements[0];
|
|
1100
|
+
|
|
1101
|
+
if (this.allowDuplicates) {
|
|
1102
|
+
yield* this.elements[1];
|
|
1103
|
+
} else {
|
|
1104
|
+
for (let element of this.elements[1]) {
|
|
1105
|
+
if (!this.elements[0].has(element)) {
|
|
1106
|
+
yield element;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
get size() {
|
|
1113
|
+
const smallestSet = this.smallestSet;
|
|
1114
|
+
const largestSet = 1 - smallestSet;
|
|
1115
|
+
|
|
1116
|
+
let size = this.elements[largestSet].size;
|
|
1117
|
+
|
|
1118
|
+
if (this.allowDuplicates) {
|
|
1119
|
+
size += this.elements[smallestSet].size;
|
|
1120
|
+
} else {
|
|
1121
|
+
for (let element of this.elements[smallestSet]) {
|
|
1122
|
+
if (!this.elements[largestSet].has(element)) ++size;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return size;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
elementAt(index = 0) {
|
|
1130
|
+
if (index >= 0) {
|
|
1131
|
+
let size = this.elements[0].size;
|
|
1132
|
+
|
|
1133
|
+
if (index < size) {
|
|
1134
|
+
return this.elements[0].elementAt(index);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
index -= size;
|
|
1138
|
+
size = this.elements[1].size;
|
|
1139
|
+
|
|
1140
|
+
if (index < size) {
|
|
1141
|
+
for (let element of this.elements[1]) {
|
|
1142
|
+
if (!this.elements[0].has(element)) {
|
|
1143
|
+
if (index-- == 0) {
|
|
1144
|
+
return element;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
throw new Error("Invalid index");
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
has(element) {
|
|
1155
|
+
return this.elements[0].has(element) || this.elements[1].has(element);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
// End Operator Definitions----------------------------------------------------
|
|
1159
|
+
}
|