itree 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|