itree 0.1.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.
- data/lib/itree.rb +3 -0
- data/lib/itree/node.rb +65 -0
- data/lib/itree/tree.rb +389 -0
- data/lib/itree/version.rb +3 -0
- data/spec/node_spec.rb +149 -0
- data/spec/tree_spec.rb +195 -0
- metadata +69 -0
data/lib/itree.rb
ADDED
data/lib/itree/node.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Intervals
|
2
|
+
class Node
|
3
|
+
attr_accessor :data, :scores, :subLeftMax, :subRightMax, :balance, :left, :right, :parent
|
4
|
+
|
5
|
+
def initialize(min,max,data)
|
6
|
+
raise ArgumentError.new("first agument cannot be greater than second argument") if min > max
|
7
|
+
@scores = [min,max]
|
8
|
+
@subLeftMax = nil
|
9
|
+
@subRightMax = nil
|
10
|
+
@balance = 0
|
11
|
+
@data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def <=>(other)
|
15
|
+
if @scores[0] != other.scores[0]
|
16
|
+
@scores[0] <=> other.scores[0]
|
17
|
+
else
|
18
|
+
other.scores[1] <=> @scores[1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
@scores == other.scores
|
24
|
+
end
|
25
|
+
|
26
|
+
def resetBalance
|
27
|
+
case @balance
|
28
|
+
when -1
|
29
|
+
@left.balance = 0
|
30
|
+
@right.balance = 1
|
31
|
+
when 0
|
32
|
+
@left.balance = 0
|
33
|
+
@right.balance = 0
|
34
|
+
when 1
|
35
|
+
@left.balance = -1
|
36
|
+
@right.balance = 0
|
37
|
+
end
|
38
|
+
@balance = 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def updateMaxScores
|
42
|
+
oldNodeMax = 0
|
43
|
+
locNode = self
|
44
|
+
while locNode
|
45
|
+
if locNode.left
|
46
|
+
oldNodeMax = locNode.left.scores[1]
|
47
|
+
oldNodeMax = (locNode.left.subLeftMax && (oldNodeMax < locNode.left.subLeftMax)) ? locNode.left.subLeftMax : oldNodeMax
|
48
|
+
oldNodeMax = (locNode.left.subRightMax && (oldNodeMax < locNode.left.subRightMax)) ? locNode.left.subRightMax : oldNodeMax
|
49
|
+
locNode.subLeftMax = oldNodeMax
|
50
|
+
else
|
51
|
+
locNode.subLeftMax = nil
|
52
|
+
end
|
53
|
+
if locNode.right
|
54
|
+
oldNodeMax = locNode.right.scores[1]
|
55
|
+
oldNodeMax = (locNode.right.subLeftMax && (oldNodeMax < locNode.right.subLeftMax)) ? locNode.right.subLeftMax : oldNodeMax
|
56
|
+
oldNodeMax = (locNode.right.subRightMax && (oldNodeMax < locNode.right.subRightMax)) ? locNode.right.subRightMax : oldNodeMax
|
57
|
+
locNode.subRightMax = oldNodeMax
|
58
|
+
else
|
59
|
+
locNode.subRightMax = nil
|
60
|
+
end
|
61
|
+
locNode = locNode.parent
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/itree/tree.rb
ADDED
@@ -0,0 +1,389 @@
|
|
1
|
+
module Intervals
|
2
|
+
class Tree
|
3
|
+
attr_accessor :root, :size
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@root = nil
|
7
|
+
@size = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def insert(leftScore, rightScore, data=nil, updateData=false)
|
11
|
+
node = Node.new(leftScore,rightScore,data)
|
12
|
+
success = true
|
13
|
+
|
14
|
+
if @root.nil?
|
15
|
+
@root = node
|
16
|
+
else
|
17
|
+
balance,success = insertNode(@root,node,updateData)
|
18
|
+
end
|
19
|
+
|
20
|
+
@size = @size + 1 if success
|
21
|
+
return success
|
22
|
+
end
|
23
|
+
|
24
|
+
def insert!(leftScore, rightScore, data=nil)
|
25
|
+
return insert(leftScore,rightScore,data,true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def stab(minScore,maxScore=nil)
|
29
|
+
maxScore = minScore if maxScore.nil?
|
30
|
+
results = []
|
31
|
+
stabNode(@root,minScore,maxScore,results) if @root
|
32
|
+
results
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove(leftScore, rightScore)
|
36
|
+
if @root
|
37
|
+
delNode = Node.new(leftScore,rightScore,nil)
|
38
|
+
height,removed = removeNode(@root,delNode)
|
39
|
+
if removed
|
40
|
+
@size = @size - 1
|
41
|
+
@root = nil if @size == 0
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def insertNode(locNode,insertNode,updateData=false)
|
50
|
+
diff = locNode <=> insertNode
|
51
|
+
|
52
|
+
if diff > 0
|
53
|
+
if locNode.left.nil?
|
54
|
+
locNode.left = insertNode
|
55
|
+
insertNode.parent = locNode
|
56
|
+
locNode.balance = locNode.balance - 1
|
57
|
+
locNode.updateMaxScores
|
58
|
+
if locNode.balance == 0
|
59
|
+
return 0,true
|
60
|
+
else
|
61
|
+
return 1,true
|
62
|
+
end
|
63
|
+
else
|
64
|
+
balanceUpdate,success = insertNode(locNode.left,insertNode,updateData)
|
65
|
+
if balanceUpdate != 0
|
66
|
+
locNode.balance = locNode.balance - 1
|
67
|
+
if locNode.balance == 0
|
68
|
+
return 0,success
|
69
|
+
elsif locNode.balance == -1
|
70
|
+
return 1,success
|
71
|
+
end
|
72
|
+
|
73
|
+
if locNode.left.balance < 0
|
74
|
+
rightRotation(locNode)
|
75
|
+
locNode.balance = 0
|
76
|
+
locNode.parent.balance = 0
|
77
|
+
locNode.subLeftMax = locNode.parent.subRightMax
|
78
|
+
locNode.parent.subRightMax = -Float::INFINITY
|
79
|
+
else
|
80
|
+
leftRotation(locNode.left)
|
81
|
+
rightRotation(locNode)
|
82
|
+
locNode.parent.resetBalance
|
83
|
+
|
84
|
+
locNode.subLeftMax = locNode.parent.subRightMax
|
85
|
+
locNode.parent.left.subRightMax = locNode.parent.subLeftMax
|
86
|
+
locNode.parent.subRightMax = -Float::INFINITY
|
87
|
+
locNode.parent.subLeftMax = -Float::INFINITY
|
88
|
+
end
|
89
|
+
|
90
|
+
locNode.parent.updateMaxScores
|
91
|
+
end
|
92
|
+
return 0,success
|
93
|
+
end
|
94
|
+
elsif diff < 0
|
95
|
+
if locNode.right.nil?
|
96
|
+
locNode.right = insertNode
|
97
|
+
insertNode.parent = locNode
|
98
|
+
locNode.balance = locNode.balance + 1
|
99
|
+
locNode.updateMaxScores
|
100
|
+
if locNode.balance == 0
|
101
|
+
return 0,true
|
102
|
+
else
|
103
|
+
return 1,true
|
104
|
+
end
|
105
|
+
else
|
106
|
+
balanceUpdate,success = insertNode(locNode.right,insertNode,updateData)
|
107
|
+
if balanceUpdate != 0
|
108
|
+
locNode.balance = locNode.balance + 1
|
109
|
+
if locNode.balance == 0
|
110
|
+
return 0,success
|
111
|
+
elsif locNode.balance == 1
|
112
|
+
return 1,success
|
113
|
+
end
|
114
|
+
|
115
|
+
if locNode.right.balance > 0
|
116
|
+
leftRotation(locNode)
|
117
|
+
locNode.balance = 0
|
118
|
+
locNode.parent.balance = 0
|
119
|
+
locNode.subRightMax = locNode.parent.subLeftMax
|
120
|
+
locNode.parent.subLeftMax = -Float::INFINITY
|
121
|
+
else
|
122
|
+
rightRotation(locNode.right)
|
123
|
+
leftRotation(locNode)
|
124
|
+
locNode.parent.resetBalance
|
125
|
+
|
126
|
+
locNode.subRightMax = locNode.parent.subLeftMax
|
127
|
+
locNode.parent.right.subLeftMax = locNode.parent.subRightMax
|
128
|
+
locNode.parent.subRightMax = -Float::INFINITY
|
129
|
+
locNode.parent.subLeftMax = -Float::INFINITY
|
130
|
+
end
|
131
|
+
|
132
|
+
locNode.parent.updateMaxScores
|
133
|
+
end
|
134
|
+
return 0,success
|
135
|
+
end
|
136
|
+
else
|
137
|
+
locNode.data = insertNode.data if updateData
|
138
|
+
return 0,false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def removeFromParent(locNode,replacementNode)
|
143
|
+
if locNode.parent
|
144
|
+
if locNode.parent.left == locNode
|
145
|
+
locNode.parent.left = replacementNode
|
146
|
+
else
|
147
|
+
locNode.parent.right = replacementNode
|
148
|
+
end
|
149
|
+
else
|
150
|
+
@root = replacementNode
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def removeNode(locNode, delNode)
|
155
|
+
diff = locNode <=> delNode
|
156
|
+
heightDelta = 0
|
157
|
+
replacementNode = nil
|
158
|
+
removed = false
|
159
|
+
|
160
|
+
if diff == 0
|
161
|
+
if locNode.left.nil?
|
162
|
+
if locNode.right.nil?
|
163
|
+
removeFromParent(locNode,nil)
|
164
|
+
locNode.parent.updateMaxScores if locNode.parent
|
165
|
+
removed = true
|
166
|
+
return -1,removed
|
167
|
+
end
|
168
|
+
removeFromParent(locNode,locNode.right)
|
169
|
+
locNode.right.parent = locNode.parent
|
170
|
+
locNode.parent.updateMaxScores if locNode.parent
|
171
|
+
locNode.right = nil
|
172
|
+
removed = true
|
173
|
+
return -1,removed
|
174
|
+
end
|
175
|
+
|
176
|
+
if locNode.right.nil?
|
177
|
+
removeFromParent(locNode,locNode.left)
|
178
|
+
locNode.left.parent = locNode.parent
|
179
|
+
locNode.parent.updateMaxScores if locNode.parent
|
180
|
+
locNode.left = nil
|
181
|
+
removed = true
|
182
|
+
return -1,removed
|
183
|
+
end
|
184
|
+
|
185
|
+
if locNode.balance < 0
|
186
|
+
replacementNode = locNode.left
|
187
|
+
while replacementNode.right do
|
188
|
+
replacementNode = replacementNode.right
|
189
|
+
end
|
190
|
+
else
|
191
|
+
replacementNode = locNode.right
|
192
|
+
while replacementNode.left do
|
193
|
+
replacementNode = replacementNode.left
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
heightDelta,removed = removeNode(locNode,replacementNode)
|
198
|
+
|
199
|
+
locNode.right.parent = replacementNode if locNode.right
|
200
|
+
locNode.left.parent = replacementNode if locNode.left
|
201
|
+
|
202
|
+
replacementNode.left = locNode.left
|
203
|
+
replacementNode.right = locNode.right
|
204
|
+
replacementNode.parent = locNode.parent
|
205
|
+
replacementNode.balance = locNode.balance
|
206
|
+
|
207
|
+
if locNode == @root
|
208
|
+
@root = replacementNode
|
209
|
+
replacementNode.updateMaxScores
|
210
|
+
else
|
211
|
+
if locNode == locNode.parent.left
|
212
|
+
locNode.parent.left = replacementNode
|
213
|
+
else
|
214
|
+
locNode.parent.right = replacementNode
|
215
|
+
end
|
216
|
+
replacementNode.updateMaxScores
|
217
|
+
end
|
218
|
+
|
219
|
+
locNode.left = nil
|
220
|
+
locNode.right = nil
|
221
|
+
|
222
|
+
if replacementNode.balance == 0
|
223
|
+
return heightDelta,removed
|
224
|
+
end
|
225
|
+
removed = true
|
226
|
+
return 0,removed
|
227
|
+
elsif diff > 0
|
228
|
+
if locNode.left
|
229
|
+
heightDelta,removed = removeNode(locNode.left,delNode)
|
230
|
+
if heightDelta
|
231
|
+
locNode.balance = locNode.balance + 1
|
232
|
+
if locNode.balance == 0
|
233
|
+
return -1,removed
|
234
|
+
elsif locNode.balance == 1
|
235
|
+
return 0,removed
|
236
|
+
end
|
237
|
+
|
238
|
+
if locNode.right.balance == 1
|
239
|
+
leftRotation(locNode)
|
240
|
+
locNode.parent.balance = 0
|
241
|
+
locNode.parent.left.balance = 0
|
242
|
+
|
243
|
+
locNode.subRightMax = locNode.parent.subLeftMax
|
244
|
+
locNode.parent.subLeftMax = nil
|
245
|
+
locNode.parent.updateMaxScores
|
246
|
+
|
247
|
+
return -1,removed
|
248
|
+
elsif locNode.right.balance == 0
|
249
|
+
leftRotation(locNode)
|
250
|
+
locNode.parent.balance = -1
|
251
|
+
locNode.parent.left.balance = 1
|
252
|
+
|
253
|
+
locNode.subRightMax = locNode.parent.subLeftMax
|
254
|
+
locNode.parent.subLeftMax = nil
|
255
|
+
locNode.parent.updateMaxScores
|
256
|
+
|
257
|
+
return 0,removed
|
258
|
+
end
|
259
|
+
rightRotation(locNode.right)
|
260
|
+
leftRotation(locNode)
|
261
|
+
locNode.parent.resetBalance
|
262
|
+
|
263
|
+
locNode.subRightMax = locNode.parent.subLeftMax
|
264
|
+
locNode.parent.right.subLeftMax = locNode.parent.subRightMax
|
265
|
+
|
266
|
+
locNode.parent.subRightMax = nil
|
267
|
+
locNode.parent.subLeftMax = nil
|
268
|
+
locNode.parent.updateMaxScores
|
269
|
+
|
270
|
+
return -1,removed
|
271
|
+
end
|
272
|
+
end
|
273
|
+
elsif diff < 0
|
274
|
+
if locNode.right
|
275
|
+
heightDelta,removed = removeNode(locNode.right,delNode)
|
276
|
+
if heightDelta
|
277
|
+
locNode.balance = locNode.balance - 1
|
278
|
+
if locNode.balance == 0
|
279
|
+
return 1,removed
|
280
|
+
elsif locNode.balance == -1
|
281
|
+
return 0,removed
|
282
|
+
end
|
283
|
+
|
284
|
+
if locNode.left.balance == -1
|
285
|
+
rightRotation(locNode)
|
286
|
+
locNode.parent.balance = 0
|
287
|
+
locNode.parent.right.balance = 0
|
288
|
+
|
289
|
+
locNode.subLeftMax = locNode.parent.subRightMax
|
290
|
+
locNode.parent.subRightMax = nil
|
291
|
+
locNode.parent.updateMaxScores
|
292
|
+
|
293
|
+
return -1,removed
|
294
|
+
elsif locNode.left.balance == 0
|
295
|
+
rightRotation(locNode)
|
296
|
+
locNode.parent.balance = 1
|
297
|
+
locNode.parent.right.balance = -1
|
298
|
+
|
299
|
+
locNode.subLeftMax = locNode.parent.subRightMax
|
300
|
+
locNode.parent.subRightMax = nil
|
301
|
+
locNode.parent.updateMaxScores
|
302
|
+
|
303
|
+
return 0,removed
|
304
|
+
end
|
305
|
+
leftRotation(locNode.left)
|
306
|
+
rightRotation(locNode)
|
307
|
+
locNode.parent.resetBalance
|
308
|
+
|
309
|
+
locNode.subLeftMax = locNode.parent.subRightMax
|
310
|
+
locNode.parent.left.subRightMax = locNode.parent.subLeftMax
|
311
|
+
|
312
|
+
locNode.parent.subLeftMax = nil
|
313
|
+
locNode.parent.subRightMax = nil
|
314
|
+
locNode.parent.updateMaxScores
|
315
|
+
|
316
|
+
return -1,removed
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
return 0,removed
|
322
|
+
end
|
323
|
+
|
324
|
+
def leftRotation(locNode)
|
325
|
+
newRoot = locNode.right
|
326
|
+
locNode.right = newRoot.left
|
327
|
+
if locNode.right
|
328
|
+
locNode.right.parent = locNode
|
329
|
+
end
|
330
|
+
newRoot.left = locNode
|
331
|
+
|
332
|
+
newRoot.parent = locNode.parent
|
333
|
+
locNode.parent = newRoot
|
334
|
+
if newRoot.parent
|
335
|
+
if ((newRoot.parent <=> newRoot) > -1)
|
336
|
+
newRoot.parent.left = newRoot
|
337
|
+
else
|
338
|
+
newRoot.parent.right = newRoot
|
339
|
+
end
|
340
|
+
else
|
341
|
+
@root = newRoot
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def rightRotation(locNode)
|
346
|
+
newRoot = locNode.left
|
347
|
+
locNode.left = newRoot.right
|
348
|
+
if locNode.left
|
349
|
+
locNode.left.parent = locNode
|
350
|
+
end
|
351
|
+
newRoot.right = locNode
|
352
|
+
|
353
|
+
newRoot.parent = locNode.parent
|
354
|
+
locNode.parent = newRoot
|
355
|
+
if newRoot.parent
|
356
|
+
if ((newRoot.parent <=> newRoot) > -1)
|
357
|
+
newRoot.parent.left = newRoot
|
358
|
+
else
|
359
|
+
newRoot.parent.right = newRoot
|
360
|
+
end
|
361
|
+
else
|
362
|
+
@root = newRoot
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def stabNode(node, minScore, maxScore, results)
|
367
|
+
if node.subRightMax &&
|
368
|
+
minScore > node.subRightMax &&
|
369
|
+
node.subLeftMax &&
|
370
|
+
minScore > node.subLeftMax &&
|
371
|
+
minScore > node.scores[1]
|
372
|
+
return
|
373
|
+
end
|
374
|
+
if node.left
|
375
|
+
stabNode(node.left, minScore, maxScore, results)
|
376
|
+
end
|
377
|
+
|
378
|
+
if minScore >= node.scores[0] && maxScore <= node.scores[1]
|
379
|
+
results << node.clone
|
380
|
+
end
|
381
|
+
|
382
|
+
return if maxScore < node.scores[0]
|
383
|
+
|
384
|
+
if node.right
|
385
|
+
stabNode(node.right, minScore, maxScore, results)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Intervals::Node do
|
4
|
+
context "creation" do
|
5
|
+
describe "#initialize" do
|
6
|
+
it { Intervals::Node.new(1,2,"taco").scores.should eq [1,2]}
|
7
|
+
it { Intervals::Node.new(1,2,"taco").data.should eq "taco"}
|
8
|
+
it { Intervals::Node.new(1,2,"taco").balance.should eq 0}
|
9
|
+
it { expect { Intervals::Node.new(2,1,"taco") }.to raise_error(ArgumentError)}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "comparing" do
|
14
|
+
let(:lesser_min_node) { Intervals::Node.new(0,5,nil) }
|
15
|
+
let(:greater_min_node) { Intervals::Node.new(1,5,nil) }
|
16
|
+
let(:lesser_max_node) { Intervals::Node.new(0,5,nil) }
|
17
|
+
let(:greater_max_node) { Intervals::Node.new(0,1,nil) }
|
18
|
+
|
19
|
+
describe "#<=>" do
|
20
|
+
it { (lesser_min_node <=> greater_min_node).should eq -1 }
|
21
|
+
it { (lesser_min_node <=> lesser_min_node).should eq 0 }
|
22
|
+
it { (greater_min_node <=> lesser_min_node).should eq 1 }
|
23
|
+
it { (lesser_max_node <=> greater_max_node).should eq -1 }
|
24
|
+
it { (lesser_max_node <=> lesser_max_node).should eq 0 }
|
25
|
+
it { (greater_max_node <=> lesser_max_node).should eq 1 }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#==" do
|
29
|
+
it { (lesser_min_node == lesser_min_node).should be true }
|
30
|
+
it { (lesser_min_node == greater_min_node).should_not be true }
|
31
|
+
it { (greater_min_node == greater_min_node).should be true }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#resetBalance" do
|
36
|
+
let(:node) do
|
37
|
+
n = Intervals::Node.new(5,10,nil)
|
38
|
+
n.left = Intervals::Node.new(0,5,nil)
|
39
|
+
n.left.balance = 2
|
40
|
+
n.right = Intervals::Node.new(15,20,nil)
|
41
|
+
n.right.balance = 2
|
42
|
+
n
|
43
|
+
end
|
44
|
+
|
45
|
+
it "resets balance to 0" do
|
46
|
+
node.balance = 1
|
47
|
+
node.resetBalance
|
48
|
+
node.balance.should eq 0
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when balance is -1" do
|
52
|
+
before(:each) { node.balance = -1 }
|
53
|
+
|
54
|
+
it "sets left child balance to 0" do
|
55
|
+
node.resetBalance
|
56
|
+
node.left.balance.should eq 0
|
57
|
+
end
|
58
|
+
|
59
|
+
it "sets right child balance to 1" do
|
60
|
+
node.resetBalance
|
61
|
+
node.right.balance.should eq 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when balance is 0" do
|
66
|
+
before(:each) { node.balance = 0 }
|
67
|
+
|
68
|
+
it "sets left child balance to 0" do
|
69
|
+
node.resetBalance
|
70
|
+
node.left.balance.should eq 0
|
71
|
+
end
|
72
|
+
|
73
|
+
it "sets right child balance to 0" do
|
74
|
+
node.resetBalance
|
75
|
+
node.right.balance.should eq 0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when balance is 1" do
|
80
|
+
before(:each) { node.balance = 1 }
|
81
|
+
|
82
|
+
it "sets left child balance to -1" do
|
83
|
+
node.resetBalance
|
84
|
+
node.left.balance.should eq -1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "sets right child balance to 0" do
|
88
|
+
node.resetBalance
|
89
|
+
node.right.balance.should eq 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#updateMaxScores" do
|
95
|
+
let(:node) do
|
96
|
+
n = Intervals::Node.new(5,10,nil)
|
97
|
+
n.left = Intervals::Node.new(0,5,nil)
|
98
|
+
n.left.parent = n
|
99
|
+
n.right = Intervals::Node.new(15,20,nil)
|
100
|
+
n.right.parent = n
|
101
|
+
n.left.left = Intervals::Node.new(-2,0,nil)
|
102
|
+
n.left.left.parent = n.left
|
103
|
+
n.left.right = Intervals::Node.new(3,5,nil)
|
104
|
+
n.left.right.parent = n.left
|
105
|
+
n.right.left = Intervals::Node.new(10,15,nil)
|
106
|
+
n.right.left.parent = n.right
|
107
|
+
n.right.right = Intervals::Node.new(20,25,nil)
|
108
|
+
n.right.right.parent = n.right
|
109
|
+
n
|
110
|
+
end
|
111
|
+
|
112
|
+
context "left-left child has largest max score" do
|
113
|
+
before(:each) { node.left.left.scores[1] = 100 }
|
114
|
+
|
115
|
+
it "sets the maxLeftScore to the max score of the left-left child" do
|
116
|
+
node.left.left.updateMaxScores
|
117
|
+
node.subLeftMax.should eq node.left.left.scores[1]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "left-right child has largest max score" do
|
122
|
+
before(:each) { node.left.right.scores[1] = 100 }
|
123
|
+
|
124
|
+
it "sets the maxLeftScore to the max score of the left-right child" do
|
125
|
+
node.left.right.updateMaxScores
|
126
|
+
node.subLeftMax.should eq node.left.right.scores[1]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "right-left child has largest max score" do
|
131
|
+
before(:each) { node.right.left.scores[1] = 100 }
|
132
|
+
|
133
|
+
it "sets the maxRightScore to the max score of the right-left child" do
|
134
|
+
node.right.left.updateMaxScores
|
135
|
+
node.subRightMax.should eq node.right.left.scores[1]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "right-right child has largest max score" do
|
140
|
+
before(:each) { node.right.right.scores[1] = 100 }
|
141
|
+
|
142
|
+
it "sets the maxRightScore to the max score of the right-right child" do
|
143
|
+
node.right.right.updateMaxScores
|
144
|
+
node.subRightMax.should eq node.right.right.scores[1]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
data/spec/tree_spec.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Intervals::Tree do
|
4
|
+
let(:tree) { Intervals::Tree.new }
|
5
|
+
|
6
|
+
context "creation" do
|
7
|
+
describe "#initialize" do
|
8
|
+
it { Intervals::Tree.new.size.should eq 0}
|
9
|
+
it { Intervals::Tree.new.root.should be nil}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#insert" do
|
14
|
+
context "insert node in empty tree" do
|
15
|
+
it "inserts at root" do
|
16
|
+
tree.insert(0,10)
|
17
|
+
tree.root.scores.should eq [0,10]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "increases tree size" do
|
21
|
+
tree.insert(0,10)
|
22
|
+
tree.size.should eq 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "inserting duplicates" do
|
27
|
+
before(:each) { tree.insert(0,10)}
|
28
|
+
|
29
|
+
it { tree.insert(0,10,"taco").should_not be true}
|
30
|
+
it "does not increase tree size" do
|
31
|
+
tree.insert(0,10,"taco")
|
32
|
+
tree.size.should_not eq 2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "rotations" do
|
37
|
+
before(:each) do
|
38
|
+
tree.insert(150,250)
|
39
|
+
tree.insert(200,300)
|
40
|
+
tree.insert(100,200)
|
41
|
+
end
|
42
|
+
|
43
|
+
it { tree.root.scores.should eq [150,250] }
|
44
|
+
it { tree.root.left.scores.should eq [100,200] }
|
45
|
+
it { tree.root.right.scores.should eq [200,300] }
|
46
|
+
it { tree.size.should eq 3 }
|
47
|
+
|
48
|
+
context "left-left rotation" do
|
49
|
+
before(:each) do
|
50
|
+
tree.insert(25,50)
|
51
|
+
tree.insert(0,25)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "does not change root" do
|
55
|
+
tree.root.scores.should eq [150,250]
|
56
|
+
end
|
57
|
+
it { tree.root.left.scores.should eq [25,50] }
|
58
|
+
it { tree.root.left.left.scores.should eq [0,25] }
|
59
|
+
it { tree.root.left.right.scores.should eq [100,200] }
|
60
|
+
it { tree.root.subLeftMax.should eq 200 }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "right-right rotation" do
|
64
|
+
before(:each) do
|
65
|
+
tree.insert(250,300)
|
66
|
+
tree.insert(300,350)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not change root" do
|
70
|
+
tree.root.scores.should eq [150,250]
|
71
|
+
end
|
72
|
+
it { tree.root.right.scores.should eq [250,300] }
|
73
|
+
it { tree.root.right.left.scores.should eq [200,300] }
|
74
|
+
it { tree.root.right.right.scores.should eq [300,350] }
|
75
|
+
it { tree.root.subRightMax.should eq 350 }
|
76
|
+
end
|
77
|
+
|
78
|
+
context "left-right rotation" do
|
79
|
+
before(:each) do
|
80
|
+
tree.insert(50,100)
|
81
|
+
tree.insert(75,125)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "does not change root" do
|
85
|
+
tree.root.scores.should eq [150,250]
|
86
|
+
end
|
87
|
+
it { tree.root.left.scores.should eq [75,125] }
|
88
|
+
it { tree.root.left.left.scores.should eq [50,100] }
|
89
|
+
it { tree.root.left.right.scores.should eq [100,200] }
|
90
|
+
it { tree.root.subLeftMax.should eq 200 }
|
91
|
+
end
|
92
|
+
|
93
|
+
context "right-left rotation" do
|
94
|
+
before(:each) do
|
95
|
+
tree.insert(250,300)
|
96
|
+
tree.insert(225,275)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "does not change root" do
|
100
|
+
tree.root.scores.should eq [150,250]
|
101
|
+
end
|
102
|
+
it { tree.root.right.scores.should eq [225,275] }
|
103
|
+
it { tree.root.right.left.scores.should eq [200,300] }
|
104
|
+
it { tree.root.right.right.scores.should eq [250,300] }
|
105
|
+
it { tree.root.subRightMax.should eq 300 }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#insert!" do
|
112
|
+
context "inserting duplicates" do
|
113
|
+
before(:each) { tree.insert(0,10,"cheese")}
|
114
|
+
|
115
|
+
it { tree.root.data.should eq "cheese" }
|
116
|
+
it { tree.insert!(0,10,"taco").should_not be true}
|
117
|
+
it "does not increase tree size" do
|
118
|
+
tree.insert!(0,10,"taco")
|
119
|
+
tree.size.should_not eq 2
|
120
|
+
end
|
121
|
+
it "updates the node data" do
|
122
|
+
tree.insert!(0,10,"taco")
|
123
|
+
tree.root.data.should eq "taco"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#remove" do
|
129
|
+
before(:each) do
|
130
|
+
tree.insert(100,200)
|
131
|
+
tree.insert(200,300)
|
132
|
+
tree.insert(150,250)
|
133
|
+
tree.insert(300,400)
|
134
|
+
tree.insert(400,500)
|
135
|
+
tree.insert(50,100)
|
136
|
+
tree.insert(75,125)
|
137
|
+
tree.insert(25,50)
|
138
|
+
tree.insert(0,25)
|
139
|
+
tree.insert(51,101)
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "#stab" do
|
146
|
+
before(:each) do
|
147
|
+
tree.insert(100,200)
|
148
|
+
tree.insert(200,300)
|
149
|
+
tree.insert(150,250)
|
150
|
+
tree.insert(300,400)
|
151
|
+
tree.insert(400,500)
|
152
|
+
tree.insert(50,100)
|
153
|
+
tree.insert(75,125)
|
154
|
+
tree.insert(25,50)
|
155
|
+
tree.insert(0,25)
|
156
|
+
tree.insert(51,101)
|
157
|
+
tree.remove(100,200)
|
158
|
+
tree.remove(50,100)
|
159
|
+
end
|
160
|
+
|
161
|
+
it { tree.remove(0,25).should be true }
|
162
|
+
it { tree.remove(0,24).should be false }
|
163
|
+
|
164
|
+
it { tree.stab(23).length.should eq 1 }
|
165
|
+
it "returns a single range" do
|
166
|
+
results = tree.stab(23).map{|n| n.scores }.sort{|a,b| a[0] <=> b[0]}
|
167
|
+
results[0].should eq [0,25]
|
168
|
+
end
|
169
|
+
|
170
|
+
it { tree.stab(40).length.should eq 1 }
|
171
|
+
it "returns a single range" do
|
172
|
+
results = tree.stab(40).map{|n| n.scores }.sort{|a,b| a[0] <=> b[0]}
|
173
|
+
results[0].should eq [25,50]
|
174
|
+
end
|
175
|
+
|
176
|
+
it { tree.stab(77).length.should eq 2 }
|
177
|
+
it "returns two ranges" do
|
178
|
+
results = tree.stab(77).map{|n| n.scores }.sort{|a,b| a[0] <=> b[0]}
|
179
|
+
results[0].should eq [51,101]
|
180
|
+
results[1].should eq [75,125]
|
181
|
+
end
|
182
|
+
|
183
|
+
it { tree.stab(175).length.should eq 1 }
|
184
|
+
it "returns a single range" do
|
185
|
+
results = tree.stab(175).map{|n| n.scores }.sort{|a,b| a[0] <=> b[0]}
|
186
|
+
results[0].should eq [150,250]
|
187
|
+
end
|
188
|
+
|
189
|
+
it { tree.stab(350).length.should eq 1 }
|
190
|
+
it "returns a single range" do
|
191
|
+
results = tree.stab(350).map{|n| n.scores }.sort{|a,b| a[0] <=> b[0]}
|
192
|
+
results[0].should eq [300,400]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: itree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kenny Hoxworth
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Interval Tree Data Structure
|
31
|
+
email:
|
32
|
+
- hoxworth@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/itree/node.rb
|
38
|
+
- lib/itree/tree.rb
|
39
|
+
- lib/itree/version.rb
|
40
|
+
- lib/itree.rb
|
41
|
+
- spec/node_spec.rb
|
42
|
+
- spec/tree_spec.rb
|
43
|
+
homepage: http://github.com/hoxworth/itree
|
44
|
+
licenses: []
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.8.24
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Interval Tree Data Structure
|
67
|
+
test_files:
|
68
|
+
- spec/node_spec.rb
|
69
|
+
- spec/tree_spec.rb
|