@live-change/flatten-interval-tree 0.8.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.babelrc +8 -0
  2. package/.travis.yml +14 -0
  3. package/LICENSE +21 -0
  4. package/README.md +172 -0
  5. package/dist/main.cjs.js +799 -0
  6. package/dist/main.esm.js +794 -0
  7. package/dist/main.umd.js +805 -0
  8. package/docs/Interval.html +1609 -0
  9. package/docs/IntervalTree.html +1506 -0
  10. package/docs/classes_interval.js.html +173 -0
  11. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  12. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  13. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  14. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  15. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  16. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  17. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  18. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  19. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  20. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  21. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  22. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  23. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  24. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  25. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  26. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  27. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  28. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  29. package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
  30. package/docs/fonts/OpenSans-Semibold-webfont.svg +1830 -0
  31. package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
  32. package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
  33. package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
  34. package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +1830 -0
  35. package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  36. package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  37. package/docs/index.html +197 -0
  38. package/docs/index.js.html +624 -0
  39. package/docs/interval.js.html +184 -0
  40. package/docs/intervalTree.js.html +624 -0
  41. package/docs/scripts/linenumber.js +25 -0
  42. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  43. package/docs/scripts/prettify/lang-css.js +2 -0
  44. package/docs/scripts/prettify/prettify.js +28 -0
  45. package/docs/styles/jsdoc-default.css +692 -0
  46. package/docs/styles/prettify-jsdoc.css +111 -0
  47. package/docs/styles/prettify-tomorrow.css +132 -0
  48. package/examples/browser/index.html +49 -0
  49. package/examples/create-react-app/package.json +15 -0
  50. package/examples/create-react-app/public/index.html +11 -0
  51. package/examples/create-react-app/src/App.js +38 -0
  52. package/examples/create-react-app/src/ComposersList.js +15 -0
  53. package/examples/create-react-app/src/index.js +5 -0
  54. package/examples/es6-module/index.html +50 -0
  55. package/examples/nodejs/index.js +23 -0
  56. package/index.d.ts +65 -0
  57. package/index.js +6 -0
  58. package/package.json +52 -0
  59. package/rollup.config.js +21 -0
  60. package/src/classes/interval.js +131 -0
  61. package/src/classes/intervalTree.js +564 -0
  62. package/src/classes/node.js +97 -0
  63. package/src/utils/constants.js +16 -0
  64. package/test/intervalTree.js +232 -0
  65. package/test/node.js +64 -0
@@ -0,0 +1,794 @@
1
+ /**
2
+ * Created by Alex Bol on 4/1/2017.
3
+ */
4
+
5
+ function min(a,b) {
6
+ return a < b ? a : b
7
+ }
8
+ function max(a,b) {
9
+ return a > b ? a : b
10
+ }
11
+
12
+ /**
13
+ * Interval is a pair of numbers or a pair of any comparable objects on which may be defined predicates
14
+ * *equal*, *less* and method *max(p1, p1)* that returns maximum in a pair.
15
+ * When interval is an object rather than pair of numbers, this object should have properties *low*, *high*, *max*
16
+ * and implement methods *less_than(), equal_to(), intersect(), not_intersect(), clone(), output()*.
17
+ * Two static methods *comparable_max(), comparable_less_than()* define how to compare values in pair. <br/>
18
+ * This interface is described in typescript definition file *index.d.ts*
19
+ *
20
+ * Axis aligned rectangle is an example of such interval.
21
+ * We may look at rectangle as an interval between its low left and top right corners.
22
+ * See **Box** class in [flatten-js](https://github.com/alexbol99/flatten-js) library as the example
23
+ * of Interval interface implementation
24
+ * @type {Interval}
25
+ */
26
+ const Interval = class Interval {
27
+ /**
28
+ * Accept two comparable values and creates new instance of interval
29
+ * Predicate Interval.comparable_less(low, high) supposed to return true on these values
30
+ * @param low
31
+ * @param high
32
+ */
33
+ constructor(low, high) {
34
+ this.low = low;
35
+ this.high = high;
36
+ }
37
+
38
+ /**
39
+ * Clone interval
40
+ * @returns {Interval}
41
+ */
42
+ clone() {
43
+ return new Interval(this.low, this.high);
44
+ }
45
+
46
+ /**
47
+ * Propery max returns clone of this interval
48
+ * @returns {Interval}
49
+ */
50
+ get max() {
51
+ return this.clone(); // this.high;
52
+ }
53
+
54
+ /**
55
+ * Predicate returns true is this interval less than other interval
56
+ * @param other_interval
57
+ * @returns {boolean}
58
+ */
59
+ less_than(other_interval) {
60
+ return this.low < other_interval.low ||
61
+ this.low == other_interval.low && this.high < other_interval.high;
62
+ }
63
+
64
+ /**
65
+ * Predicate returns true is this interval equals to other interval
66
+ * @param other_interval
67
+ * @returns {boolean}
68
+ */
69
+ equal_to(other_interval) {
70
+ return this.low == other_interval.low && this.high == other_interval.high;
71
+ }
72
+
73
+ /**
74
+ * Predicate returns true if this interval intersects other interval
75
+ * @param other_interval
76
+ * @returns {boolean}
77
+ */
78
+ intersect(other_interval) {
79
+ return !this.not_intersect(other_interval);
80
+ }
81
+
82
+ /**
83
+ * Predicate returns true if this interval does not intersect other interval
84
+ * @param other_interval
85
+ * @returns {boolean}
86
+ */
87
+ not_intersect(other_interval) {
88
+ return (this.high < other_interval.low || other_interval.high < this.low);
89
+ }
90
+
91
+ /**
92
+ * Returns new interval merged with other interval
93
+ * @param {Interval} interval - Other interval to merge with
94
+ * @returns {Interval}
95
+ */
96
+ merge(other_interval) {
97
+ return new Interval(
98
+ this.low === undefined ? other_interval.low : min(this.low, other_interval.low),
99
+ this.high === undefined ? other_interval.high : max(this.high, other_interval.high)
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Returns how key should return
105
+ */
106
+ output() {
107
+ return [this.low, this.high];
108
+ }
109
+
110
+ /**
111
+ * Function returns maximum between two comparable values
112
+ * @param interval1
113
+ * @param interval2
114
+ * @returns {Interval}
115
+ */
116
+ static comparable_max(interval1, interval2) {
117
+ return interval1.merge(interval2);
118
+ }
119
+
120
+ /**
121
+ * Predicate returns true if first value less than second value
122
+ * @param val1
123
+ * @param val2
124
+ * @returns {boolean}
125
+ */
126
+ static comparable_less_than(val1, val2 ) {
127
+ return val1 < val2;
128
+ }
129
+ };
130
+
131
+ /**
132
+ * Created by Alex Bol on 3/28/2017.
133
+ */
134
+
135
+ // module.exports = {
136
+ // RB_TREE_COLOR_RED: 0,
137
+ // RB_TREE_COLOR_BLACK: 1
138
+ // };
139
+
140
+ const RB_TREE_COLOR_RED = 0;
141
+ const RB_TREE_COLOR_BLACK = 1;
142
+
143
+ /**
144
+ * Created by Alex Bol on 4/1/2017.
145
+ */
146
+
147
+ function min$1(a,b) {
148
+ return a < b ? a : b
149
+ }
150
+ function max$1(a,b) {
151
+ return a > b ? a : b
152
+ }
153
+
154
+ class Node {
155
+ constructor(key = undefined, value = undefined,
156
+ left = null, right = null, parent = null, color = RB_TREE_COLOR_BLACK) {
157
+ this.left = left; // reference to left child node
158
+ this.right = right; // reference to right child node
159
+ this.parent = parent; // reference to parent node
160
+ this.color = color;
161
+
162
+ this.item = {key: key, value: value}; // key is supposed to be instance of Interval
163
+
164
+ /* If not, this should by an array of two numbers */
165
+ if (key && key instanceof Array && key.length == 2) {
166
+ this.item.key = new Interval(min$1(key[0], key[1]), max$1(key[0], key[1]));
167
+ }
168
+ this.max = this.item.key ? this.item.key.max : undefined;
169
+ }
170
+
171
+ isNil() {
172
+ return (this.item.key === undefined && this.item.value === undefined &&
173
+ this.left === null && this.right === null && this.color === RB_TREE_COLOR_BLACK);
174
+ }
175
+
176
+ less_than(other_node) {
177
+ if (this.item.value && other_node.item.value) {
178
+ let item_less_than = this.item.value.less_than ? this.item.value.less_than(other_node.item.value) :
179
+ this.item.value < other_node.item.value;
180
+ return this.item.key.less_than(other_node.item.key) ||
181
+ this.item.key.equal_to((other_node.item.key)) && item_less_than;
182
+ }
183
+ else {
184
+ return this.item.key.less_than(other_node.item.key);
185
+ }
186
+ }
187
+
188
+ equal_to(other_node) {
189
+ let value_equal = true;
190
+ if (this.item.value && other_node.item.value) {
191
+ value_equal = this.item.value.equal_to ? this.item.value.equal_to(other_node.item.value) :
192
+ this.item.value == other_node.item.value;
193
+ }
194
+ return this.item.key.equal_to(other_node.item.key) && value_equal;
195
+ }
196
+
197
+ intersect(other_node) {
198
+ return this.item.key.intersect(other_node.item.key);
199
+ }
200
+
201
+ copy_data(other_node) {
202
+ this.item.key = other_node.item.key.clone();
203
+ this.item.value = other_node.item.value;
204
+ }
205
+
206
+ update_max() {
207
+ // use key (Interval) max property instead of key.high
208
+ this.max = this.item.key ? this.item.key.max : undefined;
209
+ if (this.right && this.right.max) {
210
+ const comparable_max = this.item.key.constructor.comparable_max; // static method
211
+ this.max = comparable_max(this.max, this.right.max);
212
+ }
213
+ if (this.left && this.left.max) {
214
+ const comparable_max = this.item.key.constructor.comparable_max; // static method
215
+ this.max = comparable_max(this.max, this.left.max);
216
+ }
217
+ }
218
+
219
+ // Other_node does not intersect any node of left subtree, if this.left.max < other_node.item.key.low
220
+ not_intersect_left_subtree(search_node) {
221
+ const comparable_less_than = this.item.key.constructor.comparable_less_than; // static method
222
+ let high = this.left.max.high !== undefined ? this.left.max.high : this.left.max;
223
+ return comparable_less_than(high, search_node.item.key.low);
224
+ }
225
+
226
+ // Other_node does not intersect right subtree if other_node.item.key.high < this.right.key.low
227
+ not_intersect_right_subtree(search_node) {
228
+ const comparable_less_than = this.item.key.constructor.comparable_less_than; // static method
229
+ let low = this.right.max.low !== undefined ? this.right.max.low : this.right.item.key.low;
230
+ return comparable_less_than(search_node.item.key.high, low);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Created by Alex Bol on 3/31/2017.
236
+ */
237
+
238
+ // const nil_node = new Node();
239
+
240
+ /**
241
+ * Implementation of interval binary search tree <br/>
242
+ * Interval tree stores items which are couples of {key:interval, value: value} <br/>
243
+ * Interval is an object with high and low properties or simply pair [low,high] of numeric values <br />
244
+ * @type {IntervalTree}
245
+ */
246
+ class IntervalTree {
247
+ /**
248
+ * Construct new empty instance of IntervalTree
249
+ */
250
+ constructor() {
251
+ this.root = null;
252
+ this.nil_node = new Node();
253
+ }
254
+
255
+ /**
256
+ * Returns number of items stored in the interval tree
257
+ * @returns {number}
258
+ */
259
+ get size() {
260
+ let count = 0;
261
+ this.tree_walk(this.root, () => count++);
262
+ return count;
263
+ }
264
+
265
+ /**
266
+ * Returns array of sorted keys in the ascending order
267
+ * @returns {Array}
268
+ */
269
+ get keys() {
270
+ let res = [];
271
+ this.tree_walk(this.root, (node) => res.push(
272
+ node.item.key.output ? node.item.key.output() : node.item.key
273
+ ));
274
+ return res;
275
+ }
276
+
277
+ /**
278
+ * Return array of values in the ascending keys order
279
+ * @returns {Array}
280
+ */
281
+ get values() {
282
+ let res = [];
283
+ this.tree_walk(this.root, (node) => res.push(node.item.value));
284
+ return res;
285
+ }
286
+
287
+ /**
288
+ * Returns array of items (<key,value> pairs) in the ascended keys order
289
+ * @returns {Array}
290
+ */
291
+ get items() {
292
+ let res = [];
293
+ this.tree_walk(this.root, (node) => res.push({
294
+ key: node.item.key.output ? node.item.key.output() : node.item.key,
295
+ value: node.item.value
296
+ }));
297
+ return res;
298
+ }
299
+
300
+ /**
301
+ * Returns true if tree is empty
302
+ * @returns {boolean}
303
+ */
304
+ isEmpty() {
305
+ return (this.root == null || this.root == this.nil_node);
306
+ }
307
+
308
+ /**
309
+ * Insert new item into interval tree
310
+ * @param key - interval object or array of two numbers [low, high]
311
+ * @param value - value representing any object (optional)
312
+ * @returns {Node} - returns reference to inserted node as an object {key:interval, value: value}
313
+ */
314
+ insert(key, value = key) {
315
+ if (key === undefined) return;
316
+ let insert_node = new Node(key, value, this.nil_node, this.nil_node, null, RB_TREE_COLOR_RED);
317
+ this.tree_insert(insert_node);
318
+ this.recalc_max(insert_node);
319
+ return insert_node;
320
+ }
321
+
322
+ /**
323
+ * Returns true if item {key,value} exist in the tree
324
+ * @param key - interval correspondent to keys stored in the tree
325
+ * @param value - value object to be checked
326
+ * @returns {boolean} - true if item {key, value} exist in the tree, false otherwise
327
+ */
328
+ exist(key, value) {
329
+ let search_node = new Node(key, value);
330
+ return this.tree_search(this.root, search_node) ? true : false;
331
+ }
332
+
333
+ /**
334
+ * Remove entry {key, value} from the tree
335
+ * @param key - interval correspondent to keys stored in the tree
336
+ * @param value - - value object
337
+ * @returns {boolean} - true if item {key, value} deleted, false if not found
338
+ */
339
+ remove(key, value) {
340
+ let search_node = new Node(key, value);
341
+ let delete_node = this.tree_search(this.root, search_node);
342
+ if (delete_node) {
343
+ this.tree_delete(delete_node);
344
+ }
345
+ return delete_node;
346
+ }
347
+
348
+ /**
349
+ * Returns array of entry values which keys intersect with given interval <br/>
350
+ * If no values stored in the tree, returns array of keys which intersect given interval
351
+ * @param interval - search interval, or array [low, high]
352
+ * @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output
353
+ * @returns {Array}
354
+ */
355
+ search(interval, outputMapperFn = (value, key) => value ? value : key.output()) {
356
+ let search_node = new Node(interval);
357
+ let resp_nodes = [];
358
+ this.tree_search_interval(this.root, search_node, resp_nodes);
359
+ return resp_nodes.map(node => outputMapperFn(node.item.value, node.item.key))
360
+ }
361
+
362
+ /**
363
+ * Tree visitor. For each node implement a callback function. <br/>
364
+ * Method calls a callback function with two parameters (key, value)
365
+ * @param visitor(key,value) - function to be called for each tree item
366
+ */
367
+ forEach(visitor) {
368
+ this.tree_walk(this.root, (node) => visitor(node.item.key, node.item.value));
369
+ }
370
+
371
+ /** Value Mapper. Walk through every node and map node value to another value
372
+ * @param callback(value, key) - function to be called for each tree item
373
+ */
374
+ map(callback) {
375
+ const tree = new IntervalTree();
376
+ this.tree_walk(this.root, (node) => tree.insert(node.item.key, callback(node.item.value, node.item.key)));
377
+ return tree;
378
+ }
379
+
380
+ recalc_max(node) {
381
+ let node_current = node;
382
+ while (node_current.parent != null) {
383
+ node_current.parent.update_max();
384
+ node_current = node_current.parent;
385
+ }
386
+ }
387
+
388
+ tree_insert(insert_node) {
389
+ let current_node = this.root;
390
+ let parent_node = null;
391
+
392
+ if (this.root == null || this.root == this.nil_node) {
393
+ this.root = insert_node;
394
+ }
395
+ else {
396
+ while (current_node != this.nil_node) {
397
+ parent_node = current_node;
398
+ if (insert_node.less_than(current_node)) {
399
+ current_node = current_node.left;
400
+ }
401
+ else {
402
+ current_node = current_node.right;
403
+ }
404
+ }
405
+
406
+ insert_node.parent = parent_node;
407
+
408
+ if (insert_node.less_than(parent_node)) {
409
+ parent_node.left = insert_node;
410
+ }
411
+ else {
412
+ parent_node.right = insert_node;
413
+ }
414
+ }
415
+
416
+ this.insert_fixup(insert_node);
417
+ }
418
+
419
+ // After insertion insert_node may have red-colored parent, and this is a single possible violation
420
+ // Go upwords to the root and re-color until violation will be resolved
421
+ insert_fixup(insert_node) {
422
+ let current_node;
423
+ let uncle_node;
424
+
425
+ current_node = insert_node;
426
+ while (current_node != this.root && current_node.parent.color == RB_TREE_COLOR_RED) {
427
+ if (current_node.parent == current_node.parent.parent.left) { // parent is left child of grandfather
428
+ uncle_node = current_node.parent.parent.right; // right brother of parent
429
+ if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 1. Uncle is red
430
+ // re-color father and uncle into black
431
+ current_node.parent.color = RB_TREE_COLOR_BLACK;
432
+ uncle_node.color = RB_TREE_COLOR_BLACK;
433
+ current_node.parent.parent.color = RB_TREE_COLOR_RED;
434
+ current_node = current_node.parent.parent;
435
+ }
436
+ else { // Case 2 & 3. Uncle is black
437
+ if (current_node == current_node.parent.right) { // Case 2. Current if right child
438
+ // This case is transformed into Case 3.
439
+ current_node = current_node.parent;
440
+ this.rotate_left(current_node);
441
+ }
442
+ current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 3. Current is left child.
443
+ // Re-color father and grandfather, rotate grandfather right
444
+ current_node.parent.parent.color = RB_TREE_COLOR_RED;
445
+ this.rotate_right(current_node.parent.parent);
446
+ }
447
+ }
448
+ else { // parent is right child of grandfather
449
+ uncle_node = current_node.parent.parent.left; // left brother of parent
450
+ if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 4. Uncle is red
451
+ // re-color father and uncle into black
452
+ current_node.parent.color = RB_TREE_COLOR_BLACK;
453
+ uncle_node.color = RB_TREE_COLOR_BLACK;
454
+ current_node.parent.parent.color = RB_TREE_COLOR_RED;
455
+ current_node = current_node.parent.parent;
456
+ }
457
+ else {
458
+ if (current_node == current_node.parent.left) { // Case 5. Current is left child
459
+ // Transform into case 6
460
+ current_node = current_node.parent;
461
+ this.rotate_right(current_node);
462
+ }
463
+ current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 6. Current is right child.
464
+ // Re-color father and grandfather, rotate grandfather left
465
+ current_node.parent.parent.color = RB_TREE_COLOR_RED;
466
+ this.rotate_left(current_node.parent.parent);
467
+ }
468
+ }
469
+ }
470
+
471
+ this.root.color = RB_TREE_COLOR_BLACK;
472
+ }
473
+
474
+ tree_delete(delete_node) {
475
+ let cut_node; // node to be cut - either delete_node or successor_node ("y" from 14.4)
476
+ let fix_node; // node to fix rb tree property ("x" from 14.4)
477
+
478
+ if (delete_node.left == this.nil_node || delete_node.right == this.nil_node) { // delete_node has less then 2 children
479
+ cut_node = delete_node;
480
+ }
481
+ else { // delete_node has 2 children
482
+ cut_node = this.tree_successor(delete_node);
483
+ }
484
+
485
+ // fix_node if single child of cut_node
486
+ if (cut_node.left != this.nil_node) {
487
+ fix_node = cut_node.left;
488
+ }
489
+ else {
490
+ fix_node = cut_node.right;
491
+ }
492
+
493
+ // remove cut_node from parent
494
+ /*if (fix_node != this.nil_node) {*/
495
+ fix_node.parent = cut_node.parent;
496
+ /*}*/
497
+
498
+ if (cut_node == this.root) {
499
+ this.root = fix_node;
500
+ }
501
+ else {
502
+ if (cut_node == cut_node.parent.left) {
503
+ cut_node.parent.left = fix_node;
504
+ }
505
+ else {
506
+ cut_node.parent.right = fix_node;
507
+ }
508
+ cut_node.parent.update_max(); // update max property of the parent
509
+ }
510
+
511
+ this.recalc_max(fix_node); // update max property upward from fix_node to root
512
+
513
+ // COPY DATA !!!
514
+ // Delete_node becomes cut_node, it means that we cannot hold reference
515
+ // to node in outer structure and we will have to delete by key, additional search need
516
+ if (cut_node != delete_node) {
517
+ delete_node.copy_data(cut_node);
518
+ delete_node.update_max(); // update max property of the cut node at the new place
519
+ this.recalc_max(delete_node); // update max property upward from delete_node to root
520
+ }
521
+
522
+ if (/*fix_node != this.nil_node && */cut_node.color == RB_TREE_COLOR_BLACK) {
523
+ this.delete_fixup(fix_node);
524
+ }
525
+ }
526
+
527
+ delete_fixup(fix_node) {
528
+ let current_node = fix_node;
529
+ let brother_node;
530
+
531
+ while (current_node != this.root && current_node.parent != null && current_node.color == RB_TREE_COLOR_BLACK) {
532
+ if (current_node == current_node.parent.left) { // fix node is left child
533
+ brother_node = current_node.parent.right;
534
+ if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red
535
+ brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother
536
+ current_node.parent.color = RB_TREE_COLOR_RED; // re-color father
537
+ this.rotate_left(current_node.parent);
538
+ brother_node = current_node.parent.right; // update brother
539
+ }
540
+ // Derive to cases 2..4: brother is black
541
+ if (brother_node.left.color == RB_TREE_COLOR_BLACK &&
542
+ brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2: both nephews black
543
+ brother_node.color = RB_TREE_COLOR_RED; // re-color brother
544
+ current_node = current_node.parent; // continue iteration
545
+ }
546
+ else {
547
+ if (brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 3: left nephew red, right nephew black
548
+ brother_node.color = RB_TREE_COLOR_RED; // re-color brother
549
+ brother_node.left.color = RB_TREE_COLOR_BLACK; // re-color nephew
550
+ this.rotate_right(brother_node);
551
+ brother_node = current_node.parent.right; // update brother
552
+ // Derive to case 4: left nephew black, right nephew red
553
+ }
554
+ // case 4: left nephew black, right nephew red
555
+ brother_node.color = current_node.parent.color;
556
+ current_node.parent.color = RB_TREE_COLOR_BLACK;
557
+ brother_node.right.color = RB_TREE_COLOR_BLACK;
558
+ this.rotate_left(current_node.parent);
559
+ current_node = this.root; // exit from loop
560
+ }
561
+ }
562
+ else { // fix node is right child
563
+ brother_node = current_node.parent.left;
564
+ if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red
565
+ brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother
566
+ current_node.parent.color = RB_TREE_COLOR_RED; // re-color father
567
+ this.rotate_right(current_node.parent);
568
+ brother_node = current_node.parent.left; // update brother
569
+ }
570
+ // Go to cases 2..4
571
+ if (brother_node.left.color == RB_TREE_COLOR_BLACK &&
572
+ brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2
573
+ brother_node.color = RB_TREE_COLOR_RED; // re-color brother
574
+ current_node = current_node.parent; // continue iteration
575
+ }
576
+ else {
577
+ if (brother_node.left.color == RB_TREE_COLOR_BLACK) { // case 3: right nephew red, left nephew black
578
+ brother_node.color = RB_TREE_COLOR_RED; // re-color brother
579
+ brother_node.right.color = RB_TREE_COLOR_BLACK; // re-color nephew
580
+ this.rotate_left(brother_node);
581
+ brother_node = current_node.parent.left; // update brother
582
+ // Derive to case 4: right nephew black, left nephew red
583
+ }
584
+ // case 4: right nephew black, left nephew red
585
+ brother_node.color = current_node.parent.color;
586
+ current_node.parent.color = RB_TREE_COLOR_BLACK;
587
+ brother_node.left.color = RB_TREE_COLOR_BLACK;
588
+ this.rotate_right(current_node.parent);
589
+ current_node = this.root; // force exit from loop
590
+ }
591
+ }
592
+ }
593
+
594
+ current_node.color = RB_TREE_COLOR_BLACK;
595
+ }
596
+
597
+ tree_search(node, search_node) {
598
+ if (node == null || node == this.nil_node)
599
+ return undefined;
600
+
601
+ if (search_node.equal_to(node)) {
602
+ return node;
603
+ }
604
+ if (search_node.less_than(node)) {
605
+ return this.tree_search(node.left, search_node);
606
+ }
607
+ else {
608
+ return this.tree_search(node.right, search_node);
609
+ }
610
+ }
611
+
612
+ // Original search_interval method; container res support push() insertion
613
+ // Search all intervals intersecting given one
614
+ tree_search_interval(node, search_node, res) {
615
+ if (node != null && node != this.nil_node) {
616
+ // if (node->left != this.nil_node && node->left->max >= low) {
617
+ if (node.left != this.nil_node && !node.not_intersect_left_subtree(search_node)) {
618
+ this.tree_search_interval(node.left, search_node, res);
619
+ }
620
+ // if (low <= node->high && node->low <= high) {
621
+ if (node.intersect(search_node)) {
622
+ res.push(node);
623
+ }
624
+ // if (node->right != this.nil_node && node->low <= high) {
625
+ if (node.right != this.nil_node && !node.not_intersect_right_subtree(search_node)) {
626
+ this.tree_search_interval(node.right, search_node, res);
627
+ }
628
+ }
629
+ }
630
+
631
+ local_minimum(node) {
632
+ let node_min = node;
633
+ while (node_min.left != null && node_min.left != this.nil_node) {
634
+ node_min = node_min.left;
635
+ }
636
+ return node_min;
637
+ }
638
+
639
+ // not in use
640
+ local_maximum(node) {
641
+ let node_max = node;
642
+ while (node_max.right != null && node_max.right != this.nil_node) {
643
+ node_max = node_max.right;
644
+ }
645
+ return node_max;
646
+ }
647
+
648
+ tree_successor(node) {
649
+ let node_successor;
650
+ let current_node;
651
+ let parent_node;
652
+
653
+ if (node.right != this.nil_node) {
654
+ node_successor = this.local_minimum(node.right);
655
+ }
656
+ else {
657
+ current_node = node;
658
+ parent_node = node.parent;
659
+ while (parent_node != null && parent_node.right == current_node) {
660
+ current_node = parent_node;
661
+ parent_node = parent_node.parent;
662
+ }
663
+ node_successor = parent_node;
664
+ }
665
+ return node_successor;
666
+ }
667
+
668
+ // | right-rotate(T,y) |
669
+ // y ---------------. x
670
+ // / \ / \
671
+ // x c left-rotate(T,x) a y
672
+ // / \ <--------------- / \
673
+ // a b b c
674
+
675
+ rotate_left(x) {
676
+ let y = x.right;
677
+
678
+ x.right = y.left; // b goes to x.right
679
+
680
+ if (y.left != this.nil_node) {
681
+ y.left.parent = x; // x becomes parent of b
682
+ }
683
+ y.parent = x.parent; // move parent
684
+
685
+ if (x == this.root) {
686
+ this.root = y; // y becomes root
687
+ }
688
+ else { // y becomes child of x.parent
689
+ if (x == x.parent.left) {
690
+ x.parent.left = y;
691
+ }
692
+ else {
693
+ x.parent.right = y;
694
+ }
695
+ }
696
+ y.left = x; // x becomes left child of y
697
+ x.parent = y; // and y becomes parent of x
698
+
699
+ if (x != null && x != this.nil_node) {
700
+ x.update_max();
701
+ }
702
+
703
+ y = x.parent;
704
+ if (y != null && y != this.nil_node) {
705
+ y.update_max();
706
+ }
707
+ }
708
+
709
+ rotate_right(y) {
710
+ let x = y.left;
711
+
712
+ y.left = x.right; // b goes to y.left
713
+
714
+ if (x.right != this.nil_node) {
715
+ x.right.parent = y; // y becomes parent of b
716
+ }
717
+ x.parent = y.parent; // move parent
718
+
719
+ if (y == this.root) { // x becomes root
720
+ this.root = x;
721
+ }
722
+ else { // y becomes child of x.parent
723
+ if (y == y.parent.left) {
724
+ y.parent.left = x;
725
+ }
726
+ else {
727
+ y.parent.right = x;
728
+ }
729
+ }
730
+ x.right = y; // y becomes right child of x
731
+ y.parent = x; // and x becomes parent of y
732
+
733
+ if (y != null && y != this.nil_node) {
734
+ y.update_max();
735
+ }
736
+
737
+ x = y.parent;
738
+ if (x != null && x != this.nil_node) {
739
+ x.update_max();
740
+ }
741
+ }
742
+
743
+ tree_walk(node, action) {
744
+ if (node != null && node != this.nil_node) {
745
+ this.tree_walk(node.left, action);
746
+ // arr.push(node.toArray());
747
+ action(node);
748
+ this.tree_walk(node.right, action);
749
+ }
750
+ }
751
+
752
+ /* Return true if all red nodes have exactly two black child nodes */
753
+ testRedBlackProperty() {
754
+ let res = true;
755
+ this.tree_walk(this.root, function (node) {
756
+ if (node.color == RB_TREE_COLOR_RED) {
757
+ if (!(node.left.color == RB_TREE_COLOR_BLACK && node.right.color == RB_TREE_COLOR_BLACK)) {
758
+ res = false;
759
+ }
760
+ }
761
+ });
762
+ return res;
763
+ }
764
+
765
+ /* Throw error if not every path from root to bottom has same black height */
766
+ testBlackHeightProperty(node) {
767
+ let height = 0;
768
+ let heightLeft = 0;
769
+ let heightRight = 0;
770
+ if (node.color == RB_TREE_COLOR_BLACK) {
771
+ height++;
772
+ }
773
+ if (node.left != this.nil_node) {
774
+ heightLeft = this.testBlackHeightProperty(node.left);
775
+ }
776
+ else {
777
+ heightLeft = 1;
778
+ }
779
+ if (node.right != this.nil_node) {
780
+ heightRight = this.testBlackHeightProperty(node.right);
781
+ }
782
+ else {
783
+ heightRight = 1;
784
+ }
785
+ if (heightLeft != heightRight) {
786
+ throw new Error('Red-black height property violated');
787
+ }
788
+ height += heightLeft;
789
+ return height;
790
+ };
791
+ }
792
+
793
+ export default IntervalTree;
794
+ export { Node, Interval };