├── .gitignore
├── .npmignore
├── LICENSE.md
├── README.md
├── package-lock.json
├── package.json
├── rollup.config.js
├── sorted-set.js
├── sorted-set.js.map
├── sorted-set.min.js
├── sorted-set.min.js.map
├── src
├── SortedSet.js
└── SortedSet
│ ├── AbstractBinaryTreeStrategy.js
│ ├── AbstractSortedSet.js
│ ├── ArrayStrategy.js
│ ├── BinaryTreeIterator.js
│ ├── BinaryTreeStrategy.js
│ ├── InsertConflictResolvers.js
│ └── RedBlackTreeStrategy.js
└── test
├── SortedSet
├── AbstractSortedSetSpec.js
├── ArrayStrategySpec.js
├── BinaryTreeStrategySpec.js
└── RedBlackTreeStrategySpec.js
├── SortedSetSpec.js
├── helpers
└── StrategyHelper.js
└── test_helper.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /lib/
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | sorted-set.*.js
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | License
2 | =======
3 |
4 | I, Adam Hooper, the sole author of this project, waive all my rights to it and
5 | release it under the [Public
6 | Domain](http://creativecommons.org/publicdomain/zero/1.0/). Do with it what you
7 | will.
8 |
9 | My hope is that a JavaScript implementation of red-black trees somehow makes the
10 | world a better place.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Sorted Set
2 | ==========
3 |
4 | A sorted set is a data structure with these guarantees:
5 |
6 | * It's a set: it can only contain any given item once.
7 | * It's sorted: you can iterate over all its items in order.
8 |
9 | As an illustration, let's build a simple sorted set out of an `Array`:
10 |
11 | | Operation | Syntax (simple JavaScript Array) |
12 | | --------- | -------------------------------- |
13 | | Create | `const set = []` |
14 | | Insert | `set.push(value)` |
15 | | Remove | `set.splice(set.indexOf(value), 1)` |
16 | | Iterate | `set.sort(); set.forEach(doSomething)` |
17 | | Find | `set.sort(); const index = set.indexOf(value)` |
18 | | Previous | `const previousIndex = index - 1` |
19 | | Next | `const nextIndex = index + 1` |
20 | | Test | `const isInSet = set.indexOf(value) != -1` |
21 |
22 | ... this works, but it's a bit cryptic and some operations--notably iterate--
23 | will be very slow with large sets.
24 |
25 | Usage
26 | =====
27 |
28 | You can `npm install js-sorted-set`. Alternatively, just download
29 | `sorted-set.js` from this directory.
30 |
31 | To use it on a Website built with Webpack or Rollup:
32 |
33 | import SortedSet from 'js-sorted-set'
34 |
35 | To use it in Node:
36 |
37 | const SortedSet = require('js-sorted-set')
38 |
39 | Or, to pollute your global scope, insert this in your HTML:
40 |
41 |
42 |
43 | Now that you have the `SortedSet` class, here's how to use it:
44 |
45 | const set = new SortedSet({ comparator: function(a, b) { return b - a }})
46 | set.insert(5)
47 | set.insert(3)
48 | set.insert(2)
49 | set.remove(3)
50 | const yes = set.contains(2)
51 | console.log(set.map(function(x) { return x * 2 })) // returns [ 10, 4 ]
52 |
53 | Operations
54 | ==========
55 |
56 | The SortedSet API:
57 |
58 | | Operation | Syntax (js-sorted-set) | Notes |
59 | | --------- | ----------------------- | ----- |
60 | | Create | `const set = new SortedSet()` |
61 | | Insert | `set.insert(value)` |
62 | | Remove | `set.remove(value)` |
63 | | Clear | `set.clear()` |
64 | | Length | `set.length` |
65 | | Test | `set.contains(value)` | Returns `true` or `false` |
66 | | Iterate | `set.forEach(doSomething)` | Plus `set.map()` and other [iterative methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.6#Array_extras), returning `Array`s and scalars |
67 |
68 | Find, Previous and Next work with an Iterator pattern. An iterator is an
69 | immutible pointer into the space "between" two items in the set.
70 |
71 | const iterator = set.beginIterator() // points to the left of the leftmost item
72 | const iterator2 = iterator.next() // points to the left of the second item
73 | const value = iterator.value(), value2 = iterator2.value()
74 | const end = set.endIterator() // points to the right of the final item
75 | const value2 = end.value() // null, because there is no item
76 |
77 | Here's the full SortedSet iterator API:
78 |
79 | | Operation | Syntax (js-sorted-set) | Notes |
80 | | --------- | ---------------------- | ----- |
81 | | Length | `const len = set.length` |
82 | | Find | `const iterator = set.findIterator(value)` | `iterator` points to the left of `value`. If `value` is not in `set`, `iterator` points to the left of the first item _greater than_ `value`. If `value` is greater than the final item in `set`, `iterator` points to the right of the final item. |
83 | | Begin | `const iterator = set.beginIterator()` | If `set` is empty, this is equivalent to `const iterator = set.endIterator()` |
84 | | End | `const iterator = set.endIterator()` | Points past the end of `set` there is never a value here |
85 | | Value | `const value = iterator.value()` | For an end iterator, returns `null` |
86 | | Forward | `const iterator2 = iterator.next()` | If `iterator` is an end iterator, returns `null` |
87 | | Backward | `const iterator2 = iterator.previous()` | If `iterator` is a begin iterator, returns `null` |
88 | | Can go forward | `const isBegin = !iterator.hasPrevious()` | |
89 | | Can go backward | `const isEnd = !iterator.hasNext()` | Remember, if `iterator` is pointing to the left of the final item in `set`, then `hasNext()` will return `true` -- even though `iterator.next().value() === null` |
90 |
91 | All iterators on `set` become invalid as soon as something calls `set.insert()`
92 | or `set.remove()`.
93 |
94 | Options
95 | =======
96 |
97 | How exactly will these elements be ordered? Let's add a `comparator` option.
98 | This is the argument we would pass to
99 | [Array.prototype.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort):
100 |
101 | const compareNumbers = (a, b) => a - b
102 | const set = new SortedSet({ comparator: compareNumbers })
103 |
104 | How to handle insert conflicts? We'll also add a `onInsertConflict` option that
105 | provides users with a way to specify what to do in case an item is inserted
106 | that matches another item already present within the set. Such behavior
107 | **must** be specified as a function taking in the conflicting items and
108 | returning a replacement item for the previously inserted one. The `SortedSet`
109 | class ships with three implementations such behavior:
110 |
111 | SortedSet.OnInsertConflictThrow // throws an error
112 | SortedSet.OnInsertConflictReplace // keeps the new item
113 | SortedSet.OnInsertConflictIgnore // keeps the previous item
114 |
115 | Unless differently specified through the `onInsertConflict` option, the
116 | `SortedSet` class will default to `SortedSet.OnInsertConflictThrow`:
117 |
118 | const set = new SortedSet({
119 | onInsertConflict: SortedSet.OnInsertConflictThrow
120 | })
121 | set.insert("foo")
122 | set.insert("foo") // throw an error
123 |
124 | Finally, some algorithms ask for really fast replacement mechanisms. So let's
125 | add a `setValue()` method to the iterator, which puts the onus on the user to
126 | keep things ordered.
127 |
128 | Because this is a particularly dangerous API to use, you must set the option
129 | `allowSetValue: true` when creating the SortedSet.
130 |
131 | const set = new SortedSet({ allowSetValue: true })
132 | set.insert("foo")
133 | set.insert("bar")
134 | set.insert("baz")
135 |
136 | // Shortcut API
137 | const iterator = set.findIterator("bar")
138 | iterator.setValue("baq") // It must stay ordered! Do not set "bbq" here!
139 | // The shortcut executes very quickly, but if the user makes a mistake,
140 | // future operations will likely fail
141 |
142 | // iterator.setValue("baq") here is equivalent to:
143 | // set.remove("bar")
144 | // set.insert("baq")
145 |
146 | Strategies
147 | ==========
148 |
149 | We can be somewhat efficient in an `Array` approach by avoiding `sort()` calls.
150 | This strategy keeps the array ordered at all times by inserting and removing
151 | elements into and out from the correct array indices. The downside: large swaths
152 | of the array must be rewritten during each insert and remove.
153 |
154 | We can also create a simple binary tree. `insert()` and `remove()` won't
155 | overwrite the entire array each time, so this can be faster. But it's far
156 | slower to seek through a binary tree, because it can spread out very far
157 | across memory so the processor won't cache it well. Also, depending on the
158 | order in which elements were input, inserting a single item into the tree can
159 | actually be slower than rewriting an entire `Array`.
160 |
161 | Finally, we can improve upon the binary tree by balancing it. This guarantees
162 | a certain maximum number of reads and writes per operation. Think of it this
163 | way: if you're lucky, a simple binary tree's operations can be extremely fast;
164 | if you're unlucky, they can be extremely slow; you'll usually be unlucky. A
165 | balanced tree makes all operations _somewhat_ fast.
166 |
167 | The balanced tree (which, incidentally, is a [Left-Leaning Red-Black
168 | tree](http://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree)) is the
169 | default, because its speed is the most predictable.
170 |
171 | Create the sets like this:
172 |
173 | const set = new SortedSet({ strategy: SortedSet.ArrayStrategy }) // Array
174 | const set = new SortedSet({ strategy: SortedSet.BinaryTreeStrategy }) // simple binary tree
175 | const set = new SortedSet({ strategy: SortedSet.RedBlackTreeStrategy }) // default
176 |
177 | Use the `ArrayStrategy` if your set will only have a few values at a time. Use
178 | the `BinaryTreeStrategy` if you've run lots of tests and can prove it's faster
179 | than the others. If neither of these conditions applies, use the default,
180 | `RedBlackTreeStrategy`.
181 |
182 | You'll see running times like this:
183 |
184 | | Operation | Array | Binary tree | Red-black tree |
185 | | --------- | ----- | ----------- | -------------- |
186 | | Create | O(1) | O(1) | O(1) |
187 | | Length | O(1) | O(1) | O(1) |
188 | | Clear | O(1) | O(n) (in garbage collector) | O(n) (in garbage collector) |
189 | | Insert | O(n) (often slow) | O(n) (often slow) | O(lg n) (fast) |
190 | | Remove | O(n) (often slow) | O(n) (often slow) | O(lg n) (fast) |
191 | | Iterate | O(n) (fast) | O(n) (slowest) | O(n) (slower than Array) |
192 | | Find, Test | O(lg n) (fastest) | O(n) (slowest) | O(lg n) (slower than Array) |
193 |
194 | According to some simple [jsPerf
195 | tests](http://jsperf.com/js-sorted-set-insert-remove), you should use
196 | `ArrayStrategy` if you plan on maintaining about 100 to 1,000 items in your set.
197 | At that size, `ArrayStrategy`'s `insert()` and `remove()` are fastest in today's
198 | browsers; and `ArrayStrategy`'s iteration is faster at all sizes.
199 |
200 | Contributing
201 | ============
202 |
203 | 1. Fork this repository
204 | 2. Run `npm install`
205 | 3. Write the behavior you expect in `test/`
206 | 4. Edit files in `src/` until `npm test` says you're done
207 | 5. Run `npm run build` to update build products
208 | 6. Submit a pull request
209 |
210 | License
211 | =======
212 |
213 | I, Adam Hooper, the sole author of this project, waive all my rights to it and
214 | release it under the [Public
215 | Domain](http://creativecommons.org/publicdomain/zero/1.0/). Do with it what you
216 | will.
217 |
218 | My hope is that a JavaScript implementation of red-black trees somehow makes the
219 | world a better place.
220 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-sorted-set",
3 | "version": "0.7.0",
4 | "description": "Sorted set data structures",
5 | "main": "./lib/SortedSet.js",
6 | "browser": "./sorted-set.js",
7 | "scripts": {
8 | "test": "npm run build-cjs && mocha",
9 | "build-cjs": "babel ./src -d ./lib",
10 | "build-umd": "rollup --config",
11 | "build": "npm run build-cjs && npm run build-umd",
12 | "publish": "npm run build-cjs && mocha && npm publish"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/adamhooper/js-sorted-set"
17 | },
18 | "keywords": [
19 | "sort",
20 | "sorted",
21 | "set",
22 | "sets",
23 | "left-leaning",
24 | "red-black",
25 | "binary",
26 | "tree",
27 | "iterate",
28 | "iterator",
29 | "comparator"
30 | ],
31 | "author": "Adam Hooper ",
32 | "license": "Public Domain",
33 | "readmeFilename": "README.md",
34 | "devDependencies": {
35 | "@babel/cli": "^7.8.4",
36 | "@babel/core": "^7.9.0",
37 | "@babel/preset-env": "^7.9.5",
38 | "babel-plugin-add-module-exports": "^1.0.2",
39 | "chai": "^4.2.0",
40 | "mocha": "^7.1.1",
41 | "rollup": "^2.3.4",
42 | "rollup-plugin-babel": "^4.4.0",
43 | "rollup-plugin-terser": "^5.3.0",
44 | "sinon": "^9.0.1",
45 | "sinon-chai": "^3.5.0"
46 | },
47 | "dependencies": {},
48 | "mocha": {
49 | "reporter": "spec",
50 | "recursive": true
51 | },
52 | "babel": {
53 | "presets": [
54 | [
55 | "@babel/preset-env",
56 | {
57 | "modules": "cjs",
58 | "targets": {
59 | "node": true
60 | }
61 | }
62 | ]
63 | ],
64 | "plugins": [
65 | "add-module-exports"
66 | ]
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { terser } from 'rollup-plugin-terser';
2 | import babel from 'rollup-plugin-babel';
3 |
4 | export default {
5 | input: 'src/SortedSet.js',
6 | output: [
7 | {
8 | name: 'sorted-set',
9 | file: 'sorted-set.js',
10 | format: 'umd',
11 | sourcemap: true,
12 | },
13 | {
14 | name: 'sorted-set',
15 | file: 'sorted-set.min.js',
16 | format: 'umd',
17 | sourcemap: true,
18 | plugins: [terser()],
19 | },
20 | ],
21 | plugins: [
22 | babel({
23 | exclude: 'node_modules/**',
24 | presets: [
25 | ["@babel/env", {"modules": false, "targets": ">1%, not dead, not IE 11"}]
26 | ],
27 | })
28 | ]
29 | };
30 |
--------------------------------------------------------------------------------
/sorted-set.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global = global || self, global['sorted-set'] = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | class AbstractSortedSet {
8 | constructor(options) {
9 | if ((options != null ? options.strategy : void 0) == null) {
10 | throw 'Must pass options.strategy, a strategy';
11 | }
12 |
13 | if ((options != null ? options.comparator : void 0) == null) {
14 | throw 'Must pass options.comparator, a comparator';
15 | }
16 |
17 | if ((options != null ? options.onInsertConflict : void 0) == null) {
18 | throw 'Must pass options.onInsertConflict, a function';
19 | }
20 |
21 | this.priv = new options.strategy(options);
22 | this.length = 0;
23 | }
24 |
25 | insert(value) {
26 | this.priv.insert(value);
27 | this.length += 1;
28 | return this;
29 | }
30 |
31 | remove(value) {
32 | this.priv.remove(value);
33 | this.length -= 1;
34 | return this;
35 | }
36 |
37 | clear() {
38 | this.priv.clear();
39 | this.length = 0;
40 | return this;
41 | }
42 |
43 | contains(value) {
44 | return this.priv.contains(value);
45 | } // Returns this set as an Array
46 |
47 |
48 | toArray() {
49 | return this.priv.toArray();
50 | }
51 |
52 | forEach(callback, thisArg) {
53 | this.priv.forEachImpl(callback, this, thisArg);
54 | return this;
55 | }
56 |
57 | map(callback, thisArg) {
58 | const ret = [];
59 | this.forEach(function (value, index, self) {
60 | return ret.push(callback.call(thisArg, value, index, self));
61 | });
62 | return ret;
63 | }
64 |
65 | filter(callback, thisArg) {
66 | const ret = [];
67 | this.forEach(function (value, index, self) {
68 | if (callback.call(thisArg, value, index, self)) {
69 | return ret.push(value);
70 | }
71 | });
72 | return ret;
73 | }
74 |
75 | every(callback, thisArg) {
76 | let ret = true;
77 | this.forEach(function (value, index, self) {
78 | if (ret && !callback.call(thisArg, value, index, self)) {
79 | ret = false;
80 | }
81 | });
82 | return ret;
83 | }
84 |
85 | some(callback, thisArg) {
86 | let ret = false;
87 | this.forEach(function (value, index, self) {
88 | if (!ret && callback.call(thisArg, value, index, self)) {
89 | ret = true;
90 | }
91 | });
92 | return ret;
93 | } // An iterator is similar to a C++ iterator: it points _before_ a value.
94 | // So in this sorted set:
95 | // | 1 | 2 | 3 | 4 | 5 |
96 | // ^a ^b ^c
97 | // `a` is a pointer to the beginning of the iterator. `a.value()` returns
98 | // `3`. `a.previous()` returns `null`. `a.setValue()` works, if
99 | // `options.allowSetValue` is true.
100 | // `b` is a pointer to the value `3`. `a.previous()` and `a.next()` both do
101 | // the obvious.
102 | // `c` is a pointer to the `null` value. `c.previous()` works; `c.next()`
103 | // returns null. `c.setValue()` throws an exception, even if
104 | // `options.allowSetValue` is true.
105 | // Iterators have `hasNext()` and `hasPrevious()` methods, too.
106 | // Iterators are immutible. `iterator.next()` returns a new iterator.
107 | // Iterators become invalid as soon as `insert()` or `remove()` is called.
108 |
109 |
110 | findIterator(value) {
111 | return this.priv.findIterator(value);
112 | } // Finds an iterator pointing to the lowest possible value.
113 |
114 |
115 | beginIterator() {
116 | return this.priv.beginIterator();
117 | } // Finds an iterator pointing to the `null` value.
118 |
119 |
120 | endIterator() {
121 | return this.priv.endIterator();
122 | }
123 |
124 | }
125 |
126 | class Iterator {
127 | constructor(priv, index1) {
128 | this.priv = priv;
129 | this.index = index1;
130 | this.data = this.priv.data;
131 | }
132 |
133 | hasNext() {
134 | return this.index < this.data.length;
135 | }
136 |
137 | hasPrevious() {
138 | return this.index > 0;
139 | }
140 |
141 | value() {
142 | if (this.index < this.data.length) {
143 | return this.data[this.index];
144 | } else {
145 | return null;
146 | }
147 | }
148 |
149 | setValue(value) {
150 | if (!this.priv.options.allowSetValue) {
151 | throw 'Must set options.allowSetValue';
152 | }
153 |
154 | if (!this.hasNext()) {
155 | throw 'Cannot set value at end of set';
156 | }
157 |
158 | return this.data[this.index] = value;
159 | }
160 |
161 | next() {
162 | if (this.index >= this.data.length) {
163 | return null;
164 | } else {
165 | return new Iterator(this.priv, this.index + 1);
166 | }
167 | }
168 |
169 | previous() {
170 | if (this.index <= 0) {
171 | return null;
172 | } else {
173 | return new Iterator(this.priv, this.index - 1);
174 | }
175 | }
176 |
177 | }
178 |
179 | const binarySearchForIndex = (array, value, comparator) => {
180 | let low = 0;
181 | let high = array.length;
182 |
183 | while (low < high) {
184 | const mid = low + high >>> 1;
185 |
186 | if (comparator(array[mid], value) < 0) {
187 | low = mid + 1;
188 | } else {
189 | high = mid;
190 | }
191 | }
192 |
193 | return low;
194 | };
195 |
196 | class ArrayStrategy {
197 | constructor(options) {
198 | this.options = options;
199 | this.onInsertConflict = this.options.onInsertConflict;
200 | this.comparator = this.options.comparator;
201 | this.data = [];
202 | }
203 |
204 | toArray() {
205 | return this.data;
206 | }
207 |
208 | insert(value) {
209 | const index = binarySearchForIndex(this.data, value, this.comparator);
210 |
211 | if (this.data[index] !== void 0 && this.comparator(this.data[index], value) === 0) {
212 | return this.data.splice(index, 1, this.onInsertConflict(this.data[index], value));
213 | } else {
214 | return this.data.splice(index, 0, value);
215 | }
216 | }
217 |
218 | remove(value) {
219 | const index = binarySearchForIndex(this.data, value, this.comparator);
220 |
221 | if (this.comparator(this.data[index], value) !== 0) {
222 | throw 'Value not in set';
223 | }
224 |
225 | return this.data.splice(index, 1);
226 | }
227 |
228 | clear() {
229 | return this.data.length = 0;
230 | }
231 |
232 | contains(value) {
233 | const index = binarySearchForIndex(this.data, value, this.comparator);
234 | return this.index !== this.data.length && this.comparator(this.data[index], value) === 0;
235 | }
236 |
237 | forEachImpl(callback, sortedSet, thisArg) {
238 | const data = this.data;
239 | const len = data.length;
240 |
241 | for (let i = 0; i < len; i++) {
242 | callback.call(thisArg, data[i], i, sortedSet);
243 | }
244 | }
245 |
246 | findIterator(value) {
247 | const index = binarySearchForIndex(this.data, value, this.comparator);
248 | return new Iterator(this, index);
249 | }
250 |
251 | beginIterator() {
252 | return new Iterator(this, 0);
253 | }
254 |
255 | endIterator() {
256 | return new Iterator(this, this.data.length);
257 | }
258 |
259 | }
260 |
261 | const descendAllTheWay = (leftOrRight, node) => {
262 | // Assumes node._iteratorParentNode is set
263 | while (node[leftOrRight] !== null) {
264 | const parent = node;
265 | node = node[leftOrRight];
266 | node._iteratorParentNode = parent;
267 | }
268 |
269 | return node;
270 | };
271 |
272 | const moveCursor = (leftOrRight, node) => {
273 | let parent, rightOrLeft;
274 |
275 | if (node[leftOrRight] !== null) {
276 | parent = node;
277 | node = node[leftOrRight];
278 | node._iteratorParentNode = parent;
279 | rightOrLeft = leftOrRight === 'left' ? 'right' : 'left';
280 | node = descendAllTheWay(rightOrLeft, node);
281 | } else {
282 | while ((parent = node._iteratorParentNode) !== null && parent[leftOrRight] === node) {
283 | node = parent;
284 | }
285 |
286 | node = parent; // either null or the correct-direction parent
287 | }
288 |
289 | return node;
290 | }; // The BinaryTreeIterator actually writes to the tree: it maintains a
291 | // "_iteratorParentNode" variable on each node. Please ignore this.
292 |
293 |
294 | class BinaryTreeIterator {
295 | constructor(tree1, node1) {
296 | this.tree = tree1;
297 | this.node = node1;
298 | }
299 |
300 | next() {
301 | if (this.node === null) {
302 | return null;
303 | } else {
304 | const node = moveCursor('right', this.node);
305 | return new BinaryTreeIterator(this.tree, node);
306 | }
307 | }
308 |
309 | previous() {
310 | if (this.node === null) {
311 | if (this.tree.root === null) {
312 | return null;
313 | } else {
314 | this.tree.root._iteratorParentNode = null;
315 | const node = descendAllTheWay('right', this.tree.root);
316 | return new BinaryTreeIterator(this.tree, node);
317 | }
318 | } else {
319 | const node = moveCursor('left', this.node);
320 |
321 | if (node === null) {
322 | return null;
323 | } else {
324 | return new BinaryTreeIterator(this.tree, node);
325 | }
326 | }
327 | }
328 |
329 | hasNext() {
330 | return this.node !== null;
331 | }
332 |
333 | hasPrevious() {
334 | return this.previous() !== null;
335 | }
336 |
337 | value() {
338 | if (this.node === null) {
339 | return null;
340 | } else {
341 | return this.node.value;
342 | }
343 | }
344 |
345 | setValue(value) {
346 | if (!this.tree.options.allowSetValue) {
347 | throw 'Must set options.allowSetValue';
348 | }
349 |
350 | if (!this.hasNext()) {
351 | throw 'Cannot set value at end of set';
352 | }
353 |
354 | return this.node.value = value;
355 | }
356 |
357 | }
358 |
359 | BinaryTreeIterator.find = function (tree, value, comparator) {
360 | const root = tree.root;
361 |
362 | if (root != null) {
363 | root._iteratorParentNode = null;
364 | }
365 |
366 | let node = root;
367 | let nextNode = null; // For finding an in-between node
368 |
369 | while (node !== null) {
370 | const cmp = comparator(value, node.value);
371 |
372 | if (cmp === 0) {
373 | break;
374 | } else if (cmp < 0) {
375 | if (node.left === null) {
376 | break;
377 | }
378 |
379 | nextNode = node; // If we descend all right after this until there are
380 | // no more right nodes, we want to return an
381 | // "in-between" iterator ... pointing here.
382 |
383 | node.left._iteratorParentNode = node;
384 | node = node.left;
385 | } else {
386 | if (node.right !== null) {
387 | node.right._iteratorParentNode = node;
388 | node = node.right;
389 | } else {
390 | node = nextNode;
391 | break;
392 | }
393 | }
394 | }
395 |
396 | return new BinaryTreeIterator(tree, node);
397 | };
398 |
399 | BinaryTreeIterator.left = tree => {
400 | if (tree.root === null) {
401 | return new BinaryTreeIterator(tree, null);
402 | } else {
403 | tree.root._iteratorParentNode = null;
404 | const node = descendAllTheWay('left', tree.root);
405 | return new BinaryTreeIterator(tree, node);
406 | }
407 | };
408 |
409 | BinaryTreeIterator.right = tree => {
410 | return new BinaryTreeIterator(tree, null);
411 | };
412 |
413 | const binaryTreeTraverse = (node, callback) => {
414 | if (node !== null) {
415 | binaryTreeTraverse(node.left, callback);
416 | callback(node.value);
417 | binaryTreeTraverse(node.right, callback);
418 | }
419 | }; // An AbstractBinaryTree has a @root. @root is null or an object with
420 | // `.left`, `.right` and `.value` properties.
421 |
422 |
423 | class AbstractBinaryTree {
424 | toArray() {
425 | const ret = [];
426 | binaryTreeTraverse(this.root, function (value) {
427 | return ret.push(value);
428 | });
429 | return ret;
430 | }
431 |
432 | clear() {
433 | return this.root = null;
434 | }
435 |
436 | forEachImpl(callback, sortedSet, thisArg) {
437 | let i = 0;
438 | binaryTreeTraverse(this.root, function (value) {
439 | callback.call(thisArg, value, i, sortedSet);
440 | i += 1;
441 | });
442 | }
443 |
444 | contains(value) {
445 | const comparator = this.comparator;
446 | let node = this.root;
447 |
448 | while (node !== null) {
449 | const cmp = comparator(value, node.value);
450 |
451 | if (cmp === 0) {
452 | break;
453 | } else if (cmp < 0) {
454 | node = node.left;
455 | } else {
456 | node = node.right;
457 | }
458 | }
459 |
460 | return node !== null && comparator(node.value, value) === 0;
461 | }
462 |
463 | findIterator(value) {
464 | return BinaryTreeIterator.find(this, value, this.comparator);
465 | }
466 |
467 | beginIterator() {
468 | return BinaryTreeIterator.left(this);
469 | }
470 |
471 | endIterator() {
472 | return BinaryTreeIterator.right(this);
473 | }
474 |
475 | }
476 |
477 | class Node {
478 | constructor(value) {
479 | this.value = value;
480 | this.left = null;
481 | this.right = null;
482 | }
483 |
484 | }
485 |
486 | const nodeAllTheWay = (node, leftOrRight) => {
487 | while (node[leftOrRight] !== null) {
488 | node = node[leftOrRight];
489 | }
490 |
491 | return node;
492 | }; // Returns the subtree, minus value
493 |
494 |
495 | const binaryTreeDelete = (node, value, comparator) => {
496 | if (node === null) {
497 | throw 'Value not in set';
498 | }
499 |
500 | const cmp = comparator(value, node.value);
501 |
502 | if (cmp < 0) {
503 | node.left = binaryTreeDelete(node.left, value, comparator);
504 | } else if (cmp > 0) {
505 | node.right = binaryTreeDelete(node.right, value, comparator); // This is the value we want to remove
506 | } else {
507 | if (node.left === null && node.right === null) {
508 | node = null;
509 | } else if (node.right === null) {
510 | node = node.left;
511 | } else if (node.left === null) {
512 | node = node.right;
513 | } else {
514 | const nextNode = nodeAllTheWay(node.right, 'left');
515 | node.value = nextNode.value;
516 | node.right = binaryTreeDelete(node.right, nextNode.value, comparator);
517 | }
518 | }
519 |
520 | return node;
521 | };
522 |
523 | class BinaryTreeStrategy extends AbstractBinaryTree {
524 | constructor(options) {
525 | super();
526 | this.options = options;
527 | this.comparator = this.options.comparator;
528 | this.onInsertConflict = this.options.onInsertConflict;
529 | this.root = null;
530 | }
531 |
532 | insert(value) {
533 | const compare = this.comparator;
534 |
535 | if (this.root !== null) {
536 | let parent = this.root;
537 | let leftOrRight = null;
538 |
539 | while (true) {
540 | const cmp = compare(value, parent.value);
541 |
542 | if (cmp === 0) {
543 | parent.value = this.onInsertConflict(parent.value, value);
544 | return;
545 | } else {
546 | leftOrRight = cmp < 0 ? 'left' : 'right';
547 |
548 | if (parent[leftOrRight] === null) {
549 | break;
550 | }
551 |
552 | parent = parent[leftOrRight];
553 | }
554 | }
555 |
556 | return parent[leftOrRight] = new Node(value);
557 | } else {
558 | return this.root = new Node(value);
559 | }
560 | }
561 |
562 | remove(value) {
563 | return this.root = binaryTreeDelete(this.root, value, this.comparator);
564 | }
565 |
566 | }
567 |
568 | // It's copied from http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf.
569 | // It's practically a copy-paste job, minus the semicolons. missing bits were
570 | // filled in with hints from
571 | // http://www.teachsolaisgames.com/articles/balanced_left_leaning.html
572 | // Here are some differences:
573 | // * This isn't a map structure: it's just a tree. There are no keys: the
574 | // comparator applies to the values.
575 | // * We use the passed comparator.
576 |
577 | class Node$1 {
578 | constructor(value1) {
579 | this.value = value1;
580 | this.left = null;
581 | this.right = null;
582 | this.isRed = true; // null nodes -- leaves -- are black
583 | }
584 |
585 | }
586 |
587 | const rotateLeft = h => {
588 | const x = h.right;
589 | h.right = x.left;
590 | x.left = h;
591 | x.isRed = h.isRed;
592 | h.isRed = true;
593 | return x;
594 | };
595 |
596 | const rotateRight = h => {
597 | const x = h.left;
598 | h.left = x.right;
599 | x.right = h;
600 | x.isRed = h.isRed;
601 | h.isRed = true;
602 | return x;
603 | };
604 |
605 | const colorFlip = h => {
606 | h.isRed = !h.isRed;
607 | h.left.isRed = !h.left.isRed;
608 | h.right.isRed = !h.right.isRed;
609 | };
610 |
611 | const moveRedLeft = h => {
612 | //throw 'Preconditions failed' if !(!h.left.isRed && !h.left.left?.isRed)
613 | colorFlip(h);
614 |
615 | if (h.right !== null && h.right.left !== null && h.right.left.isRed) {
616 | h.right = rotateRight(h.right);
617 | h = rotateLeft(h);
618 | colorFlip(h);
619 | }
620 |
621 | return h;
622 | };
623 |
624 | const moveRedRight = h => {
625 | //throw 'Preconditions failed' if !(!h.right.isRed && !h.right.left?.isRed)
626 | colorFlip(h);
627 |
628 | if (h.left !== null && h.left.left !== null && h.left.left.isRed) {
629 | h = rotateRight(h);
630 | colorFlip(h);
631 | }
632 |
633 | return h;
634 | };
635 |
636 | const insertInNode = (h, value, compare, onInsertConflict) => {
637 | if (h === null) {
638 | return new Node$1(value);
639 | } //if h.left isnt null && h.left.isRed && h.right isnt null && h.right.isRed
640 | // colorFlip(h)
641 |
642 |
643 | const cmp = compare(value, h.value);
644 |
645 | if (cmp === 0) {
646 | h.value = onInsertConflict(h.value, value);
647 | } else if (cmp < 0) {
648 | h.left = insertInNode(h.left, value, compare, onInsertConflict);
649 | } else {
650 | h.right = insertInNode(h.right, value, compare, onInsertConflict);
651 | }
652 |
653 | if (h.right !== null && h.right.isRed && !(h.left !== null && h.left.isRed)) {
654 | h = rotateLeft(h);
655 | }
656 |
657 | if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {
658 | h = rotateRight(h);
659 | } // Put this here -- I couldn't get the whole thing to work otherwise :(
660 |
661 |
662 | if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {
663 | colorFlip(h);
664 | }
665 |
666 | return h;
667 | };
668 |
669 | const findMinNode = h => {
670 | while (h.left !== null) {
671 | h = h.left;
672 | }
673 |
674 | return h;
675 | };
676 |
677 | const fixUp = h => {
678 | // Fix right-leaning red nodes
679 | if (h.right !== null && h.right.isRed) {
680 | h = rotateLeft(h);
681 | } // Handle a 4-node that traverses down the left
682 |
683 |
684 | if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {
685 | h = rotateRight(h);
686 | } // split 4-nodes
687 |
688 |
689 | if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {
690 | colorFlip(h);
691 | }
692 |
693 | return h;
694 | };
695 |
696 | const removeMinNode = h => {
697 | if (h.left === null) {
698 | return null;
699 | }
700 |
701 | if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {
702 | h = moveRedLeft(h);
703 | }
704 |
705 | h.left = removeMinNode(h.left);
706 | return fixUp(h);
707 | };
708 |
709 | const removeFromNode = (h, value, compare) => {
710 | if (h === null) {
711 | throw 'Value not in set';
712 | }
713 |
714 | if (compare(value, h.value) < 0) {
715 | if (h.left === null) {
716 | throw 'Value not in set';
717 | }
718 |
719 | if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {
720 | h = moveRedLeft(h);
721 | }
722 |
723 | h.left = removeFromNode(h.left, value, compare);
724 | } else {
725 | if (h.left !== null && h.left.isRed) {
726 | h = rotateRight(h);
727 | }
728 |
729 | if (h.right === null) {
730 | if (compare(value, h.value) === 0) {
731 | return null; // leaf node; LLRB assures no left value here
732 | } else {
733 | throw 'Value not in set';
734 | }
735 | }
736 |
737 | if (!h.right.isRed && !(h.right.left !== null && h.right.left.isRed)) {
738 | h = moveRedRight(h);
739 | }
740 |
741 | if (compare(value, h.value) === 0) {
742 | h.value = findMinNode(h.right).value;
743 | h.right = removeMinNode(h.right);
744 | } else {
745 | h.right = removeFromNode(h.right, value, compare);
746 | }
747 | }
748 |
749 | if (h !== null) {
750 | h = fixUp(h);
751 | }
752 |
753 | return h;
754 | };
755 |
756 | class RedBlackTreeStrategy extends AbstractBinaryTree {
757 | constructor(options) {
758 | super();
759 | this.options = options;
760 | this.comparator = this.options.comparator;
761 | this.onInsertConflict = this.options.onInsertConflict;
762 | this.root = null;
763 | }
764 |
765 | insert(value) {
766 | this.root = insertInNode(this.root, value, this.comparator, this.onInsertConflict);
767 | this.root.isRed = false; // always
768 | }
769 |
770 | remove(value) {
771 | this.root = removeFromNode(this.root, value, this.comparator);
772 |
773 | if (this.root !== null) {
774 | this.root.isRed = false;
775 | }
776 | }
777 |
778 | }
779 |
780 | const InsertConflictResolvers = {
781 | OnInsertConflictThrow: (oldValue, newValue) => {
782 | throw new Error("Value already in set");
783 | },
784 | OnInsertConflictReplace: (oldValue, newValue) => newValue,
785 | OnInsertConflictIgnore: (oldValue, newValue) => oldValue
786 | };
787 |
788 | class SortedSet extends AbstractSortedSet {
789 | constructor(options) {
790 | options || (options = {});
791 | options.strategy || (options.strategy = RedBlackTreeStrategy);
792 | options.comparator || (options.comparator = function (a, b) {
793 | return (a || 0) - (b || 0);
794 | });
795 | options.onInsertConflict || (options.onInsertConflict = InsertConflictResolvers.OnInsertConflictThrow);
796 | super(options);
797 | }
798 |
799 | }
800 | SortedSet.ArrayStrategy = ArrayStrategy;
801 | SortedSet.BinaryTreeStrategy = BinaryTreeStrategy;
802 | SortedSet.RedBlackTreeStrategy = RedBlackTreeStrategy;
803 | Object.assign(SortedSet, InsertConflictResolvers);
804 |
805 | return SortedSet;
806 |
807 | })));
808 | //# sourceMappingURL=sorted-set.js.map
809 |
--------------------------------------------------------------------------------
/sorted-set.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"sorted-set.js","sources":["src/SortedSet/AbstractSortedSet.js","src/SortedSet/ArrayStrategy.js","src/SortedSet/BinaryTreeIterator.js","src/SortedSet/AbstractBinaryTreeStrategy.js","src/SortedSet/BinaryTreeStrategy.js","src/SortedSet/RedBlackTreeStrategy.js","src/SortedSet/InsertConflictResolvers.js","src/SortedSet.js"],"sourcesContent":["\nclass AbstractSortedSet {\n constructor(options) {\n if ((options != null ? options.strategy : void 0) == null) {\n throw 'Must pass options.strategy, a strategy';\n }\n if ((options != null ? options.comparator : void 0) == null) {\n throw 'Must pass options.comparator, a comparator';\n }\n if ((options != null ? options.onInsertConflict : void 0) == null) {\n throw 'Must pass options.onInsertConflict, a function';\n }\n this.priv = new options.strategy(options);\n this.length = 0;\n }\n\n insert(value) {\n this.priv.insert(value);\n this.length += 1;\n return this;\n }\n\n remove(value) {\n this.priv.remove(value);\n this.length -= 1;\n return this;\n }\n\n clear() {\n this.priv.clear();\n this.length = 0;\n return this;\n }\n\n contains(value) {\n return this.priv.contains(value);\n }\n\n // Returns this set as an Array\n toArray() {\n return this.priv.toArray();\n }\n\n forEach(callback, thisArg) {\n this.priv.forEachImpl(callback, this, thisArg);\n return this;\n }\n\n map(callback, thisArg) {\n const ret = [];\n this.forEach(function(value, index, self) {\n return ret.push(callback.call(thisArg, value, index, self));\n });\n return ret;\n }\n\n filter(callback, thisArg) {\n const ret = [];\n this.forEach(function(value, index, self) {\n if (callback.call(thisArg, value, index, self)) {\n return ret.push(value);\n }\n });\n return ret;\n }\n\n every(callback, thisArg) {\n let ret = true;\n this.forEach(function(value, index, self) {\n if (ret && !callback.call(thisArg, value, index, self)) {\n ret = false;\n }\n });\n return ret;\n }\n\n some(callback, thisArg) {\n let ret = false;\n this.forEach(function(value, index, self) {\n if (!ret && callback.call(thisArg, value, index, self)) {\n ret = true;\n }\n });\n return ret;\n }\n\n // An iterator is similar to a C++ iterator: it points _before_ a value.\n\n // So in this sorted set:\n\n // | 1 | 2 | 3 | 4 | 5 |\n // ^a ^b ^c\n\n // `a` is a pointer to the beginning of the iterator. `a.value()` returns\n // `3`. `a.previous()` returns `null`. `a.setValue()` works, if\n // `options.allowSetValue` is true.\n\n // `b` is a pointer to the value `3`. `a.previous()` and `a.next()` both do\n // the obvious.\n\n // `c` is a pointer to the `null` value. `c.previous()` works; `c.next()`\n // returns null. `c.setValue()` throws an exception, even if\n // `options.allowSetValue` is true.\n\n // Iterators have `hasNext()` and `hasPrevious()` methods, too.\n\n // Iterators are immutible. `iterator.next()` returns a new iterator.\n\n // Iterators become invalid as soon as `insert()` or `remove()` is called.\n findIterator(value) {\n return this.priv.findIterator(value);\n }\n\n // Finds an iterator pointing to the lowest possible value.\n beginIterator() {\n return this.priv.beginIterator();\n }\n\n // Finds an iterator pointing to the `null` value.\n endIterator() {\n return this.priv.endIterator();\n }\n\n};\n\nexport default AbstractSortedSet;\n\n","\nclass Iterator {\n constructor(priv, index1) {\n this.priv = priv;\n this.index = index1;\n this.data = this.priv.data;\n }\n\n hasNext() {\n return this.index < this.data.length;\n }\n\n hasPrevious() {\n return this.index > 0;\n }\n\n value() {\n if (this.index < this.data.length) {\n return this.data[this.index];\n } else {\n return null;\n }\n }\n\n setValue(value) {\n if (!this.priv.options.allowSetValue) {\n throw 'Must set options.allowSetValue';\n }\n if (!this.hasNext()) {\n throw 'Cannot set value at end of set';\n }\n return this.data[this.index] = value;\n }\n\n next() {\n if (this.index >= this.data.length) {\n return null;\n } else {\n return new Iterator(this.priv, this.index + 1);\n }\n }\n\n previous() {\n if (this.index <= 0) {\n return null;\n } else {\n return new Iterator(this.priv, this.index - 1);\n }\n }\n\n};\n\nconst binarySearchForIndex = (array, value, comparator) => {\n let low = 0;\n let high = array.length;\n while (low < high) {\n const mid = (low + high) >>> 1;\n if (comparator(array[mid], value) < 0) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n};\n\nclass ArrayStrategy {\n constructor(options) {\n this.options = options;\n this.onInsertConflict = this.options.onInsertConflict;\n this.comparator = this.options.comparator;\n this.data = [];\n }\n\n toArray() {\n return this.data;\n }\n\n insert(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n if (this.data[index] !== void 0 && this.comparator(this.data[index], value) === 0) {\n return this.data.splice(index, 1, this.onInsertConflict(this.data[index], value));\n } else {\n return this.data.splice(index, 0, value);\n }\n }\n\n remove(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n if (this.comparator(this.data[index], value) !== 0) {\n throw 'Value not in set';\n }\n return this.data.splice(index, 1);\n }\n\n clear() {\n return this.data.length = 0;\n }\n\n contains(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n return this.index !== this.data.length && this.comparator(this.data[index], value) === 0;\n }\n\n forEachImpl(callback, sortedSet, thisArg) {\n const data = this.data;\n const len = data.length;\n for (let i = 0; i < len; i++) {\n callback.call(thisArg, data[i], i, sortedSet);\n }\n }\n\n findIterator(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n return new Iterator(this, index);\n }\n\n beginIterator() {\n return new Iterator(this, 0);\n }\n\n endIterator() {\n return new Iterator(this, this.data.length);\n }\n};\n\nexport default ArrayStrategy;\n","const descendAllTheWay = (leftOrRight, node) => {\n // Assumes node._iteratorParentNode is set\n while (node[leftOrRight] !== null) {\n const parent = node;\n node = node[leftOrRight];\n node._iteratorParentNode = parent;\n }\n return node;\n};\n\nconst moveCursor = (leftOrRight, node) => {\n let parent, rightOrLeft;\n if (node[leftOrRight] !== null) {\n parent = node;\n node = node[leftOrRight];\n node._iteratorParentNode = parent;\n rightOrLeft = leftOrRight === 'left' ? 'right' : 'left';\n node = descendAllTheWay(rightOrLeft, node);\n } else {\n while ((parent = node._iteratorParentNode) !== null && parent[leftOrRight] === node) {\n node = parent;\n }\n node = parent; // either null or the correct-direction parent\n }\n return node;\n};\n\n// The BinaryTreeIterator actually writes to the tree: it maintains a\n// \"_iteratorParentNode\" variable on each node. Please ignore this.\nclass BinaryTreeIterator {\n constructor(tree1, node1) {\n this.tree = tree1;\n this.node = node1;\n }\n\n next() {\n if (this.node === null) {\n return null;\n } else {\n const node = moveCursor('right', this.node);\n return new BinaryTreeIterator(this.tree, node);\n }\n }\n\n previous() {\n if (this.node === null) {\n if (this.tree.root === null) {\n return null;\n } else {\n this.tree.root._iteratorParentNode = null;\n const node = descendAllTheWay('right', this.tree.root);\n return new BinaryTreeIterator(this.tree, node);\n }\n } else {\n const node = moveCursor('left', this.node);\n if (node === null) {\n return null;\n } else {\n return new BinaryTreeIterator(this.tree, node);\n }\n }\n }\n\n hasNext() {\n return this.node !== null;\n }\n\n hasPrevious() {\n return this.previous() !== null;\n }\n\n value() {\n if (this.node === null) {\n return null;\n } else {\n return this.node.value;\n }\n }\n\n setValue(value) {\n if (!this.tree.options.allowSetValue) {\n throw 'Must set options.allowSetValue';\n }\n if (!this.hasNext()) {\n throw 'Cannot set value at end of set';\n }\n return this.node.value = value;\n }\n\n};\n\nBinaryTreeIterator.find = function(tree, value, comparator) {\n const root = tree.root;\n if (root != null) {\n root._iteratorParentNode = null;\n }\n let node = root;\n let nextNode = null; // For finding an in-between node\n while (node !== null) {\n const cmp = comparator(value, node.value);\n if (cmp === 0) {\n break;\n } else if (cmp < 0) {\n if (node.left === null) {\n break;\n }\n nextNode = node; // If we descend all right after this until there are\n // no more right nodes, we want to return an\n // \"in-between\" iterator ... pointing here.\n node.left._iteratorParentNode = node;\n node = node.left;\n } else {\n if (node.right !== null) {\n node.right._iteratorParentNode = node;\n node = node.right;\n } else {\n node = nextNode;\n break;\n }\n }\n }\n return new BinaryTreeIterator(tree, node);\n};\n\nBinaryTreeIterator.left = (tree) => {\n if (tree.root === null) {\n return new BinaryTreeIterator(tree, null);\n } else {\n tree.root._iteratorParentNode = null;\n const node = descendAllTheWay('left', tree.root);\n return new BinaryTreeIterator(tree, node);\n }\n};\n\nBinaryTreeIterator.right = (tree) => {\n return new BinaryTreeIterator(tree, null);\n};\n\nexport default BinaryTreeIterator;\n","\nimport BinaryTreeIterator from './BinaryTreeIterator';\n\nconst binaryTreeTraverse = (node, callback) => {\n if (node !== null) {\n binaryTreeTraverse(node.left, callback);\n callback(node.value);\n binaryTreeTraverse(node.right, callback);\n }\n};\n\n// An AbstractBinaryTree has a @root. @root is null or an object with\n// `.left`, `.right` and `.value` properties.\nclass AbstractBinaryTree {\n toArray() {\n const ret = [];\n binaryTreeTraverse(this.root, function(value) {\n return ret.push(value);\n });\n return ret;\n }\n\n clear() {\n return this.root = null;\n }\n\n forEachImpl(callback, sortedSet, thisArg) {\n let i = 0;\n binaryTreeTraverse(this.root, function(value) {\n callback.call(thisArg, value, i, sortedSet);\n i += 1;\n });\n }\n\n contains(value) {\n const comparator = this.comparator;\n let node = this.root;\n while (node !== null) {\n const cmp = comparator(value, node.value);\n if (cmp === 0) {\n break;\n } else if (cmp < 0) {\n node = node.left;\n } else {\n node = node.right;\n }\n }\n return node !== null && comparator(node.value, value) === 0;\n }\n\n findIterator(value) {\n return BinaryTreeIterator.find(this, value, this.comparator);\n }\n\n beginIterator() {\n return BinaryTreeIterator.left(this);\n }\n\n endIterator() {\n return BinaryTreeIterator.right(this);\n }\n\n};\n\nexport default AbstractBinaryTree;\n\n","import AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';\n\nclass Node {\n constructor(value) {\n this.value = value;\n this.left = null;\n this.right = null;\n }\n};\n\nconst nodeAllTheWay = (node, leftOrRight) => {\n while (node[leftOrRight] !== null) {\n node = node[leftOrRight];\n }\n return node;\n};\n\n// Returns the subtree, minus value\nconst binaryTreeDelete = (node, value, comparator) => {\n if (node === null) {\n throw 'Value not in set';\n }\n const cmp = comparator(value, node.value);\n if (cmp < 0) {\n node.left = binaryTreeDelete(node.left, value, comparator);\n } else if (cmp > 0) {\n node.right = binaryTreeDelete(node.right, value, comparator); // This is the value we want to remove\n } else {\n if (node.left === null && node.right === null) {\n node = null;\n } else if (node.right === null) {\n node = node.left;\n } else if (node.left === null) {\n node = node.right;\n } else {\n const nextNode = nodeAllTheWay(node.right, 'left');\n node.value = nextNode.value;\n node.right = binaryTreeDelete(node.right, nextNode.value, comparator);\n }\n }\n return node;\n};\n\nclass BinaryTreeStrategy extends AbstractBinaryTreeStrategy {\n constructor(options) {\n super();\n this.options = options;\n this.comparator = this.options.comparator;\n this.onInsertConflict = this.options.onInsertConflict;\n this.root = null;\n }\n\n insert(value) {\n const compare = this.comparator;\n if (this.root !== null) {\n let parent = this.root;\n let leftOrRight = null;\n while (true) {\n const cmp = compare(value, parent.value);\n if (cmp === 0) {\n parent.value = this.onInsertConflict(parent.value, value);\n return;\n } else {\n leftOrRight = cmp < 0 ? 'left' : 'right';\n if (parent[leftOrRight] === null) {\n break;\n }\n parent = parent[leftOrRight];\n }\n }\n return parent[leftOrRight] = new Node(value);\n } else {\n return this.root = new Node(value);\n }\n }\n\n remove(value) {\n return this.root = binaryTreeDelete(this.root, value, this.comparator);\n }\n\n};\n\nexport default BinaryTreeStrategy;\n","\nimport AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';\n\n// An implementation of Left-Leaning Red-Black trees.\n\n// It's copied from http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf.\n// It's practically a copy-paste job, minus the semicolons. missing bits were\n// filled in with hints from\n// http://www.teachsolaisgames.com/articles/balanced_left_leaning.html\n\n// Here are some differences:\n// * This isn't a map structure: it's just a tree. There are no keys: the\n// comparator applies to the values.\n// * We use the passed comparator.\nclass Node {\n constructor(value1) {\n this.value = value1;\n this.left = null;\n this.right = null;\n this.isRed = true; // null nodes -- leaves -- are black\n }\n\n};\n\nconst rotateLeft = (h) => {\n const x = h.right;\n h.right = x.left;\n x.left = h;\n x.isRed = h.isRed;\n h.isRed = true;\n return x;\n};\n\nconst rotateRight = (h) => {\n const x = h.left;\n h.left = x.right;\n x.right = h;\n x.isRed = h.isRed;\n h.isRed = true;\n return x;\n};\n\nconst colorFlip = (h) => {\n h.isRed = !h.isRed;\n h.left.isRed = !h.left.isRed;\n h.right.isRed = !h.right.isRed;\n};\n\nconst moveRedLeft = (h) => {\n //throw 'Preconditions failed' if !(!h.left.isRed && !h.left.left?.isRed)\n colorFlip(h);\n if (h.right !== null && h.right.left !== null && h.right.left.isRed) {\n h.right = rotateRight(h.right);\n h = rotateLeft(h);\n colorFlip(h);\n }\n return h;\n};\n\nconst moveRedRight = (h) => {\n //throw 'Preconditions failed' if !(!h.right.isRed && !h.right.left?.isRed)\n colorFlip(h);\n if (h.left !== null && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n colorFlip(h);\n }\n return h;\n};\n\nconst insertInNode = (h, value, compare, onInsertConflict) => {\n if (h === null) {\n return new Node(value);\n }\n //if h.left isnt null && h.left.isRed && h.right isnt null && h.right.isRed\n // colorFlip(h)\n const cmp = compare(value, h.value);\n if (cmp === 0) {\n h.value = onInsertConflict(h.value, value);\n } else if (cmp < 0) {\n h.left = insertInNode(h.left, value, compare, onInsertConflict);\n } else {\n h.right = insertInNode(h.right, value, compare, onInsertConflict);\n }\n if (h.right !== null && h.right.isRed && !(h.left !== null && h.left.isRed)) {\n h = rotateLeft(h);\n }\n if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n }\n // Put this here -- I couldn't get the whole thing to work otherwise :(\n if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {\n colorFlip(h);\n }\n return h;\n};\n\nconst findMinNode = (h) => {\n while (h.left !== null) {\n h = h.left;\n }\n return h;\n};\n\nconst fixUp = (h) => {\n // Fix right-leaning red nodes\n if (h.right !== null && h.right.isRed) {\n h = rotateLeft(h);\n }\n // Handle a 4-node that traverses down the left\n if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n }\n // split 4-nodes\n if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {\n colorFlip(h);\n }\n return h;\n};\n\nconst removeMinNode = (h) => {\n if (h.left === null) {\n return null;\n }\n if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {\n h = moveRedLeft(h);\n }\n h.left = removeMinNode(h.left);\n return fixUp(h);\n};\n\nconst removeFromNode = (h, value, compare) => {\n if (h === null) {\n throw 'Value not in set';\n }\n if (compare(value, h.value) < 0) {\n if (h.left === null) {\n throw 'Value not in set';\n }\n if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {\n h = moveRedLeft(h);\n }\n h.left = removeFromNode(h.left, value, compare);\n } else {\n if (h.left !== null && h.left.isRed) {\n h = rotateRight(h);\n }\n if (h.right === null) {\n if (compare(value, h.value) === 0) {\n return null; // leaf node; LLRB assures no left value here\n } else {\n throw 'Value not in set';\n }\n }\n if (!h.right.isRed && !(h.right.left !== null && h.right.left.isRed)) {\n h = moveRedRight(h);\n }\n if (compare(value, h.value) === 0) {\n h.value = findMinNode(h.right).value;\n h.right = removeMinNode(h.right);\n } else {\n h.right = removeFromNode(h.right, value, compare);\n }\n }\n if (h !== null) {\n h = fixUp(h);\n }\n return h;\n};\n\nclass RedBlackTreeStrategy extends AbstractBinaryTreeStrategy {\n constructor(options) {\n super();\n this.options = options;\n this.comparator = this.options.comparator;\n this.onInsertConflict = this.options.onInsertConflict;\n this.root = null;\n }\n\n insert(value) {\n this.root = insertInNode(this.root, value, this.comparator, this.onInsertConflict);\n this.root.isRed = false; // always\n }\n\n remove(value) {\n this.root = removeFromNode(this.root, value, this.comparator);\n if (this.root !== null) {\n this.root.isRed = false;\n }\n }\n\n};\n\nexport default RedBlackTreeStrategy;\n","const InsertConflictResolvers = {\n OnInsertConflictThrow: (oldValue, newValue) => { throw new Error(\"Value already in set\") },\n OnInsertConflictReplace: (oldValue, newValue) => newValue,\n OnInsertConflictIgnore: (oldValue, newValue) => oldValue,\n};\nexport default InsertConflictResolvers;\n","import AbstractSortedSet from './SortedSet/AbstractSortedSet';\nimport ArrayStrategy from './SortedSet/ArrayStrategy';\nimport BinaryTreeStrategy from './SortedSet/BinaryTreeStrategy';\nimport RedBlackTreeStrategy from './SortedSet/RedBlackTreeStrategy';\nimport InsertConflictResolvers from './SortedSet/InsertConflictResolvers';\n\nclass SortedSet extends AbstractSortedSet {\n constructor(options) {\n options || (options = {});\n options.strategy || (options.strategy = RedBlackTreeStrategy);\n options.comparator || (options.comparator = function(a, b) {\n return (a || 0) - (b || 0);\n });\n options.onInsertConflict || (options.onInsertConflict = InsertConflictResolvers.OnInsertConflictThrow);\n super(options);\n }\n};\n\nSortedSet.ArrayStrategy = ArrayStrategy;\nSortedSet.BinaryTreeStrategy = BinaryTreeStrategy;\nSortedSet.RedBlackTreeStrategy = RedBlackTreeStrategy;\n\nObject.assign(SortedSet, InsertConflictResolvers);\n\nexport default SortedSet;\n\n"],"names":["AbstractSortedSet","constructor","options","strategy","comparator","onInsertConflict","priv","length","insert","value","remove","clear","contains","toArray","forEach","callback","thisArg","forEachImpl","map","ret","index","self","push","call","filter","every","some","findIterator","beginIterator","endIterator","Iterator","index1","data","hasNext","hasPrevious","setValue","allowSetValue","next","previous","binarySearchForIndex","array","low","high","mid","ArrayStrategy","splice","sortedSet","len","i","descendAllTheWay","leftOrRight","node","parent","_iteratorParentNode","moveCursor","rightOrLeft","BinaryTreeIterator","tree1","node1","tree","root","find","nextNode","cmp","left","right","binaryTreeTraverse","AbstractBinaryTree","Node","nodeAllTheWay","binaryTreeDelete","BinaryTreeStrategy","AbstractBinaryTreeStrategy","compare","value1","isRed","rotateLeft","h","x","rotateRight","colorFlip","moveRedLeft","moveRedRight","insertInNode","findMinNode","fixUp","removeMinNode","removeFromNode","RedBlackTreeStrategy","InsertConflictResolvers","OnInsertConflictThrow","oldValue","newValue","Error","OnInsertConflictReplace","OnInsertConflictIgnore","SortedSet","a","b","Object","assign"],"mappings":"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"}
--------------------------------------------------------------------------------
/sorted-set.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self)["sorted-set"]=e()}(this,(function(){"use strict";class t{constructor(t,e){this.priv=t,this.index=e,this.data=this.priv.data}hasNext(){return this.index0}value(){return this.index=this.data.length?null:new t(this.priv,this.index+1)}previous(){return this.index<=0?null:new t(this.priv,this.index-1)}}const e=(t,e,r)=>{let n=0,i=t.length;for(;n>>1;r(t[l],e)<0?n=l+1:i=l}return n};const r=(t,e)=>{for(;null!==e[t];){const r=e;(e=e[t])._iteratorParentNode=r}return e},n=(t,e)=>{let n,i;if(null!==e[t])n=e,(e=e[t])._iteratorParentNode=n,i="left"===t?"right":"left",e=r(i,e);else{for(;null!==(n=e._iteratorParentNode)&&n[t]===e;)e=n;e=n}return e};class i{constructor(t,e){this.tree=t,this.node=e}next(){if(null===this.node)return null;{const t=n("right",this.node);return new i(this.tree,t)}}previous(){if(null===this.node){if(null===this.tree.root)return null;{this.tree.root._iteratorParentNode=null;const t=r("right",this.tree.root);return new i(this.tree,t)}}{const t=n("left",this.node);return null===t?null:new i(this.tree,t)}}hasNext(){return null!==this.node}hasPrevious(){return null!==this.previous()}value(){return null===this.node?null:this.node.value}setValue(t){if(!this.tree.options.allowSetValue)throw"Must set options.allowSetValue";if(!this.hasNext())throw"Cannot set value at end of set";return this.node.value=t}}i.find=function(t,e,r){const n=t.root;null!=n&&(n._iteratorParentNode=null);let l=n,s=null;for(;null!==l;){const t=r(e,l.value);if(0===t)break;if(t<0){if(null===l.left)break;s=l,l.left._iteratorParentNode=l,l=l.left}else{if(null===l.right){l=s;break}l.right._iteratorParentNode=l,l=l.right}}return new i(t,l)},i.left=t=>{if(null===t.root)return new i(t,null);{t.root._iteratorParentNode=null;const e=r("left",t.root);return new i(t,e)}},i.right=t=>new i(t,null);const l=(t,e)=>{null!==t&&(l(t.left,e),e(t.value),l(t.right,e))};class s{toArray(){const t=[];return l(this.root,(function(e){return t.push(e)})),t}clear(){return this.root=null}forEachImpl(t,e,r){let n=0;l(this.root,(function(i){t.call(r,i,n,e),n+=1}))}contains(t){const e=this.comparator;let r=this.root;for(;null!==r;){const n=e(t,r.value);if(0===n)break;r=n<0?r.left:r.right}return null!==r&&0===e(r.value,t)}findIterator(t){return i.find(this,t,this.comparator)}beginIterator(){return i.left(this)}endIterator(){return i.right(this)}}class o{constructor(t){this.value=t,this.left=null,this.right=null}}const a=(t,e,r)=>{if(null===t)throw"Value not in set";const n=r(e,t.value);if(n<0)t.left=a(t.left,e,r);else if(n>0)t.right=a(t.right,e,r);else if(null===t.left&&null===t.right)t=null;else if(null===t.right)t=t.left;else if(null===t.left)t=t.right;else{const e=((t,e)=>{for(;null!==t[e];)t=t[e];return t})(t.right,"left");t.value=e.value,t.right=a(t.right,e.value,r)}return t};class h{constructor(t){this.value=t,this.left=null,this.right=null,this.isRed=!0}}const u=t=>{const e=t.right;return t.right=e.left,e.left=t,e.isRed=t.isRed,t.isRed=!0,e},f=t=>{const e=t.left;return t.left=e.right,e.right=t,e.isRed=t.isRed,t.isRed=!0,e},c=t=>{t.isRed=!t.isRed,t.left.isRed=!t.left.isRed,t.right.isRed=!t.right.isRed},d=t=>(c(t),null!==t.right&&null!==t.right.left&&t.right.left.isRed&&(t.right=f(t.right),t=u(t),c(t)),t),p=(t,e,r,n)=>{if(null===t)return new h(e);const i=r(e,t.value);return 0===i?t.value=n(t.value,e):i<0?t.left=p(t.left,e,r,n):t.right=p(t.right,e,r,n),null===t.right||!t.right.isRed||null!==t.left&&t.left.isRed||(t=u(t)),null!==t.left&&t.left.isRed&&null!==t.left.left&&t.left.left.isRed&&(t=f(t)),null!==t.left&&t.left.isRed&&null!==t.right&&t.right.isRed&&c(t),t},g=t=>(null!==t.right&&t.right.isRed&&(t=u(t)),null!==t.left&&t.left.isRed&&null!==t.left.left&&t.left.left.isRed&&(t=f(t)),null!==t.left&&t.left.isRed&&null!==t.right&&t.right.isRed&&c(t),t),v=t=>null===t.left?null:(t.left.isRed||null!==t.left.left&&t.left.left.isRed||(t=d(t)),t.left=v(t.left),g(t)),m=(t,e,r)=>{if(null===t)throw"Value not in set";if(r(e,t.value)<0){if(null===t.left)throw"Value not in set";t.left.isRed||null!==t.left.left&&t.left.left.isRed||(t=d(t)),t.left=m(t.left,e,r)}else{if(null!==t.left&&t.left.isRed&&(t=f(t)),null===t.right){if(0===r(e,t.value))return null;throw"Value not in set"}t.right.isRed||null!==t.right.left&&t.right.left.isRed||(t=(t=>(c(t),null!==t.left&&null!==t.left.left&&t.left.left.isRed&&(t=f(t),c(t)),t))(t)),0===r(e,t.value)?(t.value=(t=>{for(;null!==t.left;)t=t.left;return t})(t.right).value,t.right=v(t.right)):t.right=m(t.right,e,r)}return null!==t&&(t=g(t)),t};class R extends s{constructor(t){super(),this.options=t,this.comparator=this.options.comparator,this.onInsertConflict=this.options.onInsertConflict,this.root=null}insert(t){this.root=p(this.root,t,this.comparator,this.onInsertConflict),this.root.isRed=!1}remove(t){this.root=m(this.root,t,this.comparator),null!==this.root&&(this.root.isRed=!1)}}const w={OnInsertConflictThrow:(t,e)=>{throw new Error("Value already in set")},OnInsertConflictReplace:(t,e)=>e,OnInsertConflictIgnore:(t,e)=>t};class I extends class{constructor(t){if(null==(null!=t?t.strategy:void 0))throw"Must pass options.strategy, a strategy";if(null==(null!=t?t.comparator:void 0))throw"Must pass options.comparator, a comparator";if(null==(null!=t?t.onInsertConflict:void 0))throw"Must pass options.onInsertConflict, a function";this.priv=new t.strategy(t),this.length=0}insert(t){return this.priv.insert(t),this.length+=1,this}remove(t){return this.priv.remove(t),this.length-=1,this}clear(){return this.priv.clear(),this.length=0,this}contains(t){return this.priv.contains(t)}toArray(){return this.priv.toArray()}forEach(t,e){return this.priv.forEachImpl(t,this,e),this}map(t,e){const r=[];return this.forEach((function(n,i,l){return r.push(t.call(e,n,i,l))})),r}filter(t,e){const r=[];return this.forEach((function(n,i,l){if(t.call(e,n,i,l))return r.push(n)})),r}every(t,e){let r=!0;return this.forEach((function(n,i,l){r&&!t.call(e,n,i,l)&&(r=!1)})),r}some(t,e){let r=!1;return this.forEach((function(n,i,l){!r&&t.call(e,n,i,l)&&(r=!0)})),r}findIterator(t){return this.priv.findIterator(t)}beginIterator(){return this.priv.beginIterator()}endIterator(){return this.priv.endIterator()}}{constructor(t){t||(t={}),t.strategy||(t.strategy=R),t.comparator||(t.comparator=function(t,e){return(t||0)-(e||0)}),t.onInsertConflict||(t.onInsertConflict=w.OnInsertConflictThrow),super(t)}}return I.ArrayStrategy=class{constructor(t){this.options=t,this.onInsertConflict=this.options.onInsertConflict,this.comparator=this.options.comparator,this.data=[]}toArray(){return this.data}insert(t){const r=e(this.data,t,this.comparator);return void 0!==this.data[r]&&0===this.comparator(this.data[r],t)?this.data.splice(r,1,this.onInsertConflict(this.data[r],t)):this.data.splice(r,0,t)}remove(t){const r=e(this.data,t,this.comparator);if(0!==this.comparator(this.data[r],t))throw"Value not in set";return this.data.splice(r,1)}clear(){return this.data.length=0}contains(t){const r=e(this.data,t,this.comparator);return this.index!==this.data.length&&0===this.comparator(this.data[r],t)}forEachImpl(t,e,r){const n=this.data,i=n.length;for(let l=0;l 0;\n }\n\n value() {\n if (this.index < this.data.length) {\n return this.data[this.index];\n } else {\n return null;\n }\n }\n\n setValue(value) {\n if (!this.priv.options.allowSetValue) {\n throw 'Must set options.allowSetValue';\n }\n if (!this.hasNext()) {\n throw 'Cannot set value at end of set';\n }\n return this.data[this.index] = value;\n }\n\n next() {\n if (this.index >= this.data.length) {\n return null;\n } else {\n return new Iterator(this.priv, this.index + 1);\n }\n }\n\n previous() {\n if (this.index <= 0) {\n return null;\n } else {\n return new Iterator(this.priv, this.index - 1);\n }\n }\n\n};\n\nconst binarySearchForIndex = (array, value, comparator) => {\n let low = 0;\n let high = array.length;\n while (low < high) {\n const mid = (low + high) >>> 1;\n if (comparator(array[mid], value) < 0) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n};\n\nclass ArrayStrategy {\n constructor(options) {\n this.options = options;\n this.onInsertConflict = this.options.onInsertConflict;\n this.comparator = this.options.comparator;\n this.data = [];\n }\n\n toArray() {\n return this.data;\n }\n\n insert(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n if (this.data[index] !== void 0 && this.comparator(this.data[index], value) === 0) {\n return this.data.splice(index, 1, this.onInsertConflict(this.data[index], value));\n } else {\n return this.data.splice(index, 0, value);\n }\n }\n\n remove(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n if (this.comparator(this.data[index], value) !== 0) {\n throw 'Value not in set';\n }\n return this.data.splice(index, 1);\n }\n\n clear() {\n return this.data.length = 0;\n }\n\n contains(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n return this.index !== this.data.length && this.comparator(this.data[index], value) === 0;\n }\n\n forEachImpl(callback, sortedSet, thisArg) {\n const data = this.data;\n const len = data.length;\n for (let i = 0; i < len; i++) {\n callback.call(thisArg, data[i], i, sortedSet);\n }\n }\n\n findIterator(value) {\n const index = binarySearchForIndex(this.data, value, this.comparator);\n return new Iterator(this, index);\n }\n\n beginIterator() {\n return new Iterator(this, 0);\n }\n\n endIterator() {\n return new Iterator(this, this.data.length);\n }\n};\n\nexport default ArrayStrategy;\n","const descendAllTheWay = (leftOrRight, node) => {\n // Assumes node._iteratorParentNode is set\n while (node[leftOrRight] !== null) {\n const parent = node;\n node = node[leftOrRight];\n node._iteratorParentNode = parent;\n }\n return node;\n};\n\nconst moveCursor = (leftOrRight, node) => {\n let parent, rightOrLeft;\n if (node[leftOrRight] !== null) {\n parent = node;\n node = node[leftOrRight];\n node._iteratorParentNode = parent;\n rightOrLeft = leftOrRight === 'left' ? 'right' : 'left';\n node = descendAllTheWay(rightOrLeft, node);\n } else {\n while ((parent = node._iteratorParentNode) !== null && parent[leftOrRight] === node) {\n node = parent;\n }\n node = parent; // either null or the correct-direction parent\n }\n return node;\n};\n\n// The BinaryTreeIterator actually writes to the tree: it maintains a\n// \"_iteratorParentNode\" variable on each node. Please ignore this.\nclass BinaryTreeIterator {\n constructor(tree1, node1) {\n this.tree = tree1;\n this.node = node1;\n }\n\n next() {\n if (this.node === null) {\n return null;\n } else {\n const node = moveCursor('right', this.node);\n return new BinaryTreeIterator(this.tree, node);\n }\n }\n\n previous() {\n if (this.node === null) {\n if (this.tree.root === null) {\n return null;\n } else {\n this.tree.root._iteratorParentNode = null;\n const node = descendAllTheWay('right', this.tree.root);\n return new BinaryTreeIterator(this.tree, node);\n }\n } else {\n const node = moveCursor('left', this.node);\n if (node === null) {\n return null;\n } else {\n return new BinaryTreeIterator(this.tree, node);\n }\n }\n }\n\n hasNext() {\n return this.node !== null;\n }\n\n hasPrevious() {\n return this.previous() !== null;\n }\n\n value() {\n if (this.node === null) {\n return null;\n } else {\n return this.node.value;\n }\n }\n\n setValue(value) {\n if (!this.tree.options.allowSetValue) {\n throw 'Must set options.allowSetValue';\n }\n if (!this.hasNext()) {\n throw 'Cannot set value at end of set';\n }\n return this.node.value = value;\n }\n\n};\n\nBinaryTreeIterator.find = function(tree, value, comparator) {\n const root = tree.root;\n if (root != null) {\n root._iteratorParentNode = null;\n }\n let node = root;\n let nextNode = null; // For finding an in-between node\n while (node !== null) {\n const cmp = comparator(value, node.value);\n if (cmp === 0) {\n break;\n } else if (cmp < 0) {\n if (node.left === null) {\n break;\n }\n nextNode = node; // If we descend all right after this until there are\n // no more right nodes, we want to return an\n // \"in-between\" iterator ... pointing here.\n node.left._iteratorParentNode = node;\n node = node.left;\n } else {\n if (node.right !== null) {\n node.right._iteratorParentNode = node;\n node = node.right;\n } else {\n node = nextNode;\n break;\n }\n }\n }\n return new BinaryTreeIterator(tree, node);\n};\n\nBinaryTreeIterator.left = (tree) => {\n if (tree.root === null) {\n return new BinaryTreeIterator(tree, null);\n } else {\n tree.root._iteratorParentNode = null;\n const node = descendAllTheWay('left', tree.root);\n return new BinaryTreeIterator(tree, node);\n }\n};\n\nBinaryTreeIterator.right = (tree) => {\n return new BinaryTreeIterator(tree, null);\n};\n\nexport default BinaryTreeIterator;\n","\nimport BinaryTreeIterator from './BinaryTreeIterator';\n\nconst binaryTreeTraverse = (node, callback) => {\n if (node !== null) {\n binaryTreeTraverse(node.left, callback);\n callback(node.value);\n binaryTreeTraverse(node.right, callback);\n }\n};\n\n// An AbstractBinaryTree has a @root. @root is null or an object with\n// `.left`, `.right` and `.value` properties.\nclass AbstractBinaryTree {\n toArray() {\n const ret = [];\n binaryTreeTraverse(this.root, function(value) {\n return ret.push(value);\n });\n return ret;\n }\n\n clear() {\n return this.root = null;\n }\n\n forEachImpl(callback, sortedSet, thisArg) {\n let i = 0;\n binaryTreeTraverse(this.root, function(value) {\n callback.call(thisArg, value, i, sortedSet);\n i += 1;\n });\n }\n\n contains(value) {\n const comparator = this.comparator;\n let node = this.root;\n while (node !== null) {\n const cmp = comparator(value, node.value);\n if (cmp === 0) {\n break;\n } else if (cmp < 0) {\n node = node.left;\n } else {\n node = node.right;\n }\n }\n return node !== null && comparator(node.value, value) === 0;\n }\n\n findIterator(value) {\n return BinaryTreeIterator.find(this, value, this.comparator);\n }\n\n beginIterator() {\n return BinaryTreeIterator.left(this);\n }\n\n endIterator() {\n return BinaryTreeIterator.right(this);\n }\n\n};\n\nexport default AbstractBinaryTree;\n\n","import AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';\n\nclass Node {\n constructor(value) {\n this.value = value;\n this.left = null;\n this.right = null;\n }\n};\n\nconst nodeAllTheWay = (node, leftOrRight) => {\n while (node[leftOrRight] !== null) {\n node = node[leftOrRight];\n }\n return node;\n};\n\n// Returns the subtree, minus value\nconst binaryTreeDelete = (node, value, comparator) => {\n if (node === null) {\n throw 'Value not in set';\n }\n const cmp = comparator(value, node.value);\n if (cmp < 0) {\n node.left = binaryTreeDelete(node.left, value, comparator);\n } else if (cmp > 0) {\n node.right = binaryTreeDelete(node.right, value, comparator); // This is the value we want to remove\n } else {\n if (node.left === null && node.right === null) {\n node = null;\n } else if (node.right === null) {\n node = node.left;\n } else if (node.left === null) {\n node = node.right;\n } else {\n const nextNode = nodeAllTheWay(node.right, 'left');\n node.value = nextNode.value;\n node.right = binaryTreeDelete(node.right, nextNode.value, comparator);\n }\n }\n return node;\n};\n\nclass BinaryTreeStrategy extends AbstractBinaryTreeStrategy {\n constructor(options) {\n super();\n this.options = options;\n this.comparator = this.options.comparator;\n this.onInsertConflict = this.options.onInsertConflict;\n this.root = null;\n }\n\n insert(value) {\n const compare = this.comparator;\n if (this.root !== null) {\n let parent = this.root;\n let leftOrRight = null;\n while (true) {\n const cmp = compare(value, parent.value);\n if (cmp === 0) {\n parent.value = this.onInsertConflict(parent.value, value);\n return;\n } else {\n leftOrRight = cmp < 0 ? 'left' : 'right';\n if (parent[leftOrRight] === null) {\n break;\n }\n parent = parent[leftOrRight];\n }\n }\n return parent[leftOrRight] = new Node(value);\n } else {\n return this.root = new Node(value);\n }\n }\n\n remove(value) {\n return this.root = binaryTreeDelete(this.root, value, this.comparator);\n }\n\n};\n\nexport default BinaryTreeStrategy;\n","\nimport AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';\n\n// An implementation of Left-Leaning Red-Black trees.\n\n// It's copied from http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf.\n// It's practically a copy-paste job, minus the semicolons. missing bits were\n// filled in with hints from\n// http://www.teachsolaisgames.com/articles/balanced_left_leaning.html\n\n// Here are some differences:\n// * This isn't a map structure: it's just a tree. There are no keys: the\n// comparator applies to the values.\n// * We use the passed comparator.\nclass Node {\n constructor(value1) {\n this.value = value1;\n this.left = null;\n this.right = null;\n this.isRed = true; // null nodes -- leaves -- are black\n }\n\n};\n\nconst rotateLeft = (h) => {\n const x = h.right;\n h.right = x.left;\n x.left = h;\n x.isRed = h.isRed;\n h.isRed = true;\n return x;\n};\n\nconst rotateRight = (h) => {\n const x = h.left;\n h.left = x.right;\n x.right = h;\n x.isRed = h.isRed;\n h.isRed = true;\n return x;\n};\n\nconst colorFlip = (h) => {\n h.isRed = !h.isRed;\n h.left.isRed = !h.left.isRed;\n h.right.isRed = !h.right.isRed;\n};\n\nconst moveRedLeft = (h) => {\n //throw 'Preconditions failed' if !(!h.left.isRed && !h.left.left?.isRed)\n colorFlip(h);\n if (h.right !== null && h.right.left !== null && h.right.left.isRed) {\n h.right = rotateRight(h.right);\n h = rotateLeft(h);\n colorFlip(h);\n }\n return h;\n};\n\nconst moveRedRight = (h) => {\n //throw 'Preconditions failed' if !(!h.right.isRed && !h.right.left?.isRed)\n colorFlip(h);\n if (h.left !== null && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n colorFlip(h);\n }\n return h;\n};\n\nconst insertInNode = (h, value, compare, onInsertConflict) => {\n if (h === null) {\n return new Node(value);\n }\n //if h.left isnt null && h.left.isRed && h.right isnt null && h.right.isRed\n // colorFlip(h)\n const cmp = compare(value, h.value);\n if (cmp === 0) {\n h.value = onInsertConflict(h.value, value);\n } else if (cmp < 0) {\n h.left = insertInNode(h.left, value, compare, onInsertConflict);\n } else {\n h.right = insertInNode(h.right, value, compare, onInsertConflict);\n }\n if (h.right !== null && h.right.isRed && !(h.left !== null && h.left.isRed)) {\n h = rotateLeft(h);\n }\n if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n }\n // Put this here -- I couldn't get the whole thing to work otherwise :(\n if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {\n colorFlip(h);\n }\n return h;\n};\n\nconst findMinNode = (h) => {\n while (h.left !== null) {\n h = h.left;\n }\n return h;\n};\n\nconst fixUp = (h) => {\n // Fix right-leaning red nodes\n if (h.right !== null && h.right.isRed) {\n h = rotateLeft(h);\n }\n // Handle a 4-node that traverses down the left\n if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {\n h = rotateRight(h);\n }\n // split 4-nodes\n if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {\n colorFlip(h);\n }\n return h;\n};\n\nconst removeMinNode = (h) => {\n if (h.left === null) {\n return null;\n }\n if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {\n h = moveRedLeft(h);\n }\n h.left = removeMinNode(h.left);\n return fixUp(h);\n};\n\nconst removeFromNode = (h, value, compare) => {\n if (h === null) {\n throw 'Value not in set';\n }\n if (compare(value, h.value) < 0) {\n if (h.left === null) {\n throw 'Value not in set';\n }\n if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {\n h = moveRedLeft(h);\n }\n h.left = removeFromNode(h.left, value, compare);\n } else {\n if (h.left !== null && h.left.isRed) {\n h = rotateRight(h);\n }\n if (h.right === null) {\n if (compare(value, h.value) === 0) {\n return null; // leaf node; LLRB assures no left value here\n } else {\n throw 'Value not in set';\n }\n }\n if (!h.right.isRed && !(h.right.left !== null && h.right.left.isRed)) {\n h = moveRedRight(h);\n }\n if (compare(value, h.value) === 0) {\n h.value = findMinNode(h.right).value;\n h.right = removeMinNode(h.right);\n } else {\n h.right = removeFromNode(h.right, value, compare);\n }\n }\n if (h !== null) {\n h = fixUp(h);\n }\n return h;\n};\n\nclass RedBlackTreeStrategy extends AbstractBinaryTreeStrategy {\n constructor(options) {\n super();\n this.options = options;\n this.comparator = this.options.comparator;\n this.onInsertConflict = this.options.onInsertConflict;\n this.root = null;\n }\n\n insert(value) {\n this.root = insertInNode(this.root, value, this.comparator, this.onInsertConflict);\n this.root.isRed = false; // always\n }\n\n remove(value) {\n this.root = removeFromNode(this.root, value, this.comparator);\n if (this.root !== null) {\n this.root.isRed = false;\n }\n }\n\n};\n\nexport default RedBlackTreeStrategy;\n","const InsertConflictResolvers = {\n OnInsertConflictThrow: (oldValue, newValue) => { throw new Error(\"Value already in set\") },\n OnInsertConflictReplace: (oldValue, newValue) => newValue,\n OnInsertConflictIgnore: (oldValue, newValue) => oldValue,\n};\nexport default InsertConflictResolvers;\n","import AbstractSortedSet from './SortedSet/AbstractSortedSet';\nimport ArrayStrategy from './SortedSet/ArrayStrategy';\nimport BinaryTreeStrategy from './SortedSet/BinaryTreeStrategy';\nimport RedBlackTreeStrategy from './SortedSet/RedBlackTreeStrategy';\nimport InsertConflictResolvers from './SortedSet/InsertConflictResolvers';\n\nclass SortedSet extends AbstractSortedSet {\n constructor(options) {\n options || (options = {});\n options.strategy || (options.strategy = RedBlackTreeStrategy);\n options.comparator || (options.comparator = function(a, b) {\n return (a || 0) - (b || 0);\n });\n options.onInsertConflict || (options.onInsertConflict = InsertConflictResolvers.OnInsertConflictThrow);\n super(options);\n }\n};\n\nSortedSet.ArrayStrategy = ArrayStrategy;\nSortedSet.BinaryTreeStrategy = BinaryTreeStrategy;\nSortedSet.RedBlackTreeStrategy = RedBlackTreeStrategy;\n\nObject.assign(SortedSet, InsertConflictResolvers);\n\nexport default SortedSet;\n\n","\nclass AbstractSortedSet {\n constructor(options) {\n if ((options != null ? options.strategy : void 0) == null) {\n throw 'Must pass options.strategy, a strategy';\n }\n if ((options != null ? options.comparator : void 0) == null) {\n throw 'Must pass options.comparator, a comparator';\n }\n if ((options != null ? options.onInsertConflict : void 0) == null) {\n throw 'Must pass options.onInsertConflict, a function';\n }\n this.priv = new options.strategy(options);\n this.length = 0;\n }\n\n insert(value) {\n this.priv.insert(value);\n this.length += 1;\n return this;\n }\n\n remove(value) {\n this.priv.remove(value);\n this.length -= 1;\n return this;\n }\n\n clear() {\n this.priv.clear();\n this.length = 0;\n return this;\n }\n\n contains(value) {\n return this.priv.contains(value);\n }\n\n // Returns this set as an Array\n toArray() {\n return this.priv.toArray();\n }\n\n forEach(callback, thisArg) {\n this.priv.forEachImpl(callback, this, thisArg);\n return this;\n }\n\n map(callback, thisArg) {\n const ret = [];\n this.forEach(function(value, index, self) {\n return ret.push(callback.call(thisArg, value, index, self));\n });\n return ret;\n }\n\n filter(callback, thisArg) {\n const ret = [];\n this.forEach(function(value, index, self) {\n if (callback.call(thisArg, value, index, self)) {\n return ret.push(value);\n }\n });\n return ret;\n }\n\n every(callback, thisArg) {\n let ret = true;\n this.forEach(function(value, index, self) {\n if (ret && !callback.call(thisArg, value, index, self)) {\n ret = false;\n }\n });\n return ret;\n }\n\n some(callback, thisArg) {\n let ret = false;\n this.forEach(function(value, index, self) {\n if (!ret && callback.call(thisArg, value, index, self)) {\n ret = true;\n }\n });\n return ret;\n }\n\n // An iterator is similar to a C++ iterator: it points _before_ a value.\n\n // So in this sorted set:\n\n // | 1 | 2 | 3 | 4 | 5 |\n // ^a ^b ^c\n\n // `a` is a pointer to the beginning of the iterator. `a.value()` returns\n // `3`. `a.previous()` returns `null`. `a.setValue()` works, if\n // `options.allowSetValue` is true.\n\n // `b` is a pointer to the value `3`. `a.previous()` and `a.next()` both do\n // the obvious.\n\n // `c` is a pointer to the `null` value. `c.previous()` works; `c.next()`\n // returns null. `c.setValue()` throws an exception, even if\n // `options.allowSetValue` is true.\n\n // Iterators have `hasNext()` and `hasPrevious()` methods, too.\n\n // Iterators are immutible. `iterator.next()` returns a new iterator.\n\n // Iterators become invalid as soon as `insert()` or `remove()` is called.\n findIterator(value) {\n return this.priv.findIterator(value);\n }\n\n // Finds an iterator pointing to the lowest possible value.\n beginIterator() {\n return this.priv.beginIterator();\n }\n\n // Finds an iterator pointing to the `null` value.\n endIterator() {\n return this.priv.endIterator();\n }\n\n};\n\nexport default AbstractSortedSet;\n\n"],"names":["Iterator","constructor","priv","index1","index","data","this","hasNext","length","hasPrevious","value","setValue","options","allowSetValue","next","previous","binarySearchForIndex","array","comparator","low","high","mid","descendAllTheWay","leftOrRight","node","parent","_iteratorParentNode","moveCursor","rightOrLeft","BinaryTreeIterator","tree1","node1","tree","root","find","nextNode","cmp","left","right","binaryTreeTraverse","callback","AbstractBinaryTree","toArray","ret","push","clear","forEachImpl","sortedSet","thisArg","i","call","contains","findIterator","beginIterator","endIterator","Node","binaryTreeDelete","nodeAllTheWay","value1","isRed","rotateLeft","h","x","rotateRight","colorFlip","moveRedLeft","insertInNode","compare","onInsertConflict","fixUp","removeMinNode","removeFromNode","moveRedRight","findMinNode","RedBlackTreeStrategy","AbstractBinaryTreeStrategy","insert","remove","InsertConflictResolvers","OnInsertConflictThrow","oldValue","newValue","Error","OnInsertConflictReplace","OnInsertConflictIgnore","SortedSet","strategy","forEach","map","self","filter","every","some","a","b","ArrayStrategy","splice","len","BinaryTreeStrategy","Object","assign"],"mappings":"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"}
--------------------------------------------------------------------------------
/src/SortedSet.js:
--------------------------------------------------------------------------------
1 | import AbstractSortedSet from './SortedSet/AbstractSortedSet';
2 | import ArrayStrategy from './SortedSet/ArrayStrategy';
3 | import BinaryTreeStrategy from './SortedSet/BinaryTreeStrategy';
4 | import RedBlackTreeStrategy from './SortedSet/RedBlackTreeStrategy';
5 | import InsertConflictResolvers from './SortedSet/InsertConflictResolvers';
6 |
7 | class SortedSet extends AbstractSortedSet {
8 | constructor(options) {
9 | options || (options = {});
10 | options.strategy || (options.strategy = RedBlackTreeStrategy);
11 | options.comparator || (options.comparator = function(a, b) {
12 | return (a || 0) - (b || 0);
13 | });
14 | options.onInsertConflict || (options.onInsertConflict = InsertConflictResolvers.OnInsertConflictThrow);
15 | super(options);
16 | }
17 | };
18 |
19 | SortedSet.ArrayStrategy = ArrayStrategy;
20 | SortedSet.BinaryTreeStrategy = BinaryTreeStrategy;
21 | SortedSet.RedBlackTreeStrategy = RedBlackTreeStrategy;
22 |
23 | Object.assign(SortedSet, InsertConflictResolvers);
24 |
25 | export default SortedSet;
26 |
27 |
--------------------------------------------------------------------------------
/src/SortedSet/AbstractBinaryTreeStrategy.js:
--------------------------------------------------------------------------------
1 |
2 | import BinaryTreeIterator from './BinaryTreeIterator';
3 |
4 | const binaryTreeTraverse = (node, callback) => {
5 | if (node !== null) {
6 | binaryTreeTraverse(node.left, callback);
7 | callback(node.value);
8 | binaryTreeTraverse(node.right, callback);
9 | }
10 | };
11 |
12 | // An AbstractBinaryTree has a @root. @root is null or an object with
13 | // `.left`, `.right` and `.value` properties.
14 | class AbstractBinaryTree {
15 | toArray() {
16 | const ret = [];
17 | binaryTreeTraverse(this.root, function(value) {
18 | return ret.push(value);
19 | });
20 | return ret;
21 | }
22 |
23 | clear() {
24 | return this.root = null;
25 | }
26 |
27 | forEachImpl(callback, sortedSet, thisArg) {
28 | let i = 0;
29 | binaryTreeTraverse(this.root, function(value) {
30 | callback.call(thisArg, value, i, sortedSet);
31 | i += 1;
32 | });
33 | }
34 |
35 | contains(value) {
36 | const comparator = this.comparator;
37 | let node = this.root;
38 | while (node !== null) {
39 | const cmp = comparator(value, node.value);
40 | if (cmp === 0) {
41 | break;
42 | } else if (cmp < 0) {
43 | node = node.left;
44 | } else {
45 | node = node.right;
46 | }
47 | }
48 | return node !== null && comparator(node.value, value) === 0;
49 | }
50 |
51 | findIterator(value) {
52 | return BinaryTreeIterator.find(this, value, this.comparator);
53 | }
54 |
55 | beginIterator() {
56 | return BinaryTreeIterator.left(this);
57 | }
58 |
59 | endIterator() {
60 | return BinaryTreeIterator.right(this);
61 | }
62 |
63 | };
64 |
65 | export default AbstractBinaryTree;
66 |
67 |
--------------------------------------------------------------------------------
/src/SortedSet/AbstractSortedSet.js:
--------------------------------------------------------------------------------
1 |
2 | class AbstractSortedSet {
3 | constructor(options) {
4 | if ((options != null ? options.strategy : void 0) == null) {
5 | throw 'Must pass options.strategy, a strategy';
6 | }
7 | if ((options != null ? options.comparator : void 0) == null) {
8 | throw 'Must pass options.comparator, a comparator';
9 | }
10 | if ((options != null ? options.onInsertConflict : void 0) == null) {
11 | throw 'Must pass options.onInsertConflict, a function';
12 | }
13 | this.priv = new options.strategy(options);
14 | this.length = 0;
15 | }
16 |
17 | insert(value) {
18 | this.priv.insert(value);
19 | this.length += 1;
20 | return this;
21 | }
22 |
23 | remove(value) {
24 | this.priv.remove(value);
25 | this.length -= 1;
26 | return this;
27 | }
28 |
29 | clear() {
30 | this.priv.clear();
31 | this.length = 0;
32 | return this;
33 | }
34 |
35 | contains(value) {
36 | return this.priv.contains(value);
37 | }
38 |
39 | // Returns this set as an Array
40 | toArray() {
41 | return this.priv.toArray();
42 | }
43 |
44 | forEach(callback, thisArg) {
45 | this.priv.forEachImpl(callback, this, thisArg);
46 | return this;
47 | }
48 |
49 | map(callback, thisArg) {
50 | const ret = [];
51 | this.forEach(function(value, index, self) {
52 | return ret.push(callback.call(thisArg, value, index, self));
53 | });
54 | return ret;
55 | }
56 |
57 | filter(callback, thisArg) {
58 | const ret = [];
59 | this.forEach(function(value, index, self) {
60 | if (callback.call(thisArg, value, index, self)) {
61 | return ret.push(value);
62 | }
63 | });
64 | return ret;
65 | }
66 |
67 | every(callback, thisArg) {
68 | let ret = true;
69 | this.forEach(function(value, index, self) {
70 | if (ret && !callback.call(thisArg, value, index, self)) {
71 | ret = false;
72 | }
73 | });
74 | return ret;
75 | }
76 |
77 | some(callback, thisArg) {
78 | let ret = false;
79 | this.forEach(function(value, index, self) {
80 | if (!ret && callback.call(thisArg, value, index, self)) {
81 | ret = true;
82 | }
83 | });
84 | return ret;
85 | }
86 |
87 | // An iterator is similar to a C++ iterator: it points _before_ a value.
88 |
89 | // So in this sorted set:
90 |
91 | // | 1 | 2 | 3 | 4 | 5 |
92 | // ^a ^b ^c
93 |
94 | // `a` is a pointer to the beginning of the iterator. `a.value()` returns
95 | // `3`. `a.previous()` returns `null`. `a.setValue()` works, if
96 | // `options.allowSetValue` is true.
97 |
98 | // `b` is a pointer to the value `3`. `a.previous()` and `a.next()` both do
99 | // the obvious.
100 |
101 | // `c` is a pointer to the `null` value. `c.previous()` works; `c.next()`
102 | // returns null. `c.setValue()` throws an exception, even if
103 | // `options.allowSetValue` is true.
104 |
105 | // Iterators have `hasNext()` and `hasPrevious()` methods, too.
106 |
107 | // Iterators are immutible. `iterator.next()` returns a new iterator.
108 |
109 | // Iterators become invalid as soon as `insert()` or `remove()` is called.
110 | findIterator(value) {
111 | return this.priv.findIterator(value);
112 | }
113 |
114 | // Finds an iterator pointing to the lowest possible value.
115 | beginIterator() {
116 | return this.priv.beginIterator();
117 | }
118 |
119 | // Finds an iterator pointing to the `null` value.
120 | endIterator() {
121 | return this.priv.endIterator();
122 | }
123 |
124 | };
125 |
126 | export default AbstractSortedSet;
127 |
128 |
--------------------------------------------------------------------------------
/src/SortedSet/ArrayStrategy.js:
--------------------------------------------------------------------------------
1 |
2 | class Iterator {
3 | constructor(priv, index1) {
4 | this.priv = priv;
5 | this.index = index1;
6 | this.data = this.priv.data;
7 | }
8 |
9 | hasNext() {
10 | return this.index < this.data.length;
11 | }
12 |
13 | hasPrevious() {
14 | return this.index > 0;
15 | }
16 |
17 | value() {
18 | if (this.index < this.data.length) {
19 | return this.data[this.index];
20 | } else {
21 | return null;
22 | }
23 | }
24 |
25 | setValue(value) {
26 | if (!this.priv.options.allowSetValue) {
27 | throw 'Must set options.allowSetValue';
28 | }
29 | if (!this.hasNext()) {
30 | throw 'Cannot set value at end of set';
31 | }
32 | return this.data[this.index] = value;
33 | }
34 |
35 | next() {
36 | if (this.index >= this.data.length) {
37 | return null;
38 | } else {
39 | return new Iterator(this.priv, this.index + 1);
40 | }
41 | }
42 |
43 | previous() {
44 | if (this.index <= 0) {
45 | return null;
46 | } else {
47 | return new Iterator(this.priv, this.index - 1);
48 | }
49 | }
50 |
51 | };
52 |
53 | const binarySearchForIndex = (array, value, comparator) => {
54 | let low = 0;
55 | let high = array.length;
56 | while (low < high) {
57 | const mid = (low + high) >>> 1;
58 | if (comparator(array[mid], value) < 0) {
59 | low = mid + 1;
60 | } else {
61 | high = mid;
62 | }
63 | }
64 | return low;
65 | };
66 |
67 | class ArrayStrategy {
68 | constructor(options) {
69 | this.options = options;
70 | this.onInsertConflict = this.options.onInsertConflict;
71 | this.comparator = this.options.comparator;
72 | this.data = [];
73 | }
74 |
75 | toArray() {
76 | return this.data;
77 | }
78 |
79 | insert(value) {
80 | const index = binarySearchForIndex(this.data, value, this.comparator);
81 | if (this.data[index] !== void 0 && this.comparator(this.data[index], value) === 0) {
82 | return this.data.splice(index, 1, this.onInsertConflict(this.data[index], value));
83 | } else {
84 | return this.data.splice(index, 0, value);
85 | }
86 | }
87 |
88 | remove(value) {
89 | const index = binarySearchForIndex(this.data, value, this.comparator);
90 | if (this.comparator(this.data[index], value) !== 0) {
91 | throw 'Value not in set';
92 | }
93 | return this.data.splice(index, 1);
94 | }
95 |
96 | clear() {
97 | return this.data.length = 0;
98 | }
99 |
100 | contains(value) {
101 | const index = binarySearchForIndex(this.data, value, this.comparator);
102 | return this.index !== this.data.length && this.comparator(this.data[index], value) === 0;
103 | }
104 |
105 | forEachImpl(callback, sortedSet, thisArg) {
106 | const data = this.data;
107 | const len = data.length;
108 | for (let i = 0; i < len; i++) {
109 | callback.call(thisArg, data[i], i, sortedSet);
110 | }
111 | }
112 |
113 | findIterator(value) {
114 | const index = binarySearchForIndex(this.data, value, this.comparator);
115 | return new Iterator(this, index);
116 | }
117 |
118 | beginIterator() {
119 | return new Iterator(this, 0);
120 | }
121 |
122 | endIterator() {
123 | return new Iterator(this, this.data.length);
124 | }
125 | };
126 |
127 | export default ArrayStrategy;
128 |
--------------------------------------------------------------------------------
/src/SortedSet/BinaryTreeIterator.js:
--------------------------------------------------------------------------------
1 | const descendAllTheWay = (leftOrRight, node) => {
2 | // Assumes node._iteratorParentNode is set
3 | while (node[leftOrRight] !== null) {
4 | const parent = node;
5 | node = node[leftOrRight];
6 | node._iteratorParentNode = parent;
7 | }
8 | return node;
9 | };
10 |
11 | const moveCursor = (leftOrRight, node) => {
12 | let parent, rightOrLeft;
13 | if (node[leftOrRight] !== null) {
14 | parent = node;
15 | node = node[leftOrRight];
16 | node._iteratorParentNode = parent;
17 | rightOrLeft = leftOrRight === 'left' ? 'right' : 'left';
18 | node = descendAllTheWay(rightOrLeft, node);
19 | } else {
20 | while ((parent = node._iteratorParentNode) !== null && parent[leftOrRight] === node) {
21 | node = parent;
22 | }
23 | node = parent; // either null or the correct-direction parent
24 | }
25 | return node;
26 | };
27 |
28 | // The BinaryTreeIterator actually writes to the tree: it maintains a
29 | // "_iteratorParentNode" variable on each node. Please ignore this.
30 | class BinaryTreeIterator {
31 | constructor(tree1, node1) {
32 | this.tree = tree1;
33 | this.node = node1;
34 | }
35 |
36 | next() {
37 | if (this.node === null) {
38 | return null;
39 | } else {
40 | const node = moveCursor('right', this.node);
41 | return new BinaryTreeIterator(this.tree, node);
42 | }
43 | }
44 |
45 | previous() {
46 | if (this.node === null) {
47 | if (this.tree.root === null) {
48 | return null;
49 | } else {
50 | this.tree.root._iteratorParentNode = null;
51 | const node = descendAllTheWay('right', this.tree.root);
52 | return new BinaryTreeIterator(this.tree, node);
53 | }
54 | } else {
55 | const node = moveCursor('left', this.node);
56 | if (node === null) {
57 | return null;
58 | } else {
59 | return new BinaryTreeIterator(this.tree, node);
60 | }
61 | }
62 | }
63 |
64 | hasNext() {
65 | return this.node !== null;
66 | }
67 |
68 | hasPrevious() {
69 | return this.previous() !== null;
70 | }
71 |
72 | value() {
73 | if (this.node === null) {
74 | return null;
75 | } else {
76 | return this.node.value;
77 | }
78 | }
79 |
80 | setValue(value) {
81 | if (!this.tree.options.allowSetValue) {
82 | throw 'Must set options.allowSetValue';
83 | }
84 | if (!this.hasNext()) {
85 | throw 'Cannot set value at end of set';
86 | }
87 | return this.node.value = value;
88 | }
89 |
90 | };
91 |
92 | BinaryTreeIterator.find = function(tree, value, comparator) {
93 | const root = tree.root;
94 | if (root != null) {
95 | root._iteratorParentNode = null;
96 | }
97 | let node = root;
98 | let nextNode = null; // For finding an in-between node
99 | while (node !== null) {
100 | const cmp = comparator(value, node.value);
101 | if (cmp === 0) {
102 | break;
103 | } else if (cmp < 0) {
104 | if (node.left === null) {
105 | break;
106 | }
107 | nextNode = node; // If we descend all right after this until there are
108 | // no more right nodes, we want to return an
109 | // "in-between" iterator ... pointing here.
110 | node.left._iteratorParentNode = node;
111 | node = node.left;
112 | } else {
113 | if (node.right !== null) {
114 | node.right._iteratorParentNode = node;
115 | node = node.right;
116 | } else {
117 | node = nextNode;
118 | break;
119 | }
120 | }
121 | }
122 | return new BinaryTreeIterator(tree, node);
123 | };
124 |
125 | BinaryTreeIterator.left = (tree) => {
126 | if (tree.root === null) {
127 | return new BinaryTreeIterator(tree, null);
128 | } else {
129 | tree.root._iteratorParentNode = null;
130 | const node = descendAllTheWay('left', tree.root);
131 | return new BinaryTreeIterator(tree, node);
132 | }
133 | };
134 |
135 | BinaryTreeIterator.right = (tree) => {
136 | return new BinaryTreeIterator(tree, null);
137 | };
138 |
139 | export default BinaryTreeIterator;
140 |
--------------------------------------------------------------------------------
/src/SortedSet/BinaryTreeStrategy.js:
--------------------------------------------------------------------------------
1 | import AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';
2 |
3 | class Node {
4 | constructor(value) {
5 | this.value = value;
6 | this.left = null;
7 | this.right = null;
8 | }
9 | };
10 |
11 | const nodeAllTheWay = (node, leftOrRight) => {
12 | while (node[leftOrRight] !== null) {
13 | node = node[leftOrRight];
14 | }
15 | return node;
16 | };
17 |
18 | // Returns the subtree, minus value
19 | const binaryTreeDelete = (node, value, comparator) => {
20 | if (node === null) {
21 | throw 'Value not in set';
22 | }
23 | const cmp = comparator(value, node.value);
24 | if (cmp < 0) {
25 | node.left = binaryTreeDelete(node.left, value, comparator);
26 | } else if (cmp > 0) {
27 | node.right = binaryTreeDelete(node.right, value, comparator); // This is the value we want to remove
28 | } else {
29 | if (node.left === null && node.right === null) {
30 | node = null;
31 | } else if (node.right === null) {
32 | node = node.left;
33 | } else if (node.left === null) {
34 | node = node.right;
35 | } else {
36 | const nextNode = nodeAllTheWay(node.right, 'left');
37 | node.value = nextNode.value;
38 | node.right = binaryTreeDelete(node.right, nextNode.value, comparator);
39 | }
40 | }
41 | return node;
42 | };
43 |
44 | class BinaryTreeStrategy extends AbstractBinaryTreeStrategy {
45 | constructor(options) {
46 | super();
47 | this.options = options;
48 | this.comparator = this.options.comparator;
49 | this.onInsertConflict = this.options.onInsertConflict;
50 | this.root = null;
51 | }
52 |
53 | insert(value) {
54 | const compare = this.comparator;
55 | if (this.root !== null) {
56 | let parent = this.root;
57 | let leftOrRight = null;
58 | while (true) {
59 | const cmp = compare(value, parent.value);
60 | if (cmp === 0) {
61 | parent.value = this.onInsertConflict(parent.value, value);
62 | return;
63 | } else {
64 | leftOrRight = cmp < 0 ? 'left' : 'right';
65 | if (parent[leftOrRight] === null) {
66 | break;
67 | }
68 | parent = parent[leftOrRight];
69 | }
70 | }
71 | return parent[leftOrRight] = new Node(value);
72 | } else {
73 | return this.root = new Node(value);
74 | }
75 | }
76 |
77 | remove(value) {
78 | return this.root = binaryTreeDelete(this.root, value, this.comparator);
79 | }
80 |
81 | };
82 |
83 | export default BinaryTreeStrategy;
84 |
--------------------------------------------------------------------------------
/src/SortedSet/InsertConflictResolvers.js:
--------------------------------------------------------------------------------
1 | const InsertConflictResolvers = {
2 | OnInsertConflictThrow: (oldValue, newValue) => { throw new Error("Value already in set") },
3 | OnInsertConflictReplace: (oldValue, newValue) => newValue,
4 | OnInsertConflictIgnore: (oldValue, newValue) => oldValue,
5 | };
6 | export default InsertConflictResolvers;
7 |
--------------------------------------------------------------------------------
/src/SortedSet/RedBlackTreeStrategy.js:
--------------------------------------------------------------------------------
1 |
2 | import AbstractBinaryTreeStrategy from './AbstractBinaryTreeStrategy';
3 |
4 | // An implementation of Left-Leaning Red-Black trees.
5 |
6 | // It's copied from http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf.
7 | // It's practically a copy-paste job, minus the semicolons. missing bits were
8 | // filled in with hints from
9 | // http://www.teachsolaisgames.com/articles/balanced_left_leaning.html
10 |
11 | // Here are some differences:
12 | // * This isn't a map structure: it's just a tree. There are no keys: the
13 | // comparator applies to the values.
14 | // * We use the passed comparator.
15 | class Node {
16 | constructor(value1) {
17 | this.value = value1;
18 | this.left = null;
19 | this.right = null;
20 | this.isRed = true; // null nodes -- leaves -- are black
21 | }
22 |
23 | };
24 |
25 | const rotateLeft = (h) => {
26 | const x = h.right;
27 | h.right = x.left;
28 | x.left = h;
29 | x.isRed = h.isRed;
30 | h.isRed = true;
31 | return x;
32 | };
33 |
34 | const rotateRight = (h) => {
35 | const x = h.left;
36 | h.left = x.right;
37 | x.right = h;
38 | x.isRed = h.isRed;
39 | h.isRed = true;
40 | return x;
41 | };
42 |
43 | const colorFlip = (h) => {
44 | h.isRed = !h.isRed;
45 | h.left.isRed = !h.left.isRed;
46 | h.right.isRed = !h.right.isRed;
47 | };
48 |
49 | const moveRedLeft = (h) => {
50 | //throw 'Preconditions failed' if !(!h.left.isRed && !h.left.left?.isRed)
51 | colorFlip(h);
52 | if (h.right !== null && h.right.left !== null && h.right.left.isRed) {
53 | h.right = rotateRight(h.right);
54 | h = rotateLeft(h);
55 | colorFlip(h);
56 | }
57 | return h;
58 | };
59 |
60 | const moveRedRight = (h) => {
61 | //throw 'Preconditions failed' if !(!h.right.isRed && !h.right.left?.isRed)
62 | colorFlip(h);
63 | if (h.left !== null && h.left.left !== null && h.left.left.isRed) {
64 | h = rotateRight(h);
65 | colorFlip(h);
66 | }
67 | return h;
68 | };
69 |
70 | const insertInNode = (h, value, compare, onInsertConflict) => {
71 | if (h === null) {
72 | return new Node(value);
73 | }
74 | //if h.left isnt null && h.left.isRed && h.right isnt null && h.right.isRed
75 | // colorFlip(h)
76 | const cmp = compare(value, h.value);
77 | if (cmp === 0) {
78 | h.value = onInsertConflict(h.value, value);
79 | } else if (cmp < 0) {
80 | h.left = insertInNode(h.left, value, compare, onInsertConflict);
81 | } else {
82 | h.right = insertInNode(h.right, value, compare, onInsertConflict);
83 | }
84 | if (h.right !== null && h.right.isRed && !(h.left !== null && h.left.isRed)) {
85 | h = rotateLeft(h);
86 | }
87 | if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {
88 | h = rotateRight(h);
89 | }
90 | // Put this here -- I couldn't get the whole thing to work otherwise :(
91 | if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {
92 | colorFlip(h);
93 | }
94 | return h;
95 | };
96 |
97 | const findMinNode = (h) => {
98 | while (h.left !== null) {
99 | h = h.left;
100 | }
101 | return h;
102 | };
103 |
104 | const fixUp = (h) => {
105 | // Fix right-leaning red nodes
106 | if (h.right !== null && h.right.isRed) {
107 | h = rotateLeft(h);
108 | }
109 | // Handle a 4-node that traverses down the left
110 | if (h.left !== null && h.left.isRed && h.left.left !== null && h.left.left.isRed) {
111 | h = rotateRight(h);
112 | }
113 | // split 4-nodes
114 | if (h.left !== null && h.left.isRed && h.right !== null && h.right.isRed) {
115 | colorFlip(h);
116 | }
117 | return h;
118 | };
119 |
120 | const removeMinNode = (h) => {
121 | if (h.left === null) {
122 | return null;
123 | }
124 | if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {
125 | h = moveRedLeft(h);
126 | }
127 | h.left = removeMinNode(h.left);
128 | return fixUp(h);
129 | };
130 |
131 | const removeFromNode = (h, value, compare) => {
132 | if (h === null) {
133 | throw 'Value not in set';
134 | }
135 | if (compare(value, h.value) < 0) {
136 | if (h.left === null) {
137 | throw 'Value not in set';
138 | }
139 | if (!h.left.isRed && !(h.left.left !== null && h.left.left.isRed)) {
140 | h = moveRedLeft(h);
141 | }
142 | h.left = removeFromNode(h.left, value, compare);
143 | } else {
144 | if (h.left !== null && h.left.isRed) {
145 | h = rotateRight(h);
146 | }
147 | if (h.right === null) {
148 | if (compare(value, h.value) === 0) {
149 | return null; // leaf node; LLRB assures no left value here
150 | } else {
151 | throw 'Value not in set';
152 | }
153 | }
154 | if (!h.right.isRed && !(h.right.left !== null && h.right.left.isRed)) {
155 | h = moveRedRight(h);
156 | }
157 | if (compare(value, h.value) === 0) {
158 | h.value = findMinNode(h.right).value;
159 | h.right = removeMinNode(h.right);
160 | } else {
161 | h.right = removeFromNode(h.right, value, compare);
162 | }
163 | }
164 | if (h !== null) {
165 | h = fixUp(h);
166 | }
167 | return h;
168 | };
169 |
170 | class RedBlackTreeStrategy extends AbstractBinaryTreeStrategy {
171 | constructor(options) {
172 | super();
173 | this.options = options;
174 | this.comparator = this.options.comparator;
175 | this.onInsertConflict = this.options.onInsertConflict;
176 | this.root = null;
177 | }
178 |
179 | insert(value) {
180 | this.root = insertInNode(this.root, value, this.comparator, this.onInsertConflict);
181 | this.root.isRed = false; // always
182 | }
183 |
184 | remove(value) {
185 | this.root = removeFromNode(this.root, value, this.comparator);
186 | if (this.root !== null) {
187 | this.root.isRed = false;
188 | }
189 | }
190 |
191 | };
192 |
193 | export default RedBlackTreeStrategy;
194 |
--------------------------------------------------------------------------------
/test/SortedSet/AbstractSortedSetSpec.js:
--------------------------------------------------------------------------------
1 |
2 | require('../test_helper');
3 | const SortedSet = require('../../lib/SortedSet');
4 | const AbstractSortedSet = require('../../lib/SortedSet/AbstractSortedSet');
5 |
6 | const numberCompare = function(a, b) {
7 | return a - b;
8 | };
9 |
10 | describe('AbstractSortedSet', function() {
11 | it('should throw if there is no strategy', function() {
12 | return expect(function() {
13 | return new AbstractSortedSet({
14 | comparator: numberCompare
15 | });
16 | }).to.throw();
17 | });
18 | it('should throw if there is no comparator', function() {
19 | return expect(function() {
20 | return new AbstractSortedSet({
21 | strategy: (function() {})
22 | });
23 | }).to.throw();
24 | });
25 | return describe('with a set', function() {
26 | var MockStrategy, set, strategy;
27 | strategy = null;
28 | set = null;
29 | MockStrategy = (function() {
30 | class MockStrategy {
31 | constructor(options) {
32 | this.options = options;
33 | strategy = this;
34 | }
35 |
36 | };
37 |
38 | MockStrategy.prototype.insert = sinon.spy();
39 |
40 | MockStrategy.prototype.remove = sinon.spy();
41 |
42 | MockStrategy.prototype.clear = sinon.spy();
43 |
44 | MockStrategy.prototype.toArray = sinon.stub().returns([]);
45 |
46 | MockStrategy.prototype.forEachImpl = sinon.stub();
47 |
48 | MockStrategy.prototype.findIterator = sinon.stub();
49 |
50 | MockStrategy.prototype.beginIterator = sinon.stub();
51 |
52 | MockStrategy.prototype.endIterator = sinon.stub();
53 |
54 | return MockStrategy;
55 |
56 | }).call(this);
57 | beforeEach(function() {
58 | return set = new AbstractSortedSet({
59 | comparator: numberCompare,
60 | strategy: MockStrategy,
61 | onInsertConflict: SortedSet.OnInsertConflictThrow,
62 | });
63 | });
64 | it('should pass the options to the strategy', function() {
65 | return expect(strategy.options.comparator).to.eq(numberCompare);
66 | });
67 | it('should start with length 0', function() {
68 | return expect(set.length).to.eq(0);
69 | });
70 | it('should call strategy.insert', function() {
71 | set.insert(1);
72 | return expect(strategy.insert).to.have.been.calledWith(1);
73 | });
74 | it('should increment length on insert', function() {
75 | set.insert(1);
76 | return expect(set.length).to.eq(1);
77 | });
78 | it('should call strategy.remove', function() {
79 | set.remove(1);
80 | return expect(strategy.remove).to.have.been.calledWith(1);
81 | });
82 | it('should decrement length on remove', function() {
83 | set.insert(1);
84 | set.remove(1);
85 | return expect(set.length).to.eq(0);
86 | });
87 | it('should call strategy.clear', function() {
88 | set.clear();
89 | return expect(strategy.clear).to.have.been.called;
90 | });
91 | it('should set length=0 on clear', function() {
92 | set.insert(1);
93 | set.clear();
94 | return expect(set.length).to.eq(0);
95 | });
96 | it('should call toArray', function() {
97 | strategy.toArray.returns([1, 2, 3]);
98 | return expect(set.toArray()).to.deep.eq([1, 2, 3]);
99 | });
100 | it('should call findIterator with the value', function() {
101 | var expected, ret;
102 | expected = {};
103 | strategy.findIterator.returns(expected);
104 | ret = set.findIterator(expected);
105 | expect(strategy.findIterator).to.have.been.calledWith(expected);
106 | return expect(ret).to.eq(expected);
107 | });
108 | it('should call beginIterator', function() {
109 | var expected, ret;
110 | expected = {};
111 | strategy.beginIterator.returns(expected);
112 | ret = set.beginIterator();
113 | expect(strategy.beginIterator).to.have.been.called;
114 | return expect(ret).to.eq(expected);
115 | });
116 | it('should call endIterator', function() {
117 | var expected, ret;
118 | expected = {};
119 | strategy.endIterator.returns(expected);
120 | ret = set.endIterator();
121 | expect(strategy.endIterator).to.have.been.called;
122 | return expect(ret).to.eq(expected);
123 | });
124 | return describe('with forEachImpl calling a callback three times', function() {
125 | beforeEach(function() {
126 | return strategy.forEachImpl = function(callback, selfArg, thisArg) {
127 | callback.call(thisArg, 1, 0, selfArg);
128 | callback.call(thisArg, 2, 1, selfArg);
129 | return callback.call(thisArg, 3, 2, selfArg);
130 | };
131 | });
132 | describe('forEach', function() {
133 | it('should work with no extra arguments', function() {
134 | var spy;
135 | spy = sinon.spy();
136 | set.forEach(spy);
137 | expect(spy).to.have.callCount(3);
138 | expect(spy.thisValues[0]).to.be.undefined;
139 | expect(spy.args[0][0]).to.eq(1);
140 | expect(spy.args[0][1]).to.eq(0);
141 | expect(spy.args[0][2]).to.eq(set);
142 | expect(spy.args[1][1]).to.eq(1);
143 | return expect(spy.args[2][1]).to.eq(2);
144 | });
145 | return it('should set thisArg', function() {
146 | var callback, thisArg;
147 | callback = sinon.spy();
148 | thisArg = {};
149 | set.forEach(callback, thisArg);
150 | return expect(callback.thisValues[0]).to.eq(thisArg);
151 | });
152 | });
153 | describe('map', function() {
154 | return it('should map', function() {
155 | var ret;
156 | ret = set.map(function(value) {
157 | return value * 2;
158 | });
159 | return expect(ret).to.deep.eq([2, 4, 6]);
160 | });
161 | });
162 | describe('filter', function() {
163 | return it('should filter', function() {
164 | var ret;
165 | ret = set.filter(function(value) {
166 | return value !== 2;
167 | });
168 | return expect(ret).to.deep.eq([1, 3]);
169 | });
170 | });
171 | describe('every', function() {
172 | it('should return true', function() {
173 | var ret;
174 | ret = set.every(function(value) {
175 | return value > 0;
176 | });
177 | return expect(ret).to.eq(true);
178 | });
179 | return it('should return false', function() {
180 | var ret;
181 | ret = set.every(function(value) {
182 | return value > 1;
183 | });
184 | return expect(ret).to.eq(false);
185 | });
186 | });
187 | return describe('some', function() {
188 | it('should return true', function() {
189 | var ret;
190 | ret = set.some(function(value) {
191 | return value > 2;
192 | });
193 | return expect(ret).to.eq(true);
194 | });
195 | return it('should return false', function() {
196 | var ret;
197 | ret = set.some(function(value) {
198 | return value > 3;
199 | });
200 | return expect(ret).to.eq(false);
201 | });
202 | });
203 | });
204 | });
205 | });
206 |
207 |
--------------------------------------------------------------------------------
/test/SortedSet/ArrayStrategySpec.js:
--------------------------------------------------------------------------------
1 |
2 | require('../test_helper');
3 | const ArrayStrategy = require('../../lib/SortedSet/ArrayStrategy');
4 | const StrategyHelper = require('../helpers/StrategyHelper');
5 |
6 | StrategyHelper.describeStrategy('Array-based strategy', ArrayStrategy);
7 |
--------------------------------------------------------------------------------
/test/SortedSet/BinaryTreeStrategySpec.js:
--------------------------------------------------------------------------------
1 |
2 | require( '../test_helper');
3 | const BinaryTreeStrategy = require('../../lib/SortedSet/BinaryTreeStrategy');
4 | const StrategyHelper = require('../helpers/StrategyHelper');
5 |
6 | StrategyHelper.describeStrategy('Binary tree-based strategy', BinaryTreeStrategy);
7 |
--------------------------------------------------------------------------------
/test/SortedSet/RedBlackTreeStrategySpec.js:
--------------------------------------------------------------------------------
1 |
2 | require('../test_helper');
3 | const RedBlackTreeStrategy = require('../../lib/SortedSet/RedBlackTreeStrategy');
4 | const StrategyHelper = require('../helpers/StrategyHelper');
5 |
6 | StrategyHelper.describeStrategy('Left-leaning red-black tree-based strategy', RedBlackTreeStrategy);
7 |
--------------------------------------------------------------------------------
/test/SortedSetSpec.js:
--------------------------------------------------------------------------------
1 | require('./test_helper');
2 | const SortedSet = require('../lib/SortedSet');
3 |
4 | const numberCompare = (a, b) => {
5 | return a - b;
6 | };
7 |
8 | describe('SortedSet', function() {
9 | it('should have a RedBlackTreeStrategy', function() {
10 | return expect(SortedSet.RedBlackTreeStrategy).to.exist;
11 | });
12 | it('should have a BinaryTreeStrategy', function() {
13 | return expect(SortedSet.BinaryTreeStrategy).to.exist;
14 | });
15 | it('should have an ArrayStrategy', function() {
16 | return expect(SortedSet.ArrayStrategy).to.exist;
17 | });
18 | it('should default to RedBlackTreeStrategy', function() {
19 | var set;
20 | set = new SortedSet({
21 | comparator: numberCompare
22 | });
23 | return expect(set.priv.constructor).to.eq(SortedSet.RedBlackTreeStrategy);
24 | });
25 | return it('should set a default comparator', function() {
26 | var set;
27 | set = new SortedSet({
28 | strategy: SortedSet.RedBlackTreeStrategy
29 | });
30 | return expect(set.priv.comparator(2, 3)).to.eq(-1);
31 | });
32 | });
33 |
34 | describe('integration tests', function() {
35 | var set;
36 | set = void 0;
37 | beforeEach(function() {
38 | return set = new SortedSet();
39 | });
40 | it('should stay sorted', function() {
41 | set.insert(1);
42 | set.insert(3);
43 | set.insert(2);
44 | return expect(set.toArray()).to.deep.eq([1, 2, 3]);
45 | });
46 | it('should remove', function() {
47 | set.insert(1);
48 | set.insert(2);
49 | set.remove(2);
50 | return expect(set.toArray()).to.deep.eq([1]);
51 | });
52 | it('should map', function() {
53 | set.insert(1);
54 | set.insert(2);
55 | return expect(set.map(function(v) {
56 | return v * 2;
57 | })).to.deep.eq([2, 4]);
58 | });
59 | return it('should iterate', function() {
60 | var iterator;
61 | set.insert(1);
62 | set.insert(2);
63 | iterator = set.beginIterator();
64 | expect(iterator.value()).to.eq(1);
65 | iterator = iterator.next();
66 | expect(iterator.value()).to.eq(2);
67 | iterator = iterator.next();
68 | expect(iterator.value()).to.eq(null);
69 | iterator = iterator.next();
70 | return expect(iterator).to.eq(null);
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/test/helpers/StrategyHelper.js:
--------------------------------------------------------------------------------
1 | const SortedSet = require('../../lib/SortedSet')
2 |
3 | const numberComparator = (a, b) => {
4 | return a - b
5 | }
6 |
7 | const describeStrategy = (description, strategy) => {
8 | describe(description, function () {
9 | let priv
10 |
11 | describe('starting empty', function () {
12 | beforeEach(function () {
13 | priv = new strategy({
14 | comparator: numberComparator
15 | })
16 | })
17 |
18 | it('should not contain a value', function () {
19 | expect(priv.contains(2)).to.eq(false)
20 | })
21 |
22 | it('should store its data in an array for easy testing', function () {
23 | expect(priv.toArray()).to.deep.eq([])
24 | })
25 |
26 | it('should insert an element', function () {
27 | priv.insert(4)
28 | expect(priv.toArray()).to.deep.eq([4])
29 | })
30 |
31 | it('should fail to remove an element', function () {
32 | expect(function () {
33 | priv.remove(4)
34 | }).to.throw('Value not in set')
35 | })
36 |
37 | it('should return an iterator with no next or previous', function () {
38 | const iterator = priv.findIterator(4)
39 | expect(iterator.hasNext()).to.eq(false)
40 | expect(iterator.hasPrevious()).to.eq(false)
41 | expect(iterator.next()).to.eq(null)
42 | expect(iterator.previous()).to.eq(null)
43 | expect(iterator.value()).to.eq(null)
44 | })
45 |
46 | it('should return a beginIterator', function () {
47 | const iterator = priv.beginIterator()
48 | expect(iterator.value()).to.eq(null)
49 | })
50 |
51 | it('should return an endIterator', function () {
52 | const iterator = priv.endIterator()
53 | expect(iterator.value()).to.eq(null)
54 | })
55 |
56 | it('should do nothing in forEachImpl()', function () {
57 | const callback = sinon.spy()
58 | priv.forEachImpl(callback)
59 | expect(callback).not.to.have.been.called
60 | })
61 | })
62 |
63 | describe('with some numbers', function () {
64 | beforeEach(function () {
65 | priv = new strategy({
66 | comparator: numberComparator
67 | })
68 | // Insert in this order so binary tree isn't one-sided
69 | priv.insert(2)
70 | priv.insert(1)
71 | priv.insert(3)
72 | })
73 |
74 | it('should insert at the beginning', function () {
75 | priv.insert(0)
76 | expect(priv.toArray()).to.deep.eq([0, 1, 2, 3])
77 | })
78 |
79 | it('should insert in the middle', function () {
80 | priv.insert(2.5)
81 | expect(priv.toArray()).to.deep.eq([1, 2, 2.5, 3])
82 | })
83 |
84 | it('should insert at the end', function () {
85 | priv.insert(4)
86 | expect(priv.toArray()).to.deep.eq([1, 2, 3, 4])
87 | })
88 |
89 | it('should remove from the beginning', function () {
90 | priv.remove(1)
91 | expect(priv.toArray()).to.deep.eq([2, 3])
92 | })
93 |
94 | it('should remove from the end', function () {
95 | priv.remove(3)
96 | expect(priv.toArray()).to.deep.eq([1, 2])
97 | })
98 |
99 | it('should remove from the middle', function () {
100 | priv.remove(2)
101 | expect(priv.toArray()).to.deep.eq([1, 3])
102 | })
103 |
104 | it('should clear', function () {
105 | priv.clear()
106 | expect(priv.toArray()).to.deep.eq([])
107 | })
108 |
109 | it('should allow insert after clear', function () {
110 | priv.clear()
111 | priv.insert(4)
112 | priv.insert(2)
113 | expect(priv.toArray()).to.deep.eq([2, 4])
114 | })
115 |
116 | it('should contain the first value', function () {
117 | expect(priv.contains(1)).to.eq(true)
118 | })
119 |
120 | it('should contain the last value', function () {
121 | expect(priv.contains(3)).to.eq(true)
122 | })
123 |
124 | it('should contain a middle value', function () {
125 | expect(priv.contains(2)).to.eq(true)
126 | })
127 |
128 | it('should not contain a value below the lowest', function () {
129 | expect(priv.contains(0)).to.eq(false)
130 | })
131 |
132 | it('should not contain a value above the highest', function () {
133 | expect(priv.contains(4)).to.eq(false)
134 | })
135 |
136 | it('should not contain a value in between two values', function () {
137 | expect(priv.contains(1.5)).to.eq(false)
138 | })
139 |
140 | it('should return false from contain', function () {
141 | expect(priv.contains(4)).to.eq(false)
142 | })
143 |
144 | it('should return a begin iterator', function () {
145 | const iterator = priv.beginIterator()
146 | expect(iterator.previous()).to.eq(null)
147 | expect(iterator.value()).to.eq(1)
148 | })
149 |
150 | it('should return an end iterator', function () {
151 | const iterator = priv.endIterator()
152 | expect(iterator.next()).to.eq(null)
153 | expect(iterator.value()).to.eq(null)
154 | })
155 |
156 | it('should find an iterator', function () {
157 | const iterator = priv.findIterator(2)
158 | expect(iterator.value()).to.eq(2)
159 | })
160 |
161 | it('should find an iterator between values', function () {
162 | const iterator = priv.findIterator(1.5)
163 | expect(iterator.value()).to.eq(2)
164 | })
165 |
166 | it('should find an iterator with a value above the max', function () {
167 | const iterator = priv.findIterator(3.5)
168 | expect(iterator.value()).to.eq(null)
169 | })
170 |
171 | it('should find an iterator with a value below the min', function () {
172 | const iterator = priv.findIterator(0.5)
173 | expect(iterator.value()).to.eq(1)
174 | })
175 |
176 | it('should find a previous iterator', function () {
177 | const iterator = priv.findIterator(2).previous()
178 | expect(iterator.value()).to.eq(1)
179 | })
180 |
181 | it('should find a next iterator', function () {
182 | const iterator = priv.findIterator(2).next()
183 | expect(iterator.value()).to.eq(3)
184 | })
185 |
186 | it('should step to previous from the end iterator', function () {
187 | const iterator = priv.endIterator().previous()
188 | expect(iterator.value()).to.eq(3)
189 | })
190 |
191 | it('should step to end from a previous iterator', function () {
192 | const iterator = priv.findIterator(3).next()
193 | expect(iterator.value()).to.eq(null)
194 | })
195 |
196 | it('should fail to setValue()', function () {
197 | const iterator = priv.findIterator(2)
198 | expect(function () {
199 | iterator.setValue(2.5)
200 | }).to.throw()
201 | })
202 |
203 | it('should iterate in forEachImpl', function () {
204 | const set = 'foo'
205 | const thisArg = 'moo'
206 | const spy = sinon.spy()
207 | priv.forEachImpl(spy, set, thisArg)
208 | expect(spy).to.have.callCount(3)
209 | expect(spy.thisValues[0]).to.eq(thisArg)
210 | expect(spy.args[0]).to.deep.eq([1, 0, set])
211 | expect(spy.args[1]).to.deep.eq([2, 1, set])
212 | expect(spy.args[2]).to.deep.eq([3, 2, set])
213 | })
214 | })
215 |
216 | describe('with objects for which cmp(a, b) === 0 and a !== b', function () {
217 | beforeEach(function () {
218 | priv = new strategy({
219 | comparator: (a, b) => a.id - b.id
220 | })
221 | // Insert in this order so binary tree isn't one-sided
222 | priv.insert({ id: 2 })
223 | priv.insert({ id: 1 })
224 | priv.insert({ id: 3 })
225 | })
226 |
227 | it('should insert in the middle', function () {
228 | priv.insert({ id: 2.5 })
229 | expect(priv.toArray()).to.deep.eq([{ id: 1 }, { id: 2 }, { id: 2.5 }, { id: 3 }])
230 | })
231 |
232 | it('should remove from the beginning', function () {
233 | priv.remove({ id: 1 })
234 | expect(priv.toArray()).to.deep.eq([{ id: 2 }, { id: 3 }])
235 | })
236 |
237 | it('should remove from the end', function () {
238 | priv.remove({ id: 3 })
239 | expect(priv.toArray()).to.deep.eq([{ id: 1 }, { id: 2 }])
240 | })
241 |
242 | it('should remove from the middle', function () {
243 | priv.remove({ id: 2 })
244 | expect(priv.toArray()).to.deep.eq([{ id: 1 }, { id: 3 }])
245 | })
246 |
247 | it('should contain a middle value', function () {
248 | expect(priv.contains({ id: 2 })).to.eq(true)
249 | })
250 |
251 | it('should not contain a value in between two values', function () {
252 | expect(priv.contains({ id: 1.5 })).to.eq(false)
253 | })
254 |
255 | it('should find an iterator', function () {
256 | const iterator = priv.findIterator({ id: 2 })
257 | expect(iterator.value()).to.deep.eq({ id: 2 })
258 | })
259 |
260 | it('should find an iterator between values', function () {
261 | const iterator = priv.findIterator({ id: 1.5 })
262 | expect(iterator.value()).to.deep.eq({ id: 2 })
263 | })
264 | })
265 |
266 | describe('with allowSetValue', function () {
267 | beforeEach(function () {
268 | priv = new strategy({
269 | comparator: numberComparator,
270 | allowSetValue: true
271 | })
272 | priv.insert(1)
273 | priv.insert(2)
274 | })
275 |
276 | it('should allow you to use setValue(), even to do something stupid', function () {
277 | const iterator = priv.findIterator(2)
278 | iterator.setValue(0)
279 | expect(priv.toArray()).to.deep.eq([1, 0])
280 | })
281 |
282 | it('should not allow setValue() on an end iterator', function () {
283 | const iterator = priv.endIterator()
284 | expect(function () {
285 | iterator.setValue(2.5)
286 | }).to.throw()
287 | })
288 | })
289 |
290 | describe('with throw behavior on insert conflict', function () {
291 | beforeEach(function () {
292 | const comparator = function (a, b) {
293 | return a.v - b.v
294 | }
295 | const onInsertConflict = SortedSet.OnInsertConflictThrow
296 | priv = new strategy({ comparator, onInsertConflict })
297 | priv.insert({ v: 1, q: 'a' })
298 | priv.insert({ v: 2, q: 'b' })
299 | })
300 |
301 | it('should throw when inserting an element that matches another', function () {
302 | expect(() => priv.insert({ v: 1, q: 'c' })).to.throw('Value already in set')
303 | })
304 | })
305 |
306 | describe('with replace behavior on insert conflict', function () {
307 | beforeEach(function () {
308 | let comparator, onInsertConflict
309 | comparator = function (a, b) {
310 | return a.v - b.v
311 | }
312 | onInsertConflict = SortedSet.OnInsertConflictReplace
313 | priv = new strategy({ comparator, onInsertConflict })
314 | priv.insert({ v: 1, q: 'a' })
315 | priv.insert({ v: 2, q: 'b' })
316 | })
317 |
318 | it('should replace a matching element with the new element', function () {
319 | priv.insert({ v: 1, q: 'c' })
320 | expect(priv.toArray()).to.deep.eq([
321 | { v: 1, q: 'c' },
322 | { v: 2, q: 'b' }
323 | ])
324 | })
325 | })
326 |
327 | describe('with ignore behavior on insert conflict', function () {
328 | beforeEach(function () {
329 | const comparator = function (a, b) {
330 | return a.v - b.v
331 | }
332 | const onInsertConflict = SortedSet.OnInsertConflictIgnore
333 | priv = new strategy({ comparator, onInsertConflict })
334 | priv.insert({ v: 1, q: 'a' })
335 | priv.insert({ v: 2, q: 'b' })
336 | })
337 |
338 | it('should ignore the new element when inserting an element that matches another ', function () {
339 | priv.insert({ v: 1, q: 'c' })
340 | expect(priv.toArray()).to.deep.eq([
341 | { v: 1, q: 'a' },
342 | { v: 2, q: 'b' }
343 | ])
344 | })
345 | })
346 | })
347 | }
348 |
349 | module.exports.describeStrategy = describeStrategy
350 |
--------------------------------------------------------------------------------
/test/test_helper.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const sinon = require('sinon');
3 | const sinonChai = require('sinon-chai');
4 |
5 | global.sinon = sinon;
6 | global.expect = chai.expect;
7 |
8 | chai.use(sinonChai);
9 |
10 |
--------------------------------------------------------------------------------