@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.
- package/CHANGELOG.md +27 -0
- package/package.json +10 -9
- package/src/Shared/ObservableCountingMap.lua +9 -0
- package/src/Shared/ObservableList.lua +37 -28
- package/src/Shared/ObservableMap.lua +10 -0
- package/src/Shared/ObservableSet.lua +9 -0
- package/src/Shared/SortedList/ObservableSortedList.lua +586 -0
- package/src/Shared/{ObservableSortedList.story.lua → SortedList/ObservableSortedList.story.lua} +15 -12
- package/src/Shared/{ObservableSortedList.lua → SortedList/ObservableSortedListOld.lua} +37 -74
- package/src/Shared/SortedList/ObservableSortedList_Performance.story.lua +74 -0
- package/src/Shared/SortedList/ObservableSortedList_Print.story.lua +65 -0
- package/src/Shared/SortedList/SortFunctionUtils.lua +31 -0
- package/src/Shared/SortedList/SortedNode.lua +1171 -0
- package/src/Shared/SortedList/SortedNodeValue.lua +53 -0
- package/src/Shared/Utils/ListIndexUtils.lua +39 -0
- package/test/default.project.json +1 -7
- /package/src/Shared/{ObservableSortedList.spec.lua → SortedList/ObservableSortedList.spec.lua} +0 -0
|
@@ -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
|