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