├── .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":"oMACA,MAAMA,EACJC,YAAYC,EAAMC,QACXD,KAAOA,OACPE,MAAQD,OACRE,KAAOC,KAAKJ,KAAKG,KAGxBE,iBACSD,KAAKF,MAAQE,KAAKD,KAAKG,OAGhCC,qBACSH,KAAKF,MAAQ,EAGtBM,eACMJ,KAAKF,MAAQE,KAAKD,KAAKG,OAClBF,KAAKD,KAAKC,KAAKF,OAEf,KAIXO,SAASD,OACFJ,KAAKJ,KAAKU,QAAQC,mBACf,qCAEHP,KAAKC,eACF,wCAEDD,KAAKD,KAAKC,KAAKF,OAASM,EAGjCI,cACMR,KAAKF,OAASE,KAAKD,KAAKG,OACnB,KAEA,IAAIR,EAASM,KAAKJ,KAAMI,KAAKF,MAAQ,GAIhDW,kBACMT,KAAKF,OAAS,EACT,KAEA,IAAIJ,EAASM,KAAKJ,KAAMI,KAAKF,MAAQ,IAMlD,MAAMY,EAAuB,CAACC,EAAOP,EAAOQ,SACtCC,EAAM,EACNC,EAAOH,EAAMT,YACVW,EAAMC,GAAM,OACXC,EAAOF,EAAMC,IAAU,EACzBF,EAAWD,EAAMI,GAAMX,GAAS,EAClCS,EAAME,EAAM,EAEZD,EAAOC,SAGJF,GC/DT,MAAMG,EAAmB,CAACC,EAAaC,UAER,OAAtBA,EAAKD,IAAuB,OAC3BE,EAASD,GACfA,EAAOA,EAAKD,IACPG,oBAAsBD,SAEtBD,GAGHG,EAAa,CAACJ,EAAaC,SAC3BC,EAAQG,KACc,OAAtBJ,EAAKD,GACPE,EAASD,GACTA,EAAOA,EAAKD,IACPG,oBAAsBD,EAC3BG,EAA8B,SAAhBL,EAAyB,QAAU,OACjDC,EAAOF,EAAiBM,EAAaJ,OAChC,MAC0C,QAAvCC,EAASD,EAAKE,sBAAiCD,EAAOF,KAAiBC,GAC7EA,EAAOC,EAETD,EAAOC,SAEFD,GAKT,MAAMK,EACJ5B,YAAY6B,EAAOC,QACZC,KAAOF,OACPN,KAAOO,EAGdjB,UACoB,OAAdR,KAAKkB,YACA,KACF,OACCA,EAAOG,EAAW,QAASrB,KAAKkB,aAC/B,IAAIK,EAAmBvB,KAAK0B,KAAMR,IAI7CT,cACoB,OAAdT,KAAKkB,KAAe,IACC,OAAnBlB,KAAK0B,KAAKC,YACL,KACF,MACAD,KAAKC,KAAKP,oBAAsB,WAC/BF,EAAOF,EAAiB,QAAShB,KAAK0B,KAAKC,aAC1C,IAAIJ,EAAmBvB,KAAK0B,KAAMR,IAEtC,OACCA,EAAOG,EAAW,OAAQrB,KAAKkB,aACxB,OAATA,EACK,KAEA,IAAIK,EAAmBvB,KAAK0B,KAAMR,IAK/CjB,iBACuB,OAAdD,KAAKkB,KAGdf,qBAC6B,OAApBH,KAAKS,WAGdL,eACoB,OAAdJ,KAAKkB,KACA,KAEAlB,KAAKkB,KAAKd,MAIrBC,SAASD,OACFJ,KAAK0B,KAAKpB,QAAQC,mBACf,qCAEHP,KAAKC,eACF,wCAEDD,KAAKkB,KAAKd,MAAQA,GAK7BmB,EAAmBK,KAAO,SAASF,EAAMtB,EAAOQ,SACxCe,EAAOD,EAAKC,KACN,MAARA,IACFA,EAAKP,oBAAsB,UAEzBF,EAAOS,EACPE,EAAW,UACC,OAATX,GAAe,OACdY,EAAMlB,EAAWR,EAAOc,EAAKd,UACvB,IAAR0B,QAEG,GAAIA,EAAM,EAAG,IACA,OAAdZ,EAAKa,WAGTF,EAAWX,EAGXA,EAAKa,KAAKX,oBAAsBF,EAChCA,EAAOA,EAAKa,SACP,IACc,OAAfb,EAAKc,MAGF,CACLd,EAAOW,QAHPX,EAAKc,MAAMZ,oBAAsBF,EACjCA,EAAOA,EAAKc,cAOX,IAAIT,EAAmBG,EAAMR,IAGtCK,EAAmBQ,KAAQL,OACP,OAAdA,EAAKC,YACA,IAAIJ,EAAmBG,EAAM,MAC/B,CACLA,EAAKC,KAAKP,oBAAsB,WAC1BF,EAAOF,EAAiB,OAAQU,EAAKC,aACpC,IAAIJ,EAAmBG,EAAMR,KAIxCK,EAAmBS,MAASN,GACnB,IAAIH,EAAmBG,EAAM,MCpItC,MAAMO,EAAqB,CAACf,EAAMgB,KACnB,OAAThB,IACFe,EAAmBf,EAAKa,KAAMG,GAC9BA,EAAShB,EAAKd,OACd6B,EAAmBf,EAAKc,MAAOE,KAMnC,MAAMC,EACJC,gBACQC,EAAM,UACZJ,EAAmBjC,KAAK2B,MAAM,SAASvB,UAC9BiC,EAAIC,KAAKlC,MAEXiC,EAGTE,eACSvC,KAAK2B,KAAO,KAGrBa,YAAYN,EAAUO,EAAWC,OAC3BC,EAAI,EACRV,EAAmBjC,KAAK2B,MAAM,SAASvB,GACrC8B,EAASU,KAAKF,EAAStC,EAAOuC,EAAGF,GACjCE,GAAK,KAITE,SAASzC,SACDQ,EAAaZ,KAAKY,eACpBM,EAAOlB,KAAK2B,UACA,OAATT,GAAe,OACdY,EAAMlB,EAAWR,EAAOc,EAAKd,UACvB,IAAR0B,QAGFZ,EADSY,EAAM,EACRZ,EAAKa,KAELb,EAAKc,aAGA,OAATd,GAAmD,IAAlCN,EAAWM,EAAKd,MAAOA,GAGjD0C,aAAa1C,UACJmB,EAAmBK,KAAK5B,KAAMI,EAAOJ,KAAKY,YAGnDmC,uBACSxB,EAAmBQ,KAAK/B,MAGjCgD,qBACSzB,EAAmBS,MAAMhC,OCzDpC,MAAMiD,EACJtD,YAAYS,QACLA,MAAQA,OACR2B,KAAO,UACPC,MAAQ,MAIjB,MAQMkB,EAAmB,CAAChC,EAAMd,EAAOQ,QACxB,OAATM,OACI,yBAEFY,EAAMlB,EAAWR,EAAOc,EAAKd,UAC/B0B,EAAM,EACRZ,EAAKa,KAAOmB,EAAiBhC,EAAKa,KAAM3B,EAAOQ,QAC1C,GAAIkB,EAAM,EACfZ,EAAKc,MAAQkB,EAAiBhC,EAAKc,MAAO5B,EAAOQ,WAE/B,OAAdM,EAAKa,MAAgC,OAAfb,EAAKc,MAC7Bd,EAAO,UACF,GAAmB,OAAfA,EAAKc,MACdd,EAAOA,EAAKa,UACP,GAAkB,OAAdb,EAAKa,KACdb,EAAOA,EAAKc,UACP,OACCH,EAzBU,EAACX,EAAMD,UACE,OAAtBC,EAAKD,IACVC,EAAOA,EAAKD,UAEPC,GAqBciC,CAAcjC,EAAKc,MAAO,QAC3Cd,EAAKd,MAAQyB,EAASzB,MACtBc,EAAKc,MAAQkB,EAAiBhC,EAAKc,MAAOH,EAASzB,MAAOQ,UAGvDM,GC1BT,MAAM+B,EACJtD,YAAYyD,QACLhD,MAAQgD,OACRrB,KAAO,UACPC,MAAQ,UACRqB,OAAQ,GAKjB,MAAMC,EAAcC,UACZC,EAAID,EAAEvB,aACZuB,EAAEvB,MAAQwB,EAAEzB,KACZyB,EAAEzB,KAAOwB,EACTC,EAAEH,MAAQE,EAAEF,MACZE,EAAEF,OAAQ,EACHG,GAGHC,EAAeF,UACbC,EAAID,EAAExB,YACZwB,EAAExB,KAAOyB,EAAExB,MACXwB,EAAExB,MAAQuB,EACVC,EAAEH,MAAQE,EAAEF,MACZE,EAAEF,OAAQ,EACHG,GAGHE,EAAaH,IACjBA,EAAEF,OAASE,EAAEF,MACbE,EAAExB,KAAKsB,OAASE,EAAExB,KAAKsB,MACvBE,EAAEvB,MAAMqB,OAASE,EAAEvB,MAAMqB,OAGrBM,EAAeJ,IAEnBG,EAAUH,GACM,OAAZA,EAAEvB,OAAmC,OAAjBuB,EAAEvB,MAAMD,MAAiBwB,EAAEvB,MAAMD,KAAKsB,QAC5DE,EAAEvB,MAAQyB,EAAYF,EAAEvB,OACxBuB,EAAID,EAAWC,GACfG,EAAUH,IAELA,GAaHK,EAAe,CAACL,EAAGnD,EAAOyD,EAASC,QAC7B,OAANP,SACK,IAAIN,EAAK7C,SAIZ0B,EAAM+B,EAAQzD,EAAOmD,EAAEnD,cACjB,IAAR0B,EACFyB,EAAEnD,MAAQ0D,EAAiBP,EAAEnD,MAAOA,GAC3B0B,EAAM,EACfyB,EAAExB,KAAO6B,EAAaL,EAAExB,KAAM3B,EAAOyD,EAASC,GAE9CP,EAAEvB,MAAQ4B,EAAaL,EAAEvB,MAAO5B,EAAOyD,EAASC,GAElC,OAAZP,EAAEvB,QAAkBuB,EAAEvB,MAAMqB,OAAsB,OAAXE,EAAExB,MAAiBwB,EAAExB,KAAKsB,QACnEE,EAAID,EAAWC,IAEF,OAAXA,EAAExB,MAAiBwB,EAAExB,KAAKsB,OAAyB,OAAhBE,EAAExB,KAAKA,MAAiBwB,EAAExB,KAAKA,KAAKsB,QACzEE,EAAIE,EAAYF,IAGH,OAAXA,EAAExB,MAAiBwB,EAAExB,KAAKsB,OAAqB,OAAZE,EAAEvB,OAAkBuB,EAAEvB,MAAMqB,OACjEK,EAAUH,GAELA,GAUHQ,EAASR,IAEG,OAAZA,EAAEvB,OAAkBuB,EAAEvB,MAAMqB,QAC9BE,EAAID,EAAWC,IAGF,OAAXA,EAAExB,MAAiBwB,EAAExB,KAAKsB,OAAyB,OAAhBE,EAAExB,KAAKA,MAAiBwB,EAAExB,KAAKA,KAAKsB,QACzEE,EAAIE,EAAYF,IAGH,OAAXA,EAAExB,MAAiBwB,EAAExB,KAAKsB,OAAqB,OAAZE,EAAEvB,OAAkBuB,EAAEvB,MAAMqB,OACjEK,EAAUH,GAELA,GAGHS,EAAiBT,GACN,OAAXA,EAAExB,KACG,MAEJwB,EAAExB,KAAKsB,OAA2B,OAAhBE,EAAExB,KAAKA,MAAiBwB,EAAExB,KAAKA,KAAKsB,QACzDE,EAAII,EAAYJ,IAElBA,EAAExB,KAAOiC,EAAcT,EAAExB,MAClBgC,EAAMR,IAGTU,EAAiB,CAACV,EAAGnD,EAAOyD,QACtB,OAANN,OACI,sBAEJM,EAAQzD,EAAOmD,EAAEnD,OAAS,EAAG,IAChB,OAAXmD,EAAExB,UACE,mBAEHwB,EAAExB,KAAKsB,OAA2B,OAAhBE,EAAExB,KAAKA,MAAiBwB,EAAExB,KAAKA,KAAKsB,QACzDE,EAAII,EAAYJ,IAElBA,EAAExB,KAAOkC,EAAeV,EAAExB,KAAM3B,EAAOyD,OAClC,IACU,OAAXN,EAAExB,MAAiBwB,EAAExB,KAAKsB,QAC5BE,EAAIE,EAAYF,IAEF,OAAZA,EAAEvB,MAAgB,IACY,IAA5B6B,EAAQzD,EAAOmD,EAAEnD,cACZ,UAED,mBAGLmD,EAAEvB,MAAMqB,OAA4B,OAAjBE,EAAEvB,MAAMD,MAAiBwB,EAAEvB,MAAMD,KAAKsB,QAC5DE,EA/FgBA,CAAAA,IAEpBG,EAAUH,GACK,OAAXA,EAAExB,MAAiC,OAAhBwB,EAAExB,KAAKA,MAAiBwB,EAAExB,KAAKA,KAAKsB,QACzDE,EAAIE,EAAYF,GAChBG,EAAUH,IAELA,GAwFCW,CAAaX,IAEa,IAA5BM,EAAQzD,EAAOmD,EAAEnD,QACnBmD,EAAEnD,MA7DamD,CAAAA,SACD,OAAXA,EAAExB,MACPwB,EAAIA,EAAExB,YAEDwB,GAyDOY,CAAYZ,EAAEvB,OAAO5B,MAC/BmD,EAAEvB,MAAQgC,EAAcT,EAAEvB,QAE1BuB,EAAEvB,MAAQiC,EAAeV,EAAEvB,MAAO5B,EAAOyD,UAGnC,OAANN,IACFA,EAAIQ,EAAMR,IAELA,GAGT,MAAMa,UAA6BC,EACjC1E,YAAYW,gBAELA,QAAUA,OACVM,WAAaZ,KAAKM,QAAQM,gBAC1BkD,iBAAmB9D,KAAKM,QAAQwD,sBAChCnC,KAAO,KAGd2C,OAAOlE,QACAuB,KAAOiC,EAAa5D,KAAK2B,KAAMvB,EAAOJ,KAAKY,WAAYZ,KAAK8D,uBAC5DnC,KAAK0B,OAAQ,EAGpBkB,OAAOnE,QACAuB,KAAOsC,EAAejE,KAAK2B,KAAMvB,EAAOJ,KAAKY,YAChC,OAAdZ,KAAK2B,YACFA,KAAK0B,OAAQ,IC1LxB,MAAMmB,EAA0B,CAC9BC,sBAAuB,CAACC,EAAUC,WAAqB,IAAIC,MAAM,yBACjEC,wBAAyB,CAACH,EAAUC,IAAaA,EACjDG,uBAAwB,CAACJ,EAAUC,IAAaD,GCGlD,MAAMK,UCLN,MACEpF,YAAYW,MAC2C,OAArC,MAAXA,EAAkBA,EAAQ0E,cAAW,QAClC,4CAE+C,OAAvC,MAAX1E,EAAkBA,EAAQM,gBAAa,QACpC,gDAEqD,OAA7C,MAAXN,EAAkBA,EAAQwD,sBAAmB,QAC1C,sDAEHlE,KAAO,IAAIU,EAAQ0E,SAAS1E,QAC5BJ,OAAS,EAGhBoE,OAAOlE,eACAR,KAAK0E,OAAOlE,QACZF,QAAU,EACRF,KAGTuE,OAAOnE,eACAR,KAAK2E,OAAOnE,QACZF,QAAU,EACRF,KAGTuC,oBACO3C,KAAK2C,aACLrC,OAAS,EACPF,KAGT6C,SAASzC,UACAJ,KAAKJ,KAAKiD,SAASzC,GAI5BgC,iBACSpC,KAAKJ,KAAKwC,UAGnB6C,QAAQ/C,EAAUQ,eACX9C,KAAK4C,YAAYN,EAAUlC,KAAM0C,GAC/B1C,KAGTkF,IAAIhD,EAAUQ,SACNL,EAAM,eACP4C,SAAQ,SAAS7E,EAAON,EAAOqF,UAC3B9C,EAAIC,KAAKJ,EAASU,KAAKF,EAAStC,EAAON,EAAOqF,OAEhD9C,EAGT+C,OAAOlD,EAAUQ,SACTL,EAAM,eACP4C,SAAQ,SAAS7E,EAAON,EAAOqF,MAC9BjD,EAASU,KAAKF,EAAStC,EAAON,EAAOqF,UAChC9C,EAAIC,KAAKlC,MAGbiC,EAGTgD,MAAMnD,EAAUQ,OACVL,GAAM,cACL4C,SAAQ,SAAS7E,EAAON,EAAOqF,GAC9B9C,IAAQH,EAASU,KAAKF,EAAStC,EAAON,EAAOqF,KAC/C9C,GAAM,MAGHA,EAGTiD,KAAKpD,EAAUQ,OACTL,GAAM,cACL4C,SAAQ,SAAS7E,EAAON,EAAOqF,IAC7B9C,GAAOH,EAASU,KAAKF,EAAStC,EAAON,EAAOqF,KAC/C9C,GAAM,MAGHA,EA0BTS,aAAa1C,UACJJ,KAAKJ,KAAKkD,aAAa1C,GAIhC2C,uBACS/C,KAAKJ,KAAKmD,gBAInBC,qBACShD,KAAKJ,KAAKoD,gBDjHnBrD,YAAYW,GACVA,IAAYA,EAAU,IACtBA,EAAQ0E,WAAa1E,EAAQ0E,SAAWZ,GACxC9D,EAAQM,aAAeN,EAAQM,WAAa,SAAS2E,EAAGC,UAC9CD,GAAK,IAAMC,GAAK,KAE1BlF,EAAQwD,mBAAqBxD,EAAQwD,iBAAmBU,EAAwBC,6BAC1EnE,WAIVyE,EAAUU,cNgDV,MACE9F,YAAYW,QACLA,QAAUA,OACVwD,iBAAmB9D,KAAKM,QAAQwD,sBAChClD,WAAaZ,KAAKM,QAAQM,gBAC1Bb,KAAO,GAGdqC,iBACSpC,KAAKD,KAGduE,OAAOlE,SACCN,EAAQY,EAAqBV,KAAKD,KAAMK,EAAOJ,KAAKY,wBACjC,IAArBZ,KAAKD,KAAKD,IAAkE,IAA7CE,KAAKY,WAAWZ,KAAKD,KAAKD,GAAQM,GAC5DJ,KAAKD,KAAK2F,OAAO5F,EAAO,EAAGE,KAAK8D,iBAAiB9D,KAAKD,KAAKD,GAAQM,IAEnEJ,KAAKD,KAAK2F,OAAO5F,EAAO,EAAGM,GAItCmE,OAAOnE,SACCN,EAAQY,EAAqBV,KAAKD,KAAMK,EAAOJ,KAAKY,eACT,IAA7CZ,KAAKY,WAAWZ,KAAKD,KAAKD,GAAQM,QAC9B,0BAEDJ,KAAKD,KAAK2F,OAAO5F,EAAO,GAGjCyC,eACSvC,KAAKD,KAAKG,OAAS,EAG5B2C,SAASzC,SACDN,EAAQY,EAAqBV,KAAKD,KAAMK,EAAOJ,KAAKY,mBACnDZ,KAAKF,QAAUE,KAAKD,KAAKG,QAAuD,IAA7CF,KAAKY,WAAWZ,KAAKD,KAAKD,GAAQM,GAG9EoC,YAAYN,EAAUO,EAAWC,SACzB3C,EAAOC,KAAKD,KACZ4F,EAAM5F,EAAKG,WACZ,IAAIyC,EAAI,EAAGA,EAAIgD,EAAKhD,IACvBT,EAASU,KAAKF,EAAS3C,EAAK4C,GAAIA,EAAGF,GAIvCK,aAAa1C,SACLN,EAAQY,EAAqBV,KAAKD,KAAMK,EAAOJ,KAAKY,mBACnD,IAAIlB,EAASM,KAAMF,GAG5BiD,uBACS,IAAIrD,EAASM,KAAM,GAG5BgD,qBACS,IAAItD,EAASM,KAAMA,KAAKD,KAAKG,UMvGxC6E,EAAUa,mBHwBV,cAAiCvB,EAC/B1E,YAAYW,gBAELA,QAAUA,OACVM,WAAaZ,KAAKM,QAAQM,gBAC1BkD,iBAAmB9D,KAAKM,QAAQwD,sBAChCnC,KAAO,KAGd2C,OAAOlE,SACCyD,EAAU7D,KAAKY,cACH,OAAdZ,KAAK2B,KAAe,KAClBR,EAASnB,KAAK2B,KACdV,EAAc,YACL,OACLa,EAAM+B,EAAQzD,EAAOe,EAAOf,UACtB,IAAR0B,cACFX,EAAOf,MAAQJ,KAAK8D,iBAAiB3C,EAAOf,MAAOA,OAGnDa,EAAca,EAAM,EAAI,OAAS,QACL,OAAxBX,EAAOF,SAGXE,EAASA,EAAOF,UAGbE,EAAOF,GAAe,IAAIgC,EAAK7C,UAE/BJ,KAAK2B,KAAO,IAAIsB,EAAK7C,GAIhCmE,OAAOnE,UACEJ,KAAK2B,KAAOuB,EAAiBlD,KAAK2B,KAAMvB,EAAOJ,KAAKY,cGzD/DmE,EAAUX,qBAAuBA,EAEjCyB,OAAOC,OAAOf,EAAWP"} -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------