@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.
- package/.babelrc +8 -0
- package/.travis.yml +14 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/main.cjs.js +799 -0
- package/dist/main.esm.js +794 -0
- package/dist/main.umd.js +805 -0
- package/docs/Interval.html +1609 -0
- package/docs/IntervalTree.html +1506 -0
- package/docs/classes_interval.js.html +173 -0
- package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
- package/docs/index.html +197 -0
- package/docs/index.js.html +624 -0
- package/docs/interval.js.html +184 -0
- package/docs/intervalTree.js.html +624 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/styles/jsdoc-default.css +692 -0
- package/docs/styles/prettify-jsdoc.css +111 -0
- package/docs/styles/prettify-tomorrow.css +132 -0
- package/examples/browser/index.html +49 -0
- package/examples/create-react-app/package.json +15 -0
- package/examples/create-react-app/public/index.html +11 -0
- package/examples/create-react-app/src/App.js +38 -0
- package/examples/create-react-app/src/ComposersList.js +15 -0
- package/examples/create-react-app/src/index.js +5 -0
- package/examples/es6-module/index.html +50 -0
- package/examples/nodejs/index.js +23 -0
- package/index.d.ts +65 -0
- package/index.js +6 -0
- package/package.json +52 -0
- package/rollup.config.js +21 -0
- package/src/classes/interval.js +131 -0
- package/src/classes/intervalTree.js +564 -0
- package/src/classes/node.js +97 -0
- package/src/utils/constants.js +16 -0
- package/test/intervalTree.js +232 -0
- package/test/node.js +64 -0
package/dist/main.umd.js
ADDED
|
@@ -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
|
+
}));
|