forked from davidflanagan/jstdg7
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSets.js
203 lines (179 loc) · 6.8 KB
/
Sets.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* The AbstractSet class defines a single abstract method, has().
*/
class AbstractSet {
// Throw an error here so that subclasses are forced
// to define their own working version of this method.
has(x) { throw new Error("Abstract method"); }
}
/**
* NotSet is a concrete subclass of AbstractSet.
* The members of this set are all values that are not members of some
* other set. Because it is defined in terms of another set it is not
* writable, and because it has infinite members, it is not enumerable.
* All we can do with it is test for membership and convert it to a
* string using mathematical notation.
*/
class NotSet extends AbstractSet {
constructor(set) {
super();
this.set = set;
}
// Our implementation of the abstract method we inherited
has(x) { return !this.set.has(x); }
// And we also override this Object method
toString() { return `{ x| x ∉ ${this.set.toString()} }`; }
}
/**
* Range set is a concrete subclass of AbstractSet. Its members are
* all values that are between the from and to bounds, inclusive.
* Since its members can be floating point numbers, it is not
* enumerable and does not have a meaningful size.
*/
class RangeSet extends AbstractSet {
constructor(from, to) {
super();
this.from = from;
this.to = to;
}
has(x) { return x >= this.from && x <= this.to; }
toString() { return `{ x| ${this.from} ≤ x ≤ ${this.to} }`; }
}
/*
* AbstractEnumerableSet is an abstract subclass of AbstractSet. It defines
* an abstract getter that returns the size of the set and also defines an
* abstract iterator. And it then implements concrete isEmpty(), toString(),
* and equals() methods on top of those. Subclasses that implement the
* iterator, the size getter, and the has() method get these concrete
* methods for free.
*/
class AbstractEnumerableSet extends AbstractSet {
get size() { throw new Error("Abstract method"); }
[Symbol.iterator]() { throw new Error("Abstract method"); }
isEmpty() { return this.size === 0; }
toString() { return `{${Array.from(this).join(", ")}}`; }
equals(set) {
// If the other set is not also Enumerable, it isn't equal to this one
if (!(set instanceof AbstractEnumerableSet)) return false;
// If they don't have the same size, they're not equal
if (this.size !== set.size) return false;
// Loop through the elements of this set
for(let element of this) {
// If an element isn't in the other set, they aren't equal
if (!set.has(element)) return false;
}
// The elements matched, so the sets are equal
return true;
}
}
/*
* SingletonSet is a concrete subclass of AbstractEnumerableSet.
* A singleton set is a read-only set with a single member.
*/
class SingletonSet extends AbstractEnumerableSet {
constructor(member) {
super();
this.member = member;
}
// We implement these three methods, and inherit isEmpty, equals()
// and toString() implementations based on these methods.
has(x) { return x === this.member; }
get size() { return 1; }
*[Symbol.iterator]() { yield this.member; }
}
/*
* AbstractWritableSet is an abstract subclass of AbstractEnumerableSet.
* It defines the abstract methods insert() and remove() that insert and
* remove individual elements from the set, and then implements concrete
* add(), subtract(), and intersect() methods on top of those. Note that
* our API diverges here from the standard JavaScript Set class.
*/
class AbstractWritableSet extends AbstractEnumerableSet {
insert(x) { throw new Error("Abstract method"); }
remove(x) { throw new Error("Abstract method"); }
add(set) {
for(let element of set) {
this.insert(element);
}
}
subtract(set) {
for(let element of set) {
this.remove(element);
}
}
intersect(set) {
for(let element of this) {
if (!set.has(element)) {
this.remove(element);
}
}
}
}
/**
* A BitSet is a concrete subclass of AbstractWritableSet with a
* very efficient fixed-size set implementation for sets whose
* elements are non-negative integers less than some maximum size.
*/
class BitSet extends AbstractWritableSet {
constructor(max) {
super();
this.max = max; // The maximum integer we can store.
this.n = 0; // How many integers are in the set
this.numBytes = Math.floor(max / 8) + 1; // How many bytes we need
this.data = new Uint8Array(this.numBytes); // The bytes
}
// Internal method to check if a value is a legal member of this set
_valid(x) { return Number.isInteger(x) && x >= 0 && x <= this.max; }
// Tests whether the specified bit of the specified byte of our
// data array is set or not. Returns true or false.
_has(byte, bit) { return (this.data[byte] & BitSet.bits[bit]) !== 0; }
// Is the value x in this BitSet?
has(x) {
if (this._valid(x)) {
let byte = Math.floor(x / 8);
let bit = x % 8;
return this._has(byte, bit);
} else {
return false;
}
}
// Insert the value x into the BitSet
insert(x) {
if (this._valid(x)) { // If the value is valid
let byte = Math.floor(x / 8); // convert to byte and bit
let bit = x % 8;
if (!this._has(byte, bit)) { // If that bit is not set yet
this.data[byte] |= BitSet.bits[bit]; // then set it
this.n++; // and increment set size
}
} else {
throw new TypeError("Invalid set element: " + x );
}
}
remove(x) {
if (this._valid(x)) { // If the value is valid
let byte = Math.floor(x / 8); // compute the byte and bit
let bit = x % 8;
if (this._has(byte, bit)) { // If that bit is already set
this.data[byte] &= BitSet.masks[bit]; // then unset it
this.n--; // and decrement size
}
} else {
throw new TypeError("Invalid set element: " + x );
}
}
// A getter to return the size of the set
get size() { return this.n; }
// Iterate the set by just checking each bit in turn.
// (We could be a lot more clever and optimize this substantially)
*[Symbol.iterator]() {
for(let i = 0; i <= this.max; i++) {
if (this.has(i)) {
yield i;
}
}
}
}
// Some pre-computed values used by the has(), insert() and remove() methods
BitSet.bits = new Uint8Array([1, 2, 4, 8, 16, 32, 64, 128]);
BitSet.masks = new Uint8Array([~1, ~2, ~4, ~8, ~16, ~32, ~64, ~128]);