@quenty/observablecollection 12.11.1 → 12.12.0

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,1171 @@
1
+ --[=[
2
+ Used by [ObservableSortedList] to maintain a red-black binary search tree.
3
+
4
+ @class SortedNode
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local ListIndexUtils = require("ListIndexUtils")
10
+ local DuckTypeUtils = require("DuckTypeUtils")
11
+ local Table = require("Table")
12
+
13
+ local DEBUG_ASSERTION_SLOW = false
14
+
15
+ local Color = Table.readonly({
16
+ BLACK = "BLACK";
17
+ RED = "RED";
18
+ })
19
+
20
+ local SortedNode = {}
21
+ SortedNode.ClassName = "SortedNode"
22
+ SortedNode.__index = SortedNode
23
+
24
+ export type SortedNode<T> = typeof(setmetatable({
25
+ left = nil :: SortedNode<T>?,
26
+ right = nil :: SortedNode<T>?,
27
+ color = nil :: "B" | "R";
28
+ value = nil :: number,
29
+ descendantCount = nil :: number,
30
+ data = nil :: T
31
+ }, SortedNode))
32
+
33
+ function SortedNode.new(data): SortedNode<T>
34
+ assert(data ~= nil, "Bad data")
35
+
36
+ local self = setmetatable({}, SortedNode)
37
+
38
+ self.data = data
39
+ self.color = Color.RED
40
+ self.descendantCount = 1
41
+
42
+ return self
43
+ end
44
+
45
+ function SortedNode.isSortedNode(value)
46
+ return DuckTypeUtils.isImplementation(SortedNode, value)
47
+ end
48
+
49
+ function SortedNode:IterateNodes()
50
+ return coroutine.wrap(function()
51
+ local stack = {}
52
+ local current = self
53
+ local index = 1
54
+
55
+ while current or #stack > 0 do
56
+ -- Reach the leftmost node of the current node
57
+ while current ~= nil do
58
+ table.insert(stack, current)
59
+ current = current.left
60
+ end
61
+
62
+ current = table.remove(stack)
63
+ coroutine.yield(index, current)
64
+ index += 1
65
+ current = current.right
66
+ end
67
+ end)
68
+ end
69
+
70
+ function SortedNode:IterateData()
71
+ return coroutine.wrap(function()
72
+ local stack = {}
73
+ local current = self
74
+ local index = 1
75
+
76
+ while current or #stack > 0 do
77
+ -- Reach the leftmost node of the current node
78
+ while current ~= nil do
79
+ table.insert(stack, current)
80
+ current = current.left
81
+ end
82
+
83
+ current = table.remove(stack)
84
+ coroutine.yield(index, current.data)
85
+ index += 1
86
+ current = current.right
87
+ end
88
+ end)
89
+ end
90
+
91
+ --[=[
92
+ Inclusive iterator like string.sub. Faster than skipping because we
93
+ binary search our initial node
94
+
95
+ @param start number
96
+ @param finish number
97
+ @return (T) -> ((T, nextIndex: any) -> ...any, T?)
98
+ ]=]
99
+ function SortedNode:IterateNodesRange(start, finish)
100
+ assert(type(start) == "number", "Bad start")
101
+ assert(type(finish) == "number" or finish == nil, "Bad finish")
102
+ assert(self.parent == nil, "Should only be called on root")
103
+
104
+ if start == 1 and (finish == nil or finish == -1) then
105
+ return self:IterateNodes()
106
+ end
107
+
108
+ return coroutine.wrap(function()
109
+ local target = ListIndexUtils.toPositiveIndex(self.descendantCount, start)
110
+ local endTarget = ListIndexUtils.toPositiveIndex(self.descendantCount, finish or -1)
111
+ local current = self:FindNodeAtIndex(target)
112
+
113
+ -- We're out of range
114
+ if not current then
115
+ return
116
+ end
117
+
118
+ local index = target
119
+
120
+ while current do
121
+ coroutine.yield(index, current)
122
+ index += 1
123
+
124
+ if index > endTarget then
125
+ return
126
+ end
127
+
128
+ -- Emit right most tree first
129
+ if current.right then
130
+ for _, value in current.right:IterateNodes() do
131
+ coroutine.yield(index, value)
132
+ index += 1
133
+
134
+ if index > endTarget then
135
+ return
136
+ end
137
+ end
138
+ end
139
+
140
+ -- Skip all scenarios where we're on the right
141
+ while current.parent and current:_isOnRight() do
142
+ current = current.parent
143
+ end
144
+
145
+ current = current.parent
146
+ end
147
+ end)
148
+ end
149
+
150
+ function SortedNode:FindNodeAtIndex(searchIndex)
151
+ assert(type(searchIndex) == "number", "Bad searchIndex")
152
+ assert(self.parent == nil, "Should only be called on root")
153
+
154
+ local target = ListIndexUtils.toPositiveIndex(self.descendantCount, searchIndex)
155
+ if target > self.descendantCount or target <= 0 then
156
+ return nil
157
+ end
158
+
159
+ local current = self
160
+ local index = 1
161
+ if self.left then
162
+ index += self.left.descendantCount
163
+ end
164
+
165
+ while current do
166
+ if index == target then
167
+ return current
168
+ elseif target < index then
169
+ current = current.left
170
+ index -= 1
171
+ if current.right ~= nil then
172
+ index -= current.right.descendantCount
173
+ end
174
+ else
175
+ current = current.right
176
+ index += 1
177
+ if current.left ~= nil then
178
+ index += current.left.descendantCount
179
+ end
180
+ end
181
+ end
182
+
183
+ return nil
184
+ end
185
+
186
+ function SortedNode:FindNodeIndex(node)
187
+ assert(SortedNode.isSortedNode(node), "Bad node")
188
+ assert(self.parent == nil, "Should only be called on root")
189
+
190
+ -- TODO: Don't iterate twice
191
+ if self:ContainsNode(node) then
192
+ return node:GetIndex()
193
+ else
194
+ return nil
195
+ end
196
+ end
197
+
198
+ function SortedNode:GetIndex(): number
199
+ local index = 1
200
+
201
+ if self.left then
202
+ index += self.left.descendantCount
203
+ end
204
+
205
+ local current = self
206
+ while current.parent ~= nil do
207
+ if current == current.parent.right then
208
+ index += 1
209
+
210
+ if current.parent.left then
211
+ index += current.parent.left.descendantCount
212
+ end
213
+ end
214
+
215
+ current = current.parent
216
+ end
217
+
218
+ return index
219
+ end
220
+
221
+ function SortedNode:FindFirstNodeForData(data)
222
+ -- TODO: This is a linear search, very bad
223
+
224
+ for _, current in self:IterateNodes() do
225
+ if current.data == data then
226
+ return current
227
+ end
228
+ end
229
+
230
+ return nil
231
+ end
232
+
233
+ function SortedNode:NeedsToMove(root, newValue)
234
+ assert(newValue ~= nil, "Bad newValue")
235
+
236
+ if self.parent ~= nil then
237
+ if self:_isOnLeft() then
238
+ if self.parent.value < newValue then
239
+ return true
240
+ end
241
+ else
242
+ if self.parent.value > newValue then
243
+ return true
244
+ end
245
+ end
246
+ else
247
+ if self ~= root or root == nil then
248
+ return true
249
+ end
250
+ end
251
+
252
+ if self.left and self.left.value > newValue then
253
+ return true
254
+ end
255
+
256
+ if self.right and self.right.value < newValue then
257
+ return true
258
+ end
259
+
260
+ return false
261
+ end
262
+
263
+ --[=[
264
+ Returns true if the node is contained within the parent node
265
+ ]=]
266
+ function SortedNode:ContainsNode(node: SortedNode): boolean
267
+ assert(SortedNode.isSortedNode(node), "Bad SortedNode")
268
+
269
+ local current = node
270
+ while current do
271
+ if current == self then
272
+ return true
273
+ end
274
+
275
+ current = current.parent
276
+ end
277
+
278
+ return false
279
+ end
280
+
281
+ function SortedNode:MarkBlack()
282
+ self.color = Color.BLACK
283
+ end
284
+
285
+ function SortedNode:InsertNode(node): SortedNode<T>
286
+ assert(SortedNode.isSortedNode(node), "Bad SortedNode")
287
+ assert(self.parent == nil, "Should only be called on root")
288
+ assert(node.parent == nil, "Already parented")
289
+ assert(node.left == nil, "Already has left child")
290
+ assert(node.right == nil, "Already has right child")
291
+
292
+ local root = self
293
+ local originalCount = root.descendantCount
294
+
295
+ node.color = Color.RED
296
+
297
+ local parent = nil
298
+ local current = root
299
+
300
+ while current ~= nil do
301
+ parent = current
302
+ if node.value < current.value then
303
+ current = current.left
304
+ else
305
+ current = current.right
306
+ end
307
+ end
308
+
309
+ if parent == nil then
310
+ root = node
311
+ elseif node.value < parent.value then
312
+ parent:_setLeft(node)
313
+ else
314
+ parent:_setRight(node)
315
+ end
316
+
317
+ -- Fix the tree after insertion
318
+ root = self:_fixDoubleRed(root, node)
319
+
320
+ if DEBUG_ASSERTION_SLOW then
321
+ root:_assertIntegrity()
322
+ root:_assertRootIntegrity()
323
+ root:_assertFullIntegritySlow()
324
+ root:_assertRedBlackIntegrity()
325
+ root:_assertRedBlackFullIntegritySlow()
326
+ root:_assertDescendantCount(originalCount + 1)
327
+ end
328
+
329
+ return root
330
+ end
331
+
332
+ function SortedNode:_leftRotate(root, node): SortedNode<T>
333
+ assert(root, "No root")
334
+ assert(node, "No node")
335
+
336
+ local newParent = node.right
337
+ node:_setRight(newParent.left)
338
+
339
+ if node == root then
340
+ newParent:_unparent()
341
+ root = newParent
342
+ elseif node == node.parent.right then
343
+ node.parent:_setRight(newParent)
344
+ elseif node == node.parent.left then
345
+ node.parent:_setLeft(newParent)
346
+ else
347
+ error("Bad state")
348
+ end
349
+
350
+ newParent:_setLeft(node)
351
+
352
+ return root
353
+ end
354
+
355
+ function SortedNode:_rightRotate(root, node): SortedNode<T>
356
+ assert(root, "No root")
357
+ assert(node, "No node")
358
+
359
+ local newParent = node.left
360
+ node:_setLeft(newParent.right)
361
+
362
+ if node == root then
363
+ newParent:_unparent()
364
+ root = newParent
365
+ elseif node == node.parent.right then
366
+ node.parent:_setRight(newParent)
367
+ elseif node == node.parent.left then
368
+ node.parent:_setLeft(newParent)
369
+ else
370
+ error("Bad state")
371
+ end
372
+
373
+ newParent:_setRight(node)
374
+
375
+ return root
376
+ end
377
+
378
+ function SortedNode:_swapColors(other)
379
+ self.color, other.color = other.color, self.color
380
+ end
381
+
382
+ function SortedNode:_fixDoubleRed(root, node): SortedNode
383
+ if node == root then
384
+ node.color = Color.BLACK
385
+ return root
386
+ end
387
+
388
+ local parent = node.parent
389
+ local grandparent = node.parent and node.parent.parent
390
+ local uncle = node:_uncle()
391
+
392
+ if not grandparent then
393
+ return root
394
+ end
395
+
396
+ if parent.color == Color.BLACK then
397
+ return root
398
+ end
399
+
400
+ if uncle and uncle.color == Color.RED then
401
+ parent.color = Color.BLACK
402
+ uncle.color = Color.BLACK
403
+ grandparent.color = Color.RED
404
+
405
+ root = self:_fixDoubleRed(root, grandparent)
406
+ else
407
+ -- Rotate
408
+ if grandparent.left == parent then
409
+ if parent.left == node then
410
+ parent:_swapColors(grandparent)
411
+ elseif parent.right == node then
412
+ root = self:_leftRotate(root, parent)
413
+ node:_swapColors(grandparent)
414
+ else
415
+ error("Bad state")
416
+ end
417
+
418
+ root = self:_rightRotate(root, grandparent)
419
+ elseif grandparent.right == parent then
420
+ if parent.left == node then
421
+ root = self:_rightRotate(root, parent)
422
+ node:_swapColors(grandparent)
423
+ elseif parent.right == node then
424
+ parent:_swapColors(grandparent)
425
+ else
426
+ error("Bad state")
427
+ end
428
+
429
+ root = self:_leftRotate(root, grandparent)
430
+ else
431
+ error("Bad state")
432
+ end
433
+ end
434
+
435
+ return root
436
+ end
437
+
438
+ function SortedNode:_setLeft(node: SortedNode)
439
+ assert(node ~= self, "Cannot assign to self")
440
+
441
+ if self.left == node then
442
+ return
443
+ end
444
+
445
+ if self.left then
446
+ self.left.parent = nil
447
+ self.left = nil
448
+ end
449
+
450
+ if node then
451
+ if node.parent then
452
+ node:_unparent()
453
+ end
454
+
455
+ self.left = node
456
+ self.left.parent = self
457
+ end
458
+
459
+ self:_updateAllParentDescendantCount()
460
+ end
461
+
462
+ function SortedNode:_setRight(node: SortedNode)
463
+ assert(node ~= self, "Cannot assign to self")
464
+
465
+ if self.right == node then
466
+ return
467
+ end
468
+
469
+
470
+ if self.right then
471
+ self.right.parent = nil
472
+ self.right = nil
473
+ end
474
+
475
+ if node then
476
+ if node.parent then
477
+ node:_unparent()
478
+ end
479
+
480
+ self.right = node
481
+ self.right.parent = self
482
+ end
483
+
484
+ self:_updateAllParentDescendantCount()
485
+ end
486
+
487
+ function SortedNode:_updateAllParentDescendantCount()
488
+ local current = self
489
+ while current do
490
+ local descendantCount = 1
491
+ local left = current.left
492
+ if left then
493
+ descendantCount += left.descendantCount
494
+ end
495
+ local right = current.right
496
+ if right then
497
+ descendantCount += right.descendantCount
498
+ end
499
+
500
+ current.descendantCount = descendantCount
501
+ current = current.parent
502
+ end
503
+ end
504
+
505
+ function SortedNode:RemoveNode(node: SortedNode): SortedNode
506
+ assert(SortedNode.isSortedNode(node), "Bad SortedNode")
507
+ assert(self.parent == nil, "Should only be called on root")
508
+
509
+ local root = self
510
+ local originalCount = root.descendantCount
511
+
512
+ if not root:ContainsNode(node) then
513
+ return self
514
+ end
515
+
516
+ root = self:_removeNodeHelper(root, node)
517
+
518
+ if DEBUG_ASSERTION_SLOW then
519
+ if root then
520
+ root:_assertIntegrity()
521
+ root:_assertRootIntegrity()
522
+ root:_assertFullIntegritySlow()
523
+ root:_assertRedBlackIntegrity()
524
+ root:_assertRedBlackFullIntegritySlow()
525
+
526
+ root:_assertDescendantCount(originalCount - 1)
527
+ else
528
+ if originalCount ~= 1 then
529
+ error(string.format("Removed %d nodes instead of 1", originalCount - 1))
530
+ end
531
+ end
532
+ end
533
+
534
+ return root
535
+ end
536
+
537
+ function SortedNode:_removeNodeHelper(root, node, depth)
538
+ assert(root, "Bad root")
539
+ assert(node, "Bad node")
540
+ depth = (depth or 0) + 1
541
+
542
+ if depth > 2 then
543
+ error("Should not recursively call remove node helper more than once")
544
+ end
545
+
546
+ local replacement = self:_findReplacement(node)
547
+ local bothBlack = (replacement == nil or replacement.color == Color.BLACK) and node.color == Color.BLACK
548
+ local parent = node.parent
549
+
550
+ if replacement == nil then
551
+ -- Node is a leaf or only has 1 child
552
+ if node == root then
553
+ root = nil
554
+ else
555
+ if bothBlack then
556
+ root = self:_fixDoubleBlack(root, node)
557
+ assert(root, "Should have root")
558
+ else
559
+ local sibling = node:_sibling()
560
+ if sibling then
561
+ sibling.color = Color.RED
562
+ end
563
+ end
564
+
565
+ assert(node.descendantCount == 1, "Cannot unparent")
566
+ node:_unparent()
567
+ end
568
+ elseif node.left == nil or node.right == nil then
569
+ -- Node to be deleted has only one child
570
+
571
+ if node == root then
572
+ root = self:_swapNodes(root, node, replacement)
573
+ root = self:_removeNodeHelper(root, node, depth)
574
+ else
575
+ assert(node.parent, "Node must have parent")
576
+
577
+ if node:_isOnLeft() then
578
+ parent:_setLeft(replacement)
579
+ elseif node:_isOnRight() then
580
+ parent:_setRight(replacement)
581
+ else
582
+ error("Bad state")
583
+ end
584
+
585
+ if bothBlack then
586
+ root = self:_fixDoubleBlack(root, replacement)
587
+ assert(root, "Should have root")
588
+ else
589
+ -- One of these are red, swap to black
590
+ replacement.color = Color.BLACK
591
+ end
592
+ end
593
+ else
594
+ -- two children
595
+ root = self:_swapNodes(root, node, replacement)
596
+ root = self:_removeNodeHelper(root, node, depth)
597
+ end
598
+
599
+ if DEBUG_ASSERTION_SLOW then
600
+ if root then
601
+ root:_assertIntegrity()
602
+ root:_assertRootIntegrity()
603
+ root:_assertFullIntegritySlow()
604
+ root:_assertRedBlackIntegrity()
605
+ root:_assertRedBlackFullIntegritySlow()
606
+ end
607
+ end
608
+
609
+ return root
610
+ end
611
+
612
+ function SortedNode:_swapNodes(root, node, replacement)
613
+ assert(root, "No root")
614
+ assert(node, "No node")
615
+ assert(replacement, "No replacement")
616
+ assert(node ~= replacement, "Node can not be the replacement")
617
+
618
+ -- In direct descendant scenario node is always parent
619
+ if replacement:ContainsNode(node) then
620
+ node, replacement = replacement, node
621
+ end
622
+
623
+ assert(replacement ~= root, "Replacement cannot be root")
624
+ assert(replacement.parent, "Replacement must have parent")
625
+ local descendantCount = root.descendantCount
626
+
627
+ local nodeParent = node.parent
628
+ local nodeLeft = node.left
629
+ local nodeRight = node.right
630
+ local nodeOnLeft = nodeParent and node:_isOnLeft()
631
+ local nodeColor = node.color
632
+ local replacementLeft = replacement.left
633
+ local replacementRight = replacement.right
634
+ local replacementParent = replacement.parent
635
+ local replacementOnLeft = replacement:_isOnLeft()
636
+ local replacementColor = replacement.color
637
+
638
+ if replacement.parent == node then
639
+ node:_unparent()
640
+ replacement:_unparent()
641
+
642
+ -- Special case for direct descendants, node is always parent in this scenario
643
+ if nodeParent == nil then
644
+ if node == root then
645
+ root = replacement
646
+ else
647
+ error("Should be root if our item's parent is nil")
648
+ end
649
+ elseif nodeOnLeft then
650
+ nodeParent:_setLeft(replacement)
651
+ else
652
+ nodeParent:_setRight(replacement)
653
+ end
654
+
655
+ -- Transformed to: Replacement -> Node
656
+ if replacementOnLeft then
657
+ replacement:_setLeft(node)
658
+ replacement:_setRight(nodeRight)
659
+ else
660
+ replacement:_setRight(node)
661
+ replacement:_setLeft(nodeLeft)
662
+ end
663
+
664
+ node:_setLeft(replacementLeft)
665
+ node:_setRight(replacementRight)
666
+
667
+ if DEBUG_ASSERTION_SLOW then
668
+ assert(node.parent == replacement, "Swap failed on node.parent")
669
+ assert(replacement.parent == nodeParent, "Swap failed on replacement.parent")
670
+ assert(node.left == replacementLeft, "Swap failed on node.left")
671
+ assert(node.right == replacementRight, "Swap failed on node.right")
672
+ end
673
+ else
674
+ node:_unparent()
675
+ replacement:_unparent()
676
+
677
+ -- Unparent everything
678
+ node:_setLeft(replacementLeft)
679
+ node:_setRight(replacementRight)
680
+ replacement:_setLeft(nodeLeft)
681
+ replacement:_setRight(nodeRight)
682
+
683
+ if nodeParent == nil then
684
+ if node == root then
685
+ root = replacement
686
+ else
687
+ error("Bad state")
688
+ end
689
+ elseif nodeOnLeft then
690
+ nodeParent:_setLeft(replacement)
691
+ else
692
+ nodeParent:_setRight(replacement)
693
+ end
694
+
695
+ if replacementOnLeft then
696
+ replacementParent:_setLeft(node)
697
+ else
698
+ replacementParent:_setRight(node)
699
+ end
700
+
701
+ if DEBUG_ASSERTION_SLOW then
702
+ assert(node.parent == replacementParent, "Swap failed on node.parent")
703
+ assert(replacement.parent == nodeParent, "Swap failed on replacement.parent")
704
+ assert(node.left == replacementLeft, "Swap failed on node.left")
705
+ assert(node.right == replacementRight, "Swap failed on node.right")
706
+ assert(replacement.left == nodeLeft, "Swap failed on replacement.left")
707
+ assert(replacement.right == nodeRight, "Swap failed on replacement.right")
708
+ end
709
+ end
710
+
711
+ node.color = replacementColor
712
+ replacement.color = nodeColor
713
+
714
+ if DEBUG_ASSERTION_SLOW then
715
+ root:_assertDescendantCount(descendantCount)
716
+ end
717
+
718
+ return root
719
+ end
720
+
721
+ function SortedNode:_findReplacement(node)
722
+ if node.left and node.right then
723
+ return node.right:_successor()
724
+ end
725
+
726
+ if node.left and node.right then
727
+ return nil
728
+ end
729
+
730
+ if node.left then
731
+ return node.left
732
+ else
733
+ return node.right
734
+ end
735
+ end
736
+
737
+ function SortedNode:_successor()
738
+ local node = self
739
+ while node.left ~= nil do
740
+ node = node.left
741
+ end
742
+ return node
743
+ end
744
+
745
+ --[[
746
+ https://www.geeksforgeeks.org/deletion-in-red-black-tree/?ref=oin_asr9
747
+ ]]
748
+ function SortedNode:_fixDoubleBlack(root, node)
749
+ assert(root, "No root")
750
+ assert(node, "No node")
751
+
752
+ if node == root then
753
+ return root
754
+ end
755
+
756
+ assert(node.parent, "Should have parent")
757
+
758
+ local sibling = node:_sibling()
759
+ local parent = node.parent
760
+
761
+ if sibling == nil then
762
+ return self:_fixDoubleBlack(root, parent)
763
+ end
764
+
765
+ if sibling.color == Color.RED then
766
+ parent.color = Color.RED
767
+ sibling.color = Color.BLACK
768
+
769
+ if sibling:_isOnLeft() then
770
+ -- Left case
771
+ root = self:_rightRotate(root, parent)
772
+ elseif parent.right == sibling then
773
+ -- Right case
774
+ root = self:_leftRotate(root, parent)
775
+ else
776
+ error("Bad state")
777
+ end
778
+
779
+ root = self:_fixDoubleBlack(root, node)
780
+ elseif sibling.color == Color.BLACK then
781
+ if sibling:_hasRedChild() then
782
+ -- At least 1 red child
783
+
784
+ if sibling.left and sibling.left.color == Color.RED then
785
+ if sibling:_isOnLeft() then
786
+ -- Left-left
787
+ sibling.left.color = Color.BLACK
788
+ sibling.color = parent.color
789
+ parent.color = Color.BLACK -- This should be true, but the guide I'm following doesn't specify this?
790
+ root = self:_rightRotate(root, parent)
791
+ else
792
+ -- Right-left
793
+ sibling.left.color = parent.color
794
+ parent.color = Color.BLACK -- This should be true, but the guide I'm following doesn't specify this?
795
+ root = self:_rightRotate(root, sibling)
796
+ root = self:_leftRotate(root, parent)
797
+ end
798
+ else
799
+ if sibling:_isOnLeft() then
800
+ -- Left-right
801
+ sibling.right.color = parent.color
802
+ parent.color = Color.BLACK -- This should be true, but the guide I'm following doesn't specify this?
803
+ root = self:_leftRotate(root, sibling)
804
+ root = self:_rightRotate(root, parent)
805
+ else
806
+ -- Right-right
807
+ sibling.right.color = sibling.color
808
+ sibling.color = parent.color
809
+ parent.color = Color.BLACK -- This should be true, but the guide I'm following doesn't specify this?
810
+ root = self:_leftRotate(root, parent)
811
+ end
812
+ end
813
+ else
814
+ -- 2 black children
815
+ sibling.color = Color.RED
816
+ if parent.color == Color.BLACK then
817
+ root = self:_fixDoubleBlack(root, parent)
818
+ else
819
+ parent.color = Color.BLACK
820
+ end
821
+ end
822
+ else
823
+ error("Bad state")
824
+ end
825
+
826
+ return root
827
+ end
828
+
829
+ function SortedNode:_isOnLeft()
830
+ assert(self.parent, "Must have parent to invoke this method")
831
+
832
+ return self.parent.left == self
833
+ end
834
+
835
+ function SortedNode:_isOnRight()
836
+ assert(self.parent, "Must have parent to invoke this method")
837
+
838
+ return self.parent.right == self
839
+ end
840
+
841
+ function SortedNode:_hasRedChild()
842
+ if self.left and self.left.color == Color.RED then
843
+ return true
844
+ end
845
+
846
+ if self.right and self.right.color == Color.RED then
847
+ return true
848
+ end
849
+
850
+ return false
851
+ end
852
+
853
+ function SortedNode:_unparent()
854
+ if not self.parent then
855
+ return
856
+ elseif self.parent.left == self then
857
+ self.parent:_setLeft(nil)
858
+ elseif self.parent.right == self then
859
+ self.parent:_setRight(nil)
860
+ else
861
+ error("Bad state")
862
+ end
863
+ end
864
+
865
+
866
+ function SortedNode:_uncle()
867
+ local grandparent = self:_grandparent()
868
+ if not grandparent then
869
+ return nil
870
+ end
871
+
872
+ if self.parent == grandparent.left then
873
+ return grandparent.right
874
+ elseif self.parent == grandparent.right then
875
+ return grandparent.left
876
+ else
877
+ return nil
878
+ end
879
+ end
880
+
881
+ function SortedNode:_sibling()
882
+ if self.parent then
883
+ if self == self.parent.left then
884
+ return self.parent.right
885
+ elseif self == self.parent.right then
886
+ return self.parent.left
887
+ else
888
+ error("Bad state")
889
+ end
890
+ else
891
+ return nil
892
+ end
893
+ end
894
+
895
+ function SortedNode:_grandparent()
896
+ if self.parent then
897
+ return self.parent.parent
898
+ else
899
+ return nil
900
+ end
901
+ end
902
+
903
+ function SortedNode:__tostring()
904
+ local result
905
+ if self.parent == nil then
906
+ result = "BinarySearchTree\n"
907
+ else
908
+ result = "SortedNode\n"
909
+ end
910
+
911
+ local stack = {} -- Stack to hold nodes and their details
912
+ local seen = {}
913
+ table.insert(stack, { node = self, indent = "", isLeft = false })
914
+
915
+ while #stack > 0 do
916
+ local current = table.remove(stack) -- Pop from the stack
917
+ local wasSeen
918
+
919
+ if current.node then
920
+ wasSeen = seen[current.node]
921
+ seen[current.node] = true
922
+ else
923
+ wasSeen = false
924
+ end
925
+
926
+ local node = current.node
927
+ local indent = current.indent
928
+ local isLeft = current.isLeft
929
+
930
+ -- Add current node to result with indentation
931
+ result = result .. indent
932
+ if isLeft then
933
+ result = result .. "├── "
934
+ indent = indent .. "│ "
935
+ else
936
+ result = result .. "└── "
937
+ indent = indent .. " "
938
+ end
939
+
940
+ if node then
941
+ local text = string.format("SortedNode { index=%d, value=%s, descendants=%d, color=%s }",
942
+ node:GetIndex(),
943
+ tostring(node.value),
944
+ node.descendantCount,
945
+ node.color)
946
+
947
+ if wasSeen then
948
+ result = result .. "<LOOPED> "
949
+ end
950
+
951
+ result = result .. text .. "\n"
952
+ else
953
+ result = result .. "nil" .. "\n"
954
+ end
955
+
956
+ if node and not wasSeen and (node.left or node.right) then
957
+ -- Push right and left children to the stack with updated indentation
958
+ -- Right child is pushed first so that left child is processed first
959
+ table.insert(stack, { node = node.right, indent = indent, isLeft = false })
960
+ table.insert(stack, { node = node.left, indent = indent, isLeft = true })
961
+ end
962
+ end
963
+
964
+ return result
965
+ end
966
+
967
+ function SortedNode:_childCount()
968
+ if self.left == nil and self.right == nil then
969
+ return 0
970
+ elseif self.left and self.right then
971
+ return 1
972
+ else
973
+ return 2
974
+ end
975
+ end
976
+
977
+ function SortedNode:_debugGetRoot()
978
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
979
+
980
+ local seen = {}
981
+ local root = self
982
+ seen[root] = true
983
+
984
+ while root.parent ~= nil do
985
+ root = root.parent
986
+ if seen[root] then
987
+ error("Loop in parents")
988
+ end
989
+ seen[root] = true
990
+ end
991
+
992
+ return root
993
+ end
994
+
995
+ function SortedNode:_assertRedBlackIntegrity()
996
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
997
+
998
+ -- https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
999
+ if self.color == Color.RED then
1000
+ -- Check adjacency
1001
+ if self.left then
1002
+ if self.left.color == Color.RED then
1003
+ error(string.format("A red node should not have a red child %s\n%s", tostring(self:_debugGetRoot()), tostring(self)))
1004
+ end
1005
+ end
1006
+
1007
+ if self.right then
1008
+ if self.right.color == Color.RED then
1009
+ error(string.format("A red node should not have a red child %s\n%s", tostring(self:_debugGetRoot()), tostring(self)))
1010
+ end
1011
+ end
1012
+
1013
+ if self.parent then
1014
+ if self.parent.color == Color.RED then
1015
+ error(string.format("A red node should not be have a red parent %s\n%s", tostring(self:_debugGetRoot()), tostring(self)))
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ if self.left ~= nil and self.right == nil then
1021
+ if self.left.color ~= Color.RED then
1022
+ error(string.format("Any node with 1 child must be red %s\n%s", tostring(self:_debugGetRoot()), tostring(self)))
1023
+ end
1024
+ end
1025
+
1026
+ if self.left == nil and self.right ~= nil then
1027
+ if self.right.color ~= Color.RED then
1028
+ error(string.format("Any node with 1 child must be red %s\n%s", tostring(self:_debugGetRoot()), tostring(self)))
1029
+ end
1030
+ end
1031
+ end
1032
+
1033
+ function SortedNode:_assertRedBlackFullIntegritySlow()
1034
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
1035
+
1036
+ local root = self:_debugGetRoot()
1037
+
1038
+ for _, node in root:IterateNodes() do
1039
+ node:_assertRedBlackIntegrity()
1040
+ end
1041
+
1042
+ local seen = {}
1043
+
1044
+ local maxDepth = nil
1045
+ local function recurse(node, ancestorBlackCount)
1046
+ if seen[node] then
1047
+ error("Loop in nodes")
1048
+ end
1049
+
1050
+ seen[node] = true
1051
+
1052
+ if node.color == Color.BLACK then
1053
+ ancestorBlackCount += 1
1054
+ end
1055
+
1056
+ if node.left then
1057
+ recurse(node.left, ancestorBlackCount)
1058
+ else
1059
+ if maxDepth == nil then
1060
+ maxDepth = ancestorBlackCount
1061
+ elseif maxDepth ~= ancestorBlackCount then
1062
+ error(string.format("Leaf nodes must all pass through the same amount (%d) of black nodes to root, but we are at %d", maxDepth, ancestorBlackCount))
1063
+ end
1064
+ end
1065
+
1066
+ if node.right then
1067
+ recurse(node.right, ancestorBlackCount)
1068
+ else
1069
+ if maxDepth == nil then
1070
+ maxDepth = ancestorBlackCount
1071
+ elseif maxDepth ~= ancestorBlackCount then
1072
+ error(string.format("Leaf nodes must all pass through the same amount (%d) of black nodes to root but we are at %d", maxDepth, ancestorBlackCount))
1073
+ end
1074
+ end
1075
+ end
1076
+
1077
+ assert(root.color == Color.BLACK, "Root must be black")
1078
+ recurse(root, 0)
1079
+ end
1080
+
1081
+ function SortedNode:_assertIntegrity()
1082
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
1083
+ assert(self.left ~= self, "Node cannot be parented to self")
1084
+ assert(self.right ~= self, "Node cannot be parented to self")
1085
+ assert(self.parent ~= self, "Node cannot be parented to self")
1086
+
1087
+ local parent = self.parent
1088
+ if parent then
1089
+ assert(parent.left == self or parent.right == self, "We are parented without parent data being set")
1090
+
1091
+ if parent.left == self then
1092
+ if self.value > parent.value then
1093
+ error(string.format("self.parent.left.value %0.2f >= parent.value %0.2f", self.value, parent.value))
1094
+ end
1095
+ end
1096
+
1097
+ if parent.right == self then
1098
+ if self.value < parent.value then
1099
+ error(string.format("self.parent.right.value %0.2f <= parent.value %0.2f", self.value, parent.value))
1100
+ end
1101
+ end
1102
+ end
1103
+
1104
+ local descendantCount = 1
1105
+ local left = self.left
1106
+ if left then
1107
+ assert(left.parent == self, "Left parent is not set to us")
1108
+
1109
+ if left.value > self.value then
1110
+ error(string.format("left.value %0.2f > self.value %0.2f", left.value, self.value))
1111
+ end
1112
+
1113
+ descendantCount += left.descendantCount
1114
+ end
1115
+
1116
+ local right = self.right
1117
+ if right then
1118
+ assert(right.parent == self, "Right parent is not set to us")
1119
+
1120
+ if right.value < self.value then
1121
+ error(string.format("right.value %0.2f <= self.value %0.2f", right.value, self.value))
1122
+ end
1123
+
1124
+ descendantCount += right.descendantCount
1125
+ end
1126
+
1127
+ if self.descendantCount ~= descendantCount then
1128
+ error(string.format("Bad descendantCount on node (%d, should be %d)", self.descendantCount, descendantCount))
1129
+ end
1130
+ end
1131
+
1132
+ function SortedNode:_assertFullIntegritySlow()
1133
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
1134
+
1135
+ local root = self:_debugGetRoot()
1136
+ local previous = nil
1137
+ local seen = {}
1138
+ for index, node in root:IterateNodes() do
1139
+ if seen[node] then
1140
+ error("Loop in nodes")
1141
+ end
1142
+
1143
+ seen[node] = true
1144
+ if previous then
1145
+ assert(previous.value <= node.value, "Node is out of order")
1146
+ end
1147
+
1148
+ previous = node
1149
+ node:_assertIntegrity()
1150
+
1151
+ if node:GetIndex() ~= index then
1152
+ error(string.format("Node index at %d should be %d", index, node:GetIndex()))
1153
+ end
1154
+ end
1155
+ end
1156
+
1157
+ function SortedNode:_assertRootIntegrity()
1158
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
1159
+ assert(self.parent == nil, "Root should not have a parent")
1160
+ assert(self.color == Color.BLACK, "Root should be black")
1161
+ end
1162
+
1163
+ function SortedNode:_assertDescendantCount(expected)
1164
+ assert(DEBUG_ASSERTION_SLOW, "Must have debug enabled")
1165
+
1166
+ if self.descendantCount ~= expected then
1167
+ error(string.format("Bad descendantCount, expected %d descendants, have %d", expected, self.descendantCount), 2)
1168
+ end
1169
+ end
1170
+
1171
+ return SortedNode