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