articulated 1.3.1 → 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.
@@ -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
+ }