articulated 1.3.0 → 1.3.2
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/README.md +7 -6
- package/build/commonjs/id_list.d.ts +12 -2
- package/build/commonjs/id_list.js +52 -27
- package/build/commonjs/id_list.js.map +1 -1
- package/build/commonjs/internal/leaf_map.js +2 -5
- package/build/commonjs/internal/leaf_map.js.map +1 -1
- package/build/commonjs/internal/seq_map.d.ts +2 -2
- package/build/commonjs/internal/seq_map.js +2 -5
- package/build/commonjs/internal/seq_map.js.map +1 -1
- package/build/commonjs/vendor/functional-red-black-tree.d.ts +92 -7
- package/build/commonjs/vendor/functional-red-black-tree.js +439 -694
- package/build/commonjs/vendor/functional-red-black-tree.js.map +1 -1
- package/build/esm/id_list.d.ts +12 -2
- package/build/esm/id_list.js +53 -28
- package/build/esm/id_list.js.map +1 -1
- package/build/esm/internal/leaf_map.js +2 -2
- package/build/esm/internal/leaf_map.js.map +1 -1
- package/build/esm/internal/seq_map.d.ts +2 -2
- package/build/esm/internal/seq_map.js +2 -2
- package/build/esm/internal/seq_map.js.map +1 -1
- package/build/esm/vendor/functional-red-black-tree.d.ts +92 -7
- package/build/esm/vendor/functional-red-black-tree.js +434 -695
- package/build/esm/vendor/functional-red-black-tree.js.map +1 -1
- package/package.json +1 -1
- package/src/id_list.ts +62 -29
- package/src/internal/leaf_map.ts +3 -3
- package/src/internal/seq_map.ts +3 -3
- package/src/vendor/functional-red-black-tree.ts +653 -0
- package/src/vendor/functional-red-black-tree.d.ts +0 -177
- package/src/vendor/functional-red-black-tree.js +0 -938
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
// Modified from https://github.com/mikolalysenko/functional-red-black-tree/blob/master/rbtree.js
|
|
2
|
+
// which is MIT Licensed, Copyright (c) 2013 Mikola Lysenko.
|
|
3
|
+
//
|
|
4
|
+
// External types modified from
|
|
5
|
+
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/functional-red-black-tree/functional-red-black-tree-tests.ts
|
|
6
|
+
// which is MIT Licensed.
|
|
7
|
+
|
|
8
|
+
const RED = 0;
|
|
9
|
+
const BLACK = 1;
|
|
10
|
+
type Color = typeof RED | typeof BLACK;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @private Only exported for tests.
|
|
14
|
+
*/
|
|
15
|
+
export class RBNode<K, V> {
|
|
16
|
+
constructor(
|
|
17
|
+
public color: Color,
|
|
18
|
+
/** The key associated with the node. */
|
|
19
|
+
public key: K,
|
|
20
|
+
/** The value associated with the node. */
|
|
21
|
+
public value: V,
|
|
22
|
+
/** The left subtree of the node. */
|
|
23
|
+
public left: RBNode<K, V> | null,
|
|
24
|
+
/** The right subtree of the node. */
|
|
25
|
+
public right: RBNode<K, V> | null
|
|
26
|
+
) {}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cloneNode<K, V>(node: RBNode<K, V>): RBNode<K, V> {
|
|
30
|
+
return new RBNode(node.color, node.key, node.value, node.left, node.right);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function repaint<K, V>(color: Color, node: RBNode<K, V>): RBNode<K, V> {
|
|
34
|
+
return new RBNode(color, node.key, node.value, node.left, node.right);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Represents a functional red-black tree. */
|
|
38
|
+
export class RedBlackTree<K, V> {
|
|
39
|
+
constructor(
|
|
40
|
+
readonly compare: (key1: K, key2: K) => number,
|
|
41
|
+
private readonly root: RBNode<K, V> | null
|
|
42
|
+
) {}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates an empty red-black tree.
|
|
46
|
+
*
|
|
47
|
+
* @param compare Comparison function, same semantics as array.sort().
|
|
48
|
+
* @returns An empty tree ordered by `compare`.
|
|
49
|
+
*/
|
|
50
|
+
static new<K, V>(compare: (key1: K, key2: K) => number): RedBlackTree<K, V> {
|
|
51
|
+
return new RedBlackTree<K, V>(compare, null);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new tree with `key` set to `value`, overwriting any
|
|
56
|
+
* existing value.
|
|
57
|
+
*
|
|
58
|
+
* @param key The key of the item to insert.
|
|
59
|
+
* @param value The value of the item to insert.
|
|
60
|
+
* @returns A new tree with `key` set to `value`.
|
|
61
|
+
*/
|
|
62
|
+
set(key: K, value: V): RedBlackTree<K, V> {
|
|
63
|
+
const cmp = this.compare;
|
|
64
|
+
//Find point to insert/replace node
|
|
65
|
+
let n = this.root;
|
|
66
|
+
const n_stack: RBNode<K, V>[] = [];
|
|
67
|
+
const d_stack: number[] = [];
|
|
68
|
+
let d = 0;
|
|
69
|
+
while (n) {
|
|
70
|
+
d = cmp(key, n.key);
|
|
71
|
+
n_stack.push(n);
|
|
72
|
+
d_stack.push(d);
|
|
73
|
+
// If the keys are equivalent, skip straight to the replace = true case.
|
|
74
|
+
if (d === 0) break;
|
|
75
|
+
else if (d < 0) {
|
|
76
|
+
n = n.left;
|
|
77
|
+
} else {
|
|
78
|
+
n = n.right;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const replace = d === 0 && n_stack.length > 0;
|
|
83
|
+
if (replace) {
|
|
84
|
+
// The last node in the n_stack has key equivalent to `key`.
|
|
85
|
+
// Replace its entry without changing the tree structure.
|
|
86
|
+
const lastN = n_stack[n_stack.length - 1];
|
|
87
|
+
if (lastN.key === key && lastN.value === value) return this;
|
|
88
|
+
n_stack[n_stack.length - 1] = new RBNode(
|
|
89
|
+
lastN.color,
|
|
90
|
+
key,
|
|
91
|
+
value,
|
|
92
|
+
lastN.left,
|
|
93
|
+
lastN.right
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
n_stack.push(new RBNode(RED, key, value, null, null));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//Rebuild path to leaf node
|
|
100
|
+
let s: number;
|
|
101
|
+
for (s = n_stack.length - 2; s >= 0; --s) {
|
|
102
|
+
n = n_stack[s];
|
|
103
|
+
if (d_stack[s] <= 0) {
|
|
104
|
+
n_stack[s] = new RBNode(
|
|
105
|
+
n.color,
|
|
106
|
+
n.key,
|
|
107
|
+
n.value,
|
|
108
|
+
n_stack[s + 1],
|
|
109
|
+
n.right
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
n_stack[s] = new RBNode(
|
|
113
|
+
n.color,
|
|
114
|
+
n.key,
|
|
115
|
+
n.value,
|
|
116
|
+
n.left,
|
|
117
|
+
n_stack[s + 1]
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (replace) return new RedBlackTree<K, V>(cmp, n_stack[0]);
|
|
123
|
+
|
|
124
|
+
//Rebalance tree using rotations
|
|
125
|
+
//console.log("start insert", key, d_stack)
|
|
126
|
+
for (s = n_stack.length - 1; s > 1; --s) {
|
|
127
|
+
const p = n_stack[s - 1];
|
|
128
|
+
n = n_stack[s];
|
|
129
|
+
if (p.color === BLACK || n.color === BLACK) {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
const pp = n_stack[s - 2];
|
|
133
|
+
if (pp.left === p) {
|
|
134
|
+
if (p.left === n) {
|
|
135
|
+
const y = pp.right;
|
|
136
|
+
if (y && y.color === RED) {
|
|
137
|
+
//console.log("LLr")
|
|
138
|
+
p.color = BLACK;
|
|
139
|
+
pp.right = repaint(BLACK, y);
|
|
140
|
+
pp.color = RED;
|
|
141
|
+
s -= 1;
|
|
142
|
+
} else {
|
|
143
|
+
//console.log("LLb")
|
|
144
|
+
pp.color = RED;
|
|
145
|
+
pp.left = p.right;
|
|
146
|
+
p.color = BLACK;
|
|
147
|
+
p.right = pp;
|
|
148
|
+
n_stack[s - 2] = p;
|
|
149
|
+
n_stack[s - 1] = n;
|
|
150
|
+
if (s >= 3) {
|
|
151
|
+
const ppp = n_stack[s - 3];
|
|
152
|
+
if (ppp.left === pp) {
|
|
153
|
+
ppp.left = p;
|
|
154
|
+
} else {
|
|
155
|
+
ppp.right = p;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
const y = pp.right;
|
|
162
|
+
if (y && y.color === RED) {
|
|
163
|
+
//console.log("LRr")
|
|
164
|
+
p.color = BLACK;
|
|
165
|
+
pp.right = repaint(BLACK, y);
|
|
166
|
+
pp.color = RED;
|
|
167
|
+
s -= 1;
|
|
168
|
+
} else {
|
|
169
|
+
//console.log("LRb")
|
|
170
|
+
p.right = n.left;
|
|
171
|
+
pp.color = RED;
|
|
172
|
+
pp.left = n.right;
|
|
173
|
+
n.color = BLACK;
|
|
174
|
+
n.left = p;
|
|
175
|
+
n.right = pp;
|
|
176
|
+
n_stack[s - 2] = n;
|
|
177
|
+
n_stack[s - 1] = p;
|
|
178
|
+
if (s >= 3) {
|
|
179
|
+
const ppp = n_stack[s - 3];
|
|
180
|
+
if (ppp.left === pp) {
|
|
181
|
+
ppp.left = n;
|
|
182
|
+
} else {
|
|
183
|
+
ppp.right = n;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
if (p.right === n) {
|
|
191
|
+
const y = pp.left;
|
|
192
|
+
if (y && y.color === RED) {
|
|
193
|
+
//console.log("RRr", y.key)
|
|
194
|
+
p.color = BLACK;
|
|
195
|
+
pp.left = repaint(BLACK, y);
|
|
196
|
+
pp.color = RED;
|
|
197
|
+
s -= 1;
|
|
198
|
+
} else {
|
|
199
|
+
//console.log("RRb")
|
|
200
|
+
pp.color = RED;
|
|
201
|
+
pp.right = p.left;
|
|
202
|
+
p.color = BLACK;
|
|
203
|
+
p.left = pp;
|
|
204
|
+
n_stack[s - 2] = p;
|
|
205
|
+
n_stack[s - 1] = n;
|
|
206
|
+
if (s >= 3) {
|
|
207
|
+
const ppp = n_stack[s - 3];
|
|
208
|
+
if (ppp.right === pp) {
|
|
209
|
+
ppp.right = p;
|
|
210
|
+
} else {
|
|
211
|
+
ppp.left = p;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
const y = pp.left;
|
|
218
|
+
if (y && y.color === RED) {
|
|
219
|
+
//console.log("RLr")
|
|
220
|
+
p.color = BLACK;
|
|
221
|
+
pp.left = repaint(BLACK, y);
|
|
222
|
+
pp.color = RED;
|
|
223
|
+
s -= 1;
|
|
224
|
+
} else {
|
|
225
|
+
//console.log("RLb")
|
|
226
|
+
p.left = n.right;
|
|
227
|
+
pp.color = RED;
|
|
228
|
+
pp.right = n.left;
|
|
229
|
+
n.color = BLACK;
|
|
230
|
+
n.right = p;
|
|
231
|
+
n.left = pp;
|
|
232
|
+
n_stack[s - 2] = n;
|
|
233
|
+
n_stack[s - 1] = p;
|
|
234
|
+
if (s >= 3) {
|
|
235
|
+
const ppp = n_stack[s - 3];
|
|
236
|
+
if (ppp.right === pp) {
|
|
237
|
+
ppp.right = n;
|
|
238
|
+
} else {
|
|
239
|
+
ppp.left = n;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//Return new tree
|
|
248
|
+
n_stack[0].color = BLACK;
|
|
249
|
+
return new RedBlackTree<K, V>(cmp, n_stack[0]);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Finds the last item in the tree whose key is <= `key`.
|
|
254
|
+
*
|
|
255
|
+
* @param key The key to search for.
|
|
256
|
+
* @returns An iterator at the given element.
|
|
257
|
+
*/
|
|
258
|
+
le(key: K): RedBlackTreeIterator<K, V> {
|
|
259
|
+
const cmp = this.compare;
|
|
260
|
+
let n = this.root;
|
|
261
|
+
const stack: RBNode<K, V>[] = [];
|
|
262
|
+
let last_ptr = 0;
|
|
263
|
+
while (n) {
|
|
264
|
+
const d = cmp(key, n.key);
|
|
265
|
+
stack.push(n);
|
|
266
|
+
if (d >= 0) {
|
|
267
|
+
last_ptr = stack.length;
|
|
268
|
+
}
|
|
269
|
+
if (d < 0) {
|
|
270
|
+
n = n.left;
|
|
271
|
+
} else {
|
|
272
|
+
n = n.right;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
stack.length = last_ptr;
|
|
276
|
+
return new RedBlackTreeIterator<K, V>(this, stack);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @returns An iterator pointing to the first item in the tree with `key`, otherwise null.
|
|
281
|
+
*/
|
|
282
|
+
find(key: K): RedBlackTreeIterator<K, V> {
|
|
283
|
+
const cmp = this.compare;
|
|
284
|
+
let n = this.root;
|
|
285
|
+
const stack: RBNode<K, V>[] = [];
|
|
286
|
+
while (n) {
|
|
287
|
+
const d = cmp(key, n.key);
|
|
288
|
+
stack.push(n);
|
|
289
|
+
if (d === 0) {
|
|
290
|
+
return new RedBlackTreeIterator<K, V>(this, stack);
|
|
291
|
+
}
|
|
292
|
+
if (d <= 0) {
|
|
293
|
+
n = n.left;
|
|
294
|
+
} else {
|
|
295
|
+
n = n.right;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return new RedBlackTreeIterator<K, V>(this, []);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Removes the first item with `key` in the tree.
|
|
303
|
+
*
|
|
304
|
+
* @param key The key of the item to remove.
|
|
305
|
+
* @returns A new tree with the given item removed, if it exists.
|
|
306
|
+
*/
|
|
307
|
+
remove(key: K): RedBlackTree<K, V> {
|
|
308
|
+
const iter = this.find(key);
|
|
309
|
+
return iter.remove();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Retrieves the value associated with `key`.
|
|
314
|
+
*
|
|
315
|
+
* @param key The key of the item to look up.
|
|
316
|
+
* @returns The value of the first node associated with `key`.
|
|
317
|
+
*/
|
|
318
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
319
|
+
get(key: K): V | void {
|
|
320
|
+
const cmp = this.compare;
|
|
321
|
+
let n = this.root;
|
|
322
|
+
while (n) {
|
|
323
|
+
const d = cmp(key, n.key);
|
|
324
|
+
if (d === 0) {
|
|
325
|
+
return n.value;
|
|
326
|
+
}
|
|
327
|
+
if (d <= 0) {
|
|
328
|
+
n = n.left;
|
|
329
|
+
} else {
|
|
330
|
+
n = n.right;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
//Swaps two nodes
|
|
338
|
+
function swapNode<K, V>(n: RBNode<K, V>, v: RBNode<K, V>): void {
|
|
339
|
+
n.key = v.key;
|
|
340
|
+
n.value = v.value;
|
|
341
|
+
n.left = v.left;
|
|
342
|
+
n.right = v.right;
|
|
343
|
+
n.color = v.color;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
//Fix up a double black node in a tree
|
|
347
|
+
function fixDoubleBlack<K, V>(stack: RBNode<K, V>[]): void {
|
|
348
|
+
let n: RBNode<K, V>, p: RBNode<K, V>, s: RBNode<K, V>, z: RBNode<K, V>;
|
|
349
|
+
for (let i = stack.length - 1; i >= 0; --i) {
|
|
350
|
+
n = stack[i];
|
|
351
|
+
if (i === 0) {
|
|
352
|
+
n.color = BLACK;
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
//console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key)
|
|
356
|
+
p = stack[i - 1];
|
|
357
|
+
if (p.left === n) {
|
|
358
|
+
//console.log("left child")
|
|
359
|
+
s = p.right!;
|
|
360
|
+
if (s.right && s.right.color === RED) {
|
|
361
|
+
//console.log("case 1: right sibling child red")
|
|
362
|
+
s = p.right = cloneNode(s);
|
|
363
|
+
z = s.right = cloneNode(s.right!);
|
|
364
|
+
p.right = s.left;
|
|
365
|
+
s.left = p;
|
|
366
|
+
s.right = z;
|
|
367
|
+
s.color = p.color;
|
|
368
|
+
n.color = BLACK;
|
|
369
|
+
p.color = BLACK;
|
|
370
|
+
z.color = BLACK;
|
|
371
|
+
if (i > 1) {
|
|
372
|
+
const pp = stack[i - 2];
|
|
373
|
+
if (pp.left === p) {
|
|
374
|
+
pp.left = s;
|
|
375
|
+
} else {
|
|
376
|
+
pp.right = s;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
stack[i - 1] = s;
|
|
380
|
+
return;
|
|
381
|
+
} else if (s.left && s.left.color === RED) {
|
|
382
|
+
//console.log("case 1: left sibling child red")
|
|
383
|
+
s = p.right = cloneNode(s);
|
|
384
|
+
z = s.left = cloneNode(s.left!);
|
|
385
|
+
p.right = z.left;
|
|
386
|
+
s.left = z.right;
|
|
387
|
+
z.left = p;
|
|
388
|
+
z.right = s;
|
|
389
|
+
z.color = p.color;
|
|
390
|
+
p.color = BLACK;
|
|
391
|
+
s.color = BLACK;
|
|
392
|
+
n.color = BLACK;
|
|
393
|
+
if (i > 1) {
|
|
394
|
+
const pp = stack[i - 2];
|
|
395
|
+
if (pp.left === p) {
|
|
396
|
+
pp.left = z;
|
|
397
|
+
} else {
|
|
398
|
+
pp.right = z;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
stack[i - 1] = z;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (s.color === BLACK) {
|
|
405
|
+
if (p.color === RED) {
|
|
406
|
+
//console.log("case 2: black sibling, red parent", p.right.value)
|
|
407
|
+
p.color = BLACK;
|
|
408
|
+
p.right = repaint(RED, s);
|
|
409
|
+
return;
|
|
410
|
+
} else {
|
|
411
|
+
//console.log("case 2: black sibling, black parent", p.right.value)
|
|
412
|
+
p.right = repaint(RED, s);
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
//console.log("case 3: red sibling")
|
|
417
|
+
s = cloneNode(s);
|
|
418
|
+
p.right = s.left;
|
|
419
|
+
s.left = p;
|
|
420
|
+
s.color = p.color;
|
|
421
|
+
p.color = RED;
|
|
422
|
+
if (i > 1) {
|
|
423
|
+
const pp = stack[i - 2];
|
|
424
|
+
if (pp.left === p) {
|
|
425
|
+
pp.left = s;
|
|
426
|
+
} else {
|
|
427
|
+
pp.right = s;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
stack[i - 1] = s;
|
|
431
|
+
stack[i] = p;
|
|
432
|
+
if (i + 1 < stack.length) {
|
|
433
|
+
stack[i + 1] = n;
|
|
434
|
+
} else {
|
|
435
|
+
stack.push(n);
|
|
436
|
+
}
|
|
437
|
+
i = i + 2;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
//console.log("right child")
|
|
441
|
+
s = p.left!;
|
|
442
|
+
if (s.left && s.left.color === RED) {
|
|
443
|
+
//console.log("case 1: left sibling child red", p.value, p.color)
|
|
444
|
+
s = p.left = cloneNode(s);
|
|
445
|
+
z = s.left = cloneNode(s.left!);
|
|
446
|
+
p.left = s.right;
|
|
447
|
+
s.right = p;
|
|
448
|
+
s.left = z;
|
|
449
|
+
s.color = p.color;
|
|
450
|
+
n.color = BLACK;
|
|
451
|
+
p.color = BLACK;
|
|
452
|
+
z.color = BLACK;
|
|
453
|
+
if (i > 1) {
|
|
454
|
+
const pp = stack[i - 2];
|
|
455
|
+
if (pp.right === p) {
|
|
456
|
+
pp.right = s;
|
|
457
|
+
} else {
|
|
458
|
+
pp.left = s;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
stack[i - 1] = s;
|
|
462
|
+
return;
|
|
463
|
+
} else if (s.right && s.right.color === RED) {
|
|
464
|
+
//console.log("case 1: right sibling child red")
|
|
465
|
+
s = p.left = cloneNode(s);
|
|
466
|
+
z = s.right = cloneNode(s.right!);
|
|
467
|
+
p.left = z.right;
|
|
468
|
+
s.right = z.left;
|
|
469
|
+
z.right = p;
|
|
470
|
+
z.left = s;
|
|
471
|
+
z.color = p.color;
|
|
472
|
+
p.color = BLACK;
|
|
473
|
+
s.color = BLACK;
|
|
474
|
+
n.color = BLACK;
|
|
475
|
+
if (i > 1) {
|
|
476
|
+
const pp = stack[i - 2];
|
|
477
|
+
if (pp.right === p) {
|
|
478
|
+
pp.right = z;
|
|
479
|
+
} else {
|
|
480
|
+
pp.left = z;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
stack[i - 1] = z;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (s.color === BLACK) {
|
|
487
|
+
if (p.color === RED) {
|
|
488
|
+
//console.log("case 2: black sibling, red parent")
|
|
489
|
+
p.color = BLACK;
|
|
490
|
+
p.left = repaint(RED, s);
|
|
491
|
+
return;
|
|
492
|
+
} else {
|
|
493
|
+
//console.log("case 2: black sibling, black parent")
|
|
494
|
+
p.left = repaint(RED, s);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
//console.log("case 3: red sibling")
|
|
499
|
+
s = cloneNode(s);
|
|
500
|
+
p.left = s.right;
|
|
501
|
+
s.right = p;
|
|
502
|
+
s.color = p.color;
|
|
503
|
+
p.color = RED;
|
|
504
|
+
if (i > 1) {
|
|
505
|
+
const pp = stack[i - 2];
|
|
506
|
+
if (pp.right === p) {
|
|
507
|
+
pp.right = s;
|
|
508
|
+
} else {
|
|
509
|
+
pp.left = s;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
stack[i - 1] = s;
|
|
513
|
+
stack[i] = p;
|
|
514
|
+
if (i + 1 < stack.length) {
|
|
515
|
+
stack[i + 1] = n;
|
|
516
|
+
} else {
|
|
517
|
+
stack.push(n);
|
|
518
|
+
}
|
|
519
|
+
i = i + 2;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Iterates through the nodes in a red-black tree. */
|
|
526
|
+
export class RedBlackTreeIterator<K, V> {
|
|
527
|
+
constructor(
|
|
528
|
+
/** The tree associated with the iterator. */
|
|
529
|
+
readonly tree: RedBlackTree<K, V>,
|
|
530
|
+
private readonly stack: RBNode<K, V>[]
|
|
531
|
+
) {}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Removes the iterator's current item form the tree.
|
|
535
|
+
*
|
|
536
|
+
* @returns A new binary search tree with the item removed.
|
|
537
|
+
*/
|
|
538
|
+
remove(): RedBlackTree<K, V> {
|
|
539
|
+
const stack = this.stack;
|
|
540
|
+
if (stack.length === 0) {
|
|
541
|
+
return this.tree;
|
|
542
|
+
}
|
|
543
|
+
//First copy path to node
|
|
544
|
+
const cstack = new Array<RBNode<K, V>>(stack.length);
|
|
545
|
+
let n = stack[stack.length - 1];
|
|
546
|
+
cstack[cstack.length - 1] = new RBNode(
|
|
547
|
+
n.color,
|
|
548
|
+
n.key,
|
|
549
|
+
n.value,
|
|
550
|
+
n.left,
|
|
551
|
+
n.right
|
|
552
|
+
);
|
|
553
|
+
let i: number;
|
|
554
|
+
for (i = stack.length - 2; i >= 0; --i) {
|
|
555
|
+
n = stack[i];
|
|
556
|
+
if (n.left === stack[i + 1]) {
|
|
557
|
+
cstack[i] = new RBNode(n.color, n.key, n.value, cstack[i + 1], n.right);
|
|
558
|
+
} else {
|
|
559
|
+
cstack[i] = new RBNode(n.color, n.key, n.value, n.left, cstack[i + 1]);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
//Get node
|
|
564
|
+
n = cstack[cstack.length - 1];
|
|
565
|
+
//console.log("start remove: ", n.value)
|
|
566
|
+
|
|
567
|
+
//If not leaf, then swap with previous node
|
|
568
|
+
if (n.left && n.right) {
|
|
569
|
+
//console.log("moving to leaf")
|
|
570
|
+
|
|
571
|
+
//First walk to previous leaf
|
|
572
|
+
const split = cstack.length;
|
|
573
|
+
n = n.left;
|
|
574
|
+
while (n.right) {
|
|
575
|
+
cstack.push(n);
|
|
576
|
+
n = n.right;
|
|
577
|
+
}
|
|
578
|
+
//Copy path to leaf
|
|
579
|
+
const v = cstack[split - 1];
|
|
580
|
+
cstack.push(new RBNode(n.color, v.key, v.value, n.left, n.right));
|
|
581
|
+
cstack[split - 1].key = n.key;
|
|
582
|
+
cstack[split - 1].value = n.value;
|
|
583
|
+
|
|
584
|
+
//Fix up stack
|
|
585
|
+
for (i = cstack.length - 2; i >= split; --i) {
|
|
586
|
+
n = cstack[i];
|
|
587
|
+
cstack[i] = new RBNode(n.color, n.key, n.value, n.left, cstack[i + 1]);
|
|
588
|
+
}
|
|
589
|
+
cstack[split - 1].left = cstack[split];
|
|
590
|
+
}
|
|
591
|
+
//console.log("stack=", cstack.map(function(v) { return v.value }))
|
|
592
|
+
|
|
593
|
+
//Remove leaf node
|
|
594
|
+
n = cstack[cstack.length - 1];
|
|
595
|
+
if (n.color === RED) {
|
|
596
|
+
//Easy case: removing red leaf
|
|
597
|
+
//console.log("RED leaf")
|
|
598
|
+
const p = cstack[cstack.length - 2];
|
|
599
|
+
if (p.left === n) {
|
|
600
|
+
p.left = null;
|
|
601
|
+
} else if (p.right === n) {
|
|
602
|
+
p.right = null;
|
|
603
|
+
}
|
|
604
|
+
cstack.pop();
|
|
605
|
+
return new RedBlackTree<K, V>(this.tree.compare, cstack[0]);
|
|
606
|
+
} else {
|
|
607
|
+
if (n.left || n.right) {
|
|
608
|
+
//Second easy case: Single child black parent
|
|
609
|
+
//console.log("BLACK single child")
|
|
610
|
+
if (n.left) {
|
|
611
|
+
swapNode(n, n.left);
|
|
612
|
+
} else if (n.right) {
|
|
613
|
+
swapNode(n, n.right);
|
|
614
|
+
}
|
|
615
|
+
//Child must be red, so repaint it black to balance color
|
|
616
|
+
n.color = BLACK;
|
|
617
|
+
return new RedBlackTree<K, V>(this.tree.compare, cstack[0]);
|
|
618
|
+
} else if (cstack.length === 1) {
|
|
619
|
+
//Third easy case: root
|
|
620
|
+
//console.log("ROOT")
|
|
621
|
+
return new RedBlackTree<K, V>(this.tree.compare, null);
|
|
622
|
+
} else {
|
|
623
|
+
//Hard case: Repaint n, and then do some nasty stuff
|
|
624
|
+
//console.log("BLACK leaf no children")
|
|
625
|
+
const parent = cstack[cstack.length - 2];
|
|
626
|
+
fixDoubleBlack(cstack);
|
|
627
|
+
//Fix up links
|
|
628
|
+
if (parent.left === n) {
|
|
629
|
+
parent.left = null;
|
|
630
|
+
} else {
|
|
631
|
+
parent.right = null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return new RedBlackTree<K, V>(this.tree.compare, cstack[0]);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** The key of the iterator's current item. */
|
|
639
|
+
get key(): K | undefined {
|
|
640
|
+
if (this.stack.length > 0) {
|
|
641
|
+
return this.stack[this.stack.length - 1].key;
|
|
642
|
+
}
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/** The value of the iterator's current item. */
|
|
647
|
+
get value(): V | undefined {
|
|
648
|
+
if (this.stack.length > 0) {
|
|
649
|
+
return this.stack[this.stack.length - 1].value;
|
|
650
|
+
}
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
}
|