rbtree-pure 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/.document +5 -0
- data/.project +17 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +20 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/lib/rbtree.rb +10 -0
- data/lib/rbtree/guard_node.rb +28 -0
- data/lib/rbtree/multi_rb_tree.rb +317 -0
- data/lib/rbtree/node.rb +59 -0
- data/lib/rbtree/rb_tree.rb +485 -0
- data/lib/rbtree/tree.rb +382 -0
- data/lib/rbtree/tree_cmp.rb +92 -0
- data/old_ext/dict.c +1216 -0
- data/old_ext/dict.h +123 -0
- data/old_ext/rbtree.c +1706 -0
- data/test/helper.rb +17 -0
- data/test/multi_rbtree_test.rb +226 -0
- data/test/rbtree_test.rb +791 -0
- metadata +145 -0
data/lib/rbtree/node.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
class RBTree
|
3
|
+
|
4
|
+
# A node in the red-black tree.
|
5
|
+
#
|
6
|
+
# Nodes should only be manipulated directly by the RedBlackTree class.
|
7
|
+
class Node
|
8
|
+
attr_accessor :key
|
9
|
+
attr_accessor :value
|
10
|
+
|
11
|
+
attr_accessor :color
|
12
|
+
attr_accessor :left
|
13
|
+
attr_accessor :right
|
14
|
+
attr_accessor :parent
|
15
|
+
|
16
|
+
# Creates a new node.
|
17
|
+
#
|
18
|
+
# New tree nodes are red by default.
|
19
|
+
def initialize(key, value, guard)
|
20
|
+
@color = :red
|
21
|
+
@key = key
|
22
|
+
@value = value
|
23
|
+
@left = @right = @parent = guard
|
24
|
+
end
|
25
|
+
|
26
|
+
# True for black nodes.
|
27
|
+
def black?
|
28
|
+
@color == :black
|
29
|
+
end
|
30
|
+
|
31
|
+
# True for red nodes.
|
32
|
+
def red?
|
33
|
+
@color == :red
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns an array of the node's [key, value].
|
37
|
+
#
|
38
|
+
# This method is used for nodes in a RBTree's tree.
|
39
|
+
def to_a
|
40
|
+
[@key, @value]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns an array of the node's [key, first value].
|
44
|
+
#
|
45
|
+
# This method is used for nodes in a MultiRBTree's tree.
|
46
|
+
def to_single_a
|
47
|
+
[@key, @value.first]
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
<<ENDI
|
52
|
+
<RBTree::Node (#{@color}) #{@key.inspect} -> #{@value.inspect}
|
53
|
+
Left: [#{@left.inspect.gsub!("\n", "\n ")}]
|
54
|
+
Right: [#{@right.inspect.gsub!("\n", "\n ")}]>
|
55
|
+
ENDI
|
56
|
+
end
|
57
|
+
end # class RBTree::Node
|
58
|
+
|
59
|
+
end # namespace RBTree
|
@@ -0,0 +1,485 @@
|
|
1
|
+
# Sorted hash.
|
2
|
+
class RBTree
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# The red-black tree backing this store.
|
6
|
+
attr_reader :tree
|
7
|
+
|
8
|
+
# The value returned when trying to read keys that don't exist in the tree.
|
9
|
+
def default(key = nil)
|
10
|
+
@default_proc ? @default_proc.call(tree, key) : @default
|
11
|
+
end
|
12
|
+
|
13
|
+
# The value returned when trying to read keys that don't exist in the tree.
|
14
|
+
def default=(new_default)
|
15
|
+
@default_proc = nil
|
16
|
+
@default = new_default
|
17
|
+
end
|
18
|
+
|
19
|
+
# Block called when trying to read keys that don't exist in the tree.
|
20
|
+
attr_reader :default_proc
|
21
|
+
|
22
|
+
# Block called when trying to read keys that don't exist in the tree.
|
23
|
+
def default_proc=(new_proc)
|
24
|
+
@default = nil
|
25
|
+
@default_proc = new_proc
|
26
|
+
end
|
27
|
+
|
28
|
+
# Block used to implement custom comparisons.
|
29
|
+
attr_reader :cmp_proc
|
30
|
+
|
31
|
+
def initialize(default = nil, &default_proc)
|
32
|
+
raise ArgumentError, "wrong number of arguments" if default && default_proc
|
33
|
+
@default = default
|
34
|
+
@default_proc = default_proc
|
35
|
+
@cmp_proc = nil
|
36
|
+
@lock_count = 0
|
37
|
+
@tree = RBTree::Tree.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize_copy(source)
|
41
|
+
super
|
42
|
+
@tree = source.tree.dup
|
43
|
+
@lock_count = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.[](*key_values)
|
47
|
+
if key_values.length == 1
|
48
|
+
hash = key_values.first
|
49
|
+
unless hash.respond_to? :values_at
|
50
|
+
raise ArgumentError, "expected a Hash-like argument"
|
51
|
+
end
|
52
|
+
if self == RBTree && hash.instance_of?(MultiRBTree)
|
53
|
+
raise TypeError, "can't convert MultiRBTree to RBTree"
|
54
|
+
end
|
55
|
+
tree = self.new
|
56
|
+
begin
|
57
|
+
hash.each { |k, v| tree[k] = v }
|
58
|
+
rescue NoMethodError
|
59
|
+
raise ArgumentError, "expected a Hash-like argument"
|
60
|
+
end
|
61
|
+
return tree
|
62
|
+
end
|
63
|
+
|
64
|
+
if key_values.length % 2 == 1
|
65
|
+
raise ArgumentError, 'odd number of arguments for RBTree'
|
66
|
+
end
|
67
|
+
|
68
|
+
tree = self.new
|
69
|
+
0.upto(key_values.length / 2 - 1) do |i|
|
70
|
+
tree[key_values[i * 2]] = key_values[i * 2 + 1]
|
71
|
+
end
|
72
|
+
tree
|
73
|
+
end
|
74
|
+
|
75
|
+
# Rejects changes while this method's block is executed.
|
76
|
+
def lock_changes
|
77
|
+
begin
|
78
|
+
@lock_count += 1
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
@lock_count -= 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
private :lock_changes
|
85
|
+
|
86
|
+
def lower_bound(key)
|
87
|
+
@tree.lower_bound(key).to_a
|
88
|
+
end
|
89
|
+
|
90
|
+
def upper_bound(key)
|
91
|
+
@tree.upper_bound(key).to_a
|
92
|
+
end
|
93
|
+
|
94
|
+
def bound(lower_key, upper_key = nil)
|
95
|
+
result = []
|
96
|
+
bound_nodes lower_key, upper_key do |node|
|
97
|
+
if block_given?
|
98
|
+
yield node.key, node.value
|
99
|
+
else
|
100
|
+
result << node.to_a
|
101
|
+
end
|
102
|
+
end
|
103
|
+
block_given? ? self : result
|
104
|
+
end
|
105
|
+
|
106
|
+
# Internal version of bound that yields the corresponding nodes.
|
107
|
+
def bound_nodes(lower_key, upper_key = nil)
|
108
|
+
upper_key ||= lower_key
|
109
|
+
node = @tree.lower_bound(lower_key)
|
110
|
+
return block_given? ? self : [] if node.nil?
|
111
|
+
|
112
|
+
lock_changes do
|
113
|
+
if @cmp_proc
|
114
|
+
# Slow path
|
115
|
+
until node.nil? || @cmp_proc.call(upper_key, node.key) < 0
|
116
|
+
yield node
|
117
|
+
node = @tree.successor node
|
118
|
+
end
|
119
|
+
else
|
120
|
+
# Fast path.
|
121
|
+
until node.nil? || upper_key < node.key
|
122
|
+
yield node
|
123
|
+
node = @tree.successor node
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_rbtree
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
def readjust(*proc_arg, &new_cmp_proc)
|
134
|
+
raise TypeError, 'cannot modify rbtree in iteration' if @lock_count > 0
|
135
|
+
|
136
|
+
if new_cmp_proc
|
137
|
+
cmp_proc = new_cmp_proc
|
138
|
+
unless proc_arg.empty?
|
139
|
+
raise ArgumentError, "expected 0 arguments when given a block"
|
140
|
+
end
|
141
|
+
else
|
142
|
+
unless proc_arg.length <= 1
|
143
|
+
raise ArgumentError, "expected 1 arguments (given #{proc_arg.length})"
|
144
|
+
end
|
145
|
+
unless proc_arg.first.respond_to?(:call) || proc_arg.first.nil?
|
146
|
+
raise TypeError, "expected a proc argument"
|
147
|
+
end
|
148
|
+
cmp_proc = proc_arg.first
|
149
|
+
end
|
150
|
+
|
151
|
+
lock_changes do
|
152
|
+
if cmp_proc
|
153
|
+
new_tree = RBTree::TreeCmp.new(&cmp_proc)
|
154
|
+
else
|
155
|
+
new_tree = RBTree::Tree.new
|
156
|
+
end
|
157
|
+
|
158
|
+
@tree.inorder do |node|
|
159
|
+
new_tree.insert new_tree.node(node.key, node.value)
|
160
|
+
end
|
161
|
+
@tree = new_tree
|
162
|
+
@cmp_proc = cmp_proc
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def replace(other)
|
167
|
+
raise TypeError, 'cannot modify rbtree in iteration' if @lock_count > 0
|
168
|
+
unless other.instance_of? RBTree
|
169
|
+
raise TypeError, "expected RBTree, got #{other.class}"
|
170
|
+
end
|
171
|
+
|
172
|
+
@tree = other.tree.dup
|
173
|
+
@default_proc = other.default_proc
|
174
|
+
@default = other.default
|
175
|
+
@cmp_proc = other.cmp_proc
|
176
|
+
self
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# :nodoc: array behavior
|
181
|
+
class RBTree
|
182
|
+
# The [key, value] for the smallest key in the tree.
|
183
|
+
def first
|
184
|
+
node = @tree.minimum
|
185
|
+
node.nil? ? default : node.to_a
|
186
|
+
end
|
187
|
+
|
188
|
+
# The [key, value] for the largest key in the tree.
|
189
|
+
def last
|
190
|
+
node = @tree.maximum
|
191
|
+
node.nil? ? default : node.to_a
|
192
|
+
end
|
193
|
+
|
194
|
+
# Removes the largest key in the tree.
|
195
|
+
def pop
|
196
|
+
return default if (node = @tree.maximum).nil?
|
197
|
+
@tree.delete node
|
198
|
+
node.to_a
|
199
|
+
end
|
200
|
+
|
201
|
+
# Removes the smallest key in the tree.
|
202
|
+
def shift
|
203
|
+
return default if (node = @tree.minimum).nil?
|
204
|
+
@tree.delete node
|
205
|
+
node.to_a
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# :nodoc: hash behavior
|
210
|
+
class RBTree
|
211
|
+
# See Hash#[]
|
212
|
+
def [](key)
|
213
|
+
node = tree.search key
|
214
|
+
node ? node.value : default(key)
|
215
|
+
end
|
216
|
+
|
217
|
+
# See Hash#[]=
|
218
|
+
def []=(key, value)
|
219
|
+
raise TypeError, 'cannot modify rbtree in iteration' if @lock_count > 0
|
220
|
+
|
221
|
+
key = key.clone.freeze if key.kind_of? String
|
222
|
+
@tree.insert(@tree.node(key, value)).value = value
|
223
|
+
end
|
224
|
+
|
225
|
+
# See Hash#size
|
226
|
+
def size
|
227
|
+
@tree.size
|
228
|
+
end
|
229
|
+
|
230
|
+
# See Hash#empty
|
231
|
+
def empty?
|
232
|
+
@tree.empty?
|
233
|
+
end
|
234
|
+
|
235
|
+
# See Hash#clear
|
236
|
+
def clear
|
237
|
+
@tree = RBTree::Tree.new
|
238
|
+
end
|
239
|
+
|
240
|
+
# See Hash#==
|
241
|
+
def ==(other)
|
242
|
+
return false unless other.kind_of?(RBTree)
|
243
|
+
return false unless other.size == self.size
|
244
|
+
return false unless other.cmp_proc == @cmp_proc
|
245
|
+
|
246
|
+
ary = self.to_a
|
247
|
+
other_ary = other.to_a
|
248
|
+
ary.each_with_index { |v, i| return false unless other_ary[i] == v }
|
249
|
+
true
|
250
|
+
end
|
251
|
+
|
252
|
+
# See Hash#each
|
253
|
+
def each
|
254
|
+
if block_given?
|
255
|
+
lock_changes do
|
256
|
+
@tree.inorder { |node| yield node.key, node.value }
|
257
|
+
end
|
258
|
+
else
|
259
|
+
Enumerator.new self, :each
|
260
|
+
end
|
261
|
+
end
|
262
|
+
alias :each_pair :each
|
263
|
+
|
264
|
+
# See Hash#reverse_each
|
265
|
+
def reverse_each
|
266
|
+
if block_given?
|
267
|
+
lock_changes do
|
268
|
+
@tree.reverse_inorder { |node| yield node.key, node.value }
|
269
|
+
end
|
270
|
+
else
|
271
|
+
Enumerator.new self, :reverse_each
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
# See Hash#index
|
277
|
+
def index(value)
|
278
|
+
each { |k, v| return k if value == v }
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
|
282
|
+
# See Hash#fetch
|
283
|
+
def fetch(key, *default)
|
284
|
+
if default.length > 1
|
285
|
+
raise ArgumentError, "expected at most 1 default, got #{default.length}"
|
286
|
+
end
|
287
|
+
if default.length == 1 && block_given?
|
288
|
+
$stderr << "warning: block supersedes default value argument"
|
289
|
+
end
|
290
|
+
|
291
|
+
node = tree.search key
|
292
|
+
return node.value if node
|
293
|
+
if block_given?
|
294
|
+
yield key
|
295
|
+
else
|
296
|
+
if default.length == 1
|
297
|
+
default.first
|
298
|
+
else
|
299
|
+
raise IndexError, 'key not found'
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# See Hash#delete
|
305
|
+
def delete(key)
|
306
|
+
node = @tree.search key
|
307
|
+
unless node
|
308
|
+
return block_given? ? yield : nil
|
309
|
+
end
|
310
|
+
@tree.delete node
|
311
|
+
node.value
|
312
|
+
end
|
313
|
+
|
314
|
+
# See Hash#delete_if
|
315
|
+
def delete_if(&block)
|
316
|
+
reject!(&block)
|
317
|
+
self
|
318
|
+
end
|
319
|
+
|
320
|
+
# See Hash#reject!
|
321
|
+
def reject!
|
322
|
+
if block_given?
|
323
|
+
dead_nodes = []
|
324
|
+
lock_changes do
|
325
|
+
@tree.inorder do |node|
|
326
|
+
dead_nodes << node if yield node.key, node.value
|
327
|
+
end
|
328
|
+
end
|
329
|
+
dead_nodes.each { |node| @tree.delete node }
|
330
|
+
dead_nodes.empty? ? nil : self
|
331
|
+
else
|
332
|
+
Enumerator.new self, :each
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# See Hash#reject
|
337
|
+
def reject(&block)
|
338
|
+
copy = self.dup
|
339
|
+
copy.reject!(&block)
|
340
|
+
# NOTE: the correct answer should be "copy", but we're copying RBTree
|
341
|
+
# bug-for-bug
|
342
|
+
# copy
|
343
|
+
end
|
344
|
+
|
345
|
+
# See Hash#each_key.
|
346
|
+
def each_key
|
347
|
+
if block_given?
|
348
|
+
lock_changes do
|
349
|
+
@tree.inorder { |node| yield node.key }
|
350
|
+
end
|
351
|
+
else
|
352
|
+
Enumerator.new self, :each_key
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# See Hash#each_value.
|
357
|
+
def each_value
|
358
|
+
if block_given?
|
359
|
+
lock_changes do
|
360
|
+
@tree.inorder { |node| yield node.value }
|
361
|
+
end
|
362
|
+
else
|
363
|
+
Enumerator.new self, :each_value
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# See Hash#keys.
|
368
|
+
def keys
|
369
|
+
result = Array.new
|
370
|
+
each_key { |key| result << key }
|
371
|
+
result
|
372
|
+
end
|
373
|
+
|
374
|
+
# See Hash#values.
|
375
|
+
def values
|
376
|
+
result = Array.new
|
377
|
+
each_value { |value| result << value }
|
378
|
+
result
|
379
|
+
end
|
380
|
+
|
381
|
+
# See Hash#has_key?
|
382
|
+
def has_key?(key)
|
383
|
+
!!@tree.search(key)
|
384
|
+
end
|
385
|
+
|
386
|
+
# See Hash#has_value?
|
387
|
+
def has_value?(value)
|
388
|
+
lock_changes do
|
389
|
+
each_value { |v| return true if value == v }
|
390
|
+
end
|
391
|
+
false
|
392
|
+
end
|
393
|
+
|
394
|
+
# See Hash#invert
|
395
|
+
def invert
|
396
|
+
tree = self.class.new
|
397
|
+
each { |key, value| tree[value] = key }
|
398
|
+
tree
|
399
|
+
end
|
400
|
+
|
401
|
+
# See Hash#values_at
|
402
|
+
def values_at(*keys)
|
403
|
+
keys.map { |key| self[key] }
|
404
|
+
end
|
405
|
+
|
406
|
+
# See Hash#merge!
|
407
|
+
def merge!(other)
|
408
|
+
unless other.instance_of? RBTree
|
409
|
+
raise TypeError, "wrong argument type #{other.class} (expected RBTree)"
|
410
|
+
end
|
411
|
+
|
412
|
+
if block_given?
|
413
|
+
other.each do |key, value|
|
414
|
+
if node = @tree.search(key)
|
415
|
+
node.value = yield key, node.value, value
|
416
|
+
else
|
417
|
+
self[key] = value
|
418
|
+
end
|
419
|
+
end
|
420
|
+
else
|
421
|
+
other.each { |key, value| self[key] = value }
|
422
|
+
end
|
423
|
+
self
|
424
|
+
end
|
425
|
+
alias :update :merge!
|
426
|
+
|
427
|
+
# See Hash#merge
|
428
|
+
def merge(other)
|
429
|
+
copy = self.dup
|
430
|
+
copy.merge! other
|
431
|
+
copy
|
432
|
+
end
|
433
|
+
|
434
|
+
# :nodoc:
|
435
|
+
def to_s
|
436
|
+
to_a.to_s
|
437
|
+
end
|
438
|
+
|
439
|
+
# A new Hash with the same contents and defaults as this RBTree instance.
|
440
|
+
def to_hash
|
441
|
+
if @default_proc && !Hash.method_defined?(:default_proc=)
|
442
|
+
# Slow path for default block and Ruby 1.8.7
|
443
|
+
hash = Hash.new &@default_proc
|
444
|
+
each { |key, value| hash[key] = value }
|
445
|
+
return hash
|
446
|
+
end
|
447
|
+
|
448
|
+
hash = Hash[to_a]
|
449
|
+
if @default_proc
|
450
|
+
hash.default_proc = @default_proc if hash.respond_to? :default_proc=
|
451
|
+
else
|
452
|
+
hash.default = @default
|
453
|
+
end
|
454
|
+
hash
|
455
|
+
end
|
456
|
+
|
457
|
+
# :nodoc:
|
458
|
+
def inspect
|
459
|
+
contents = map { |k, v|
|
460
|
+
k_inspect = k.equal?(self) ? '#<RBTree: ...>' : k.inspect
|
461
|
+
v_inspect = v.equal?(self) ? '#<RBTree: ...>' : v.inspect
|
462
|
+
"#{k_inspect}=>#{v_inspect}"
|
463
|
+
}.join(', ')
|
464
|
+
default_inspect = default.equal?(self) ? '#<RBTree: ...>' : default.inspect
|
465
|
+
"#<RBTree: {#{contents}}, default=#{default_inspect}, cmp_proc=#{@cmp_proc.inspect}>"
|
466
|
+
end
|
467
|
+
|
468
|
+
def pretty_print(q)
|
469
|
+
q.group(1, '#<RBTree: ', '>') do
|
470
|
+
q.pp_hash self
|
471
|
+
q.text ','
|
472
|
+
q.breakable ' '
|
473
|
+
q.text 'default='
|
474
|
+
q.pp default
|
475
|
+
q.text ','
|
476
|
+
q.breakable ' '
|
477
|
+
q.text 'cmp_proc='
|
478
|
+
q.pp cmp_proc
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
def pretty_print_cycle(q)
|
483
|
+
q.text '"#<RBTree: ...>"'
|
484
|
+
end
|
485
|
+
end # class RBTree::RBTree
|