avl_tree 1.0.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/README ADDED
@@ -0,0 +1,21 @@
1
+ avl_tree - Naive implementation of AVL tree for Ruby
2
+ Copyright (C) 2012 Hiroshi Nakamura <nahi@ruby-lang.org>
3
+
4
+
5
+ == TODO
6
+
7
+ It's still under development.
8
+
9
+
10
+ == Author
11
+
12
+ Name:: Hiroshi Nakamura
13
+ E-mail:: nahi@ruby-lang.org
14
+ Project web site:: http://github.com/nahi/radix_tree
15
+
16
+
17
+ == License
18
+
19
+ This program is copyrighted free software by Hiroshi Nakamura. You can
20
+ redistribute it and/or modify it under the same terms of Ruby's license;
21
+ either the dual license version in 2003, or any later version.
data/bench/bench.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'benchmark'
2
+ require 'radix_tree' # gem install radix_tree
3
+ require 'avl_tree'
4
+
5
+ random = Random.new(0)
6
+
7
+ TIMES = 100000
8
+ key_size = 10
9
+
10
+ def aset(h, keys)
11
+ keys.each do |k|
12
+ h[k] = 1
13
+ end
14
+ end
15
+
16
+ def aref(h, keys)
17
+ keys.each do |k|
18
+ h[k]
19
+ end
20
+ end
21
+
22
+ def delete(h, keys)
23
+ keys.each do |k|
24
+ h.delete(k)
25
+ end
26
+ end
27
+
28
+ def run(bm, h, keys)
29
+ name = h.class.name
30
+ bm.report("#{name} aset") do
31
+ aset(h, keys)
32
+ end
33
+ bm.report("#{name} aref") do
34
+ aref(h, keys)
35
+ end
36
+ bm.report("#{name} delete") do
37
+ delete(h, keys)
38
+ end
39
+ end
40
+
41
+ keys = []
42
+ TIMES.times do
43
+ keys << random.bytes(key_size)
44
+ end
45
+
46
+ Benchmark.bmbm do |bm|
47
+ run(bm, Hash.new, keys)
48
+ run(bm, RadixTree.new, keys)
49
+ run(bm, AVLTree.new, keys)
50
+ end
@@ -0,0 +1,52 @@
1
+ require 'benchmark'
2
+ require 'radix_tree' # gem install radix_tree
3
+ require 'avl_tree'
4
+
5
+ random = Random.new(0)
6
+
7
+ times = 100000
8
+ key_size = 10
9
+
10
+ def aset(h, keys)
11
+ keys.each do |k|
12
+ h[k] = 1
13
+ end
14
+ end
15
+
16
+ def aref(h, keys)
17
+ keys.each do |k|
18
+ h[k]
19
+ end
20
+ end
21
+
22
+ def delete(h, keys)
23
+ keys.each do |k|
24
+ h.delete(k)
25
+ end
26
+ end
27
+
28
+ def run(bm, h, keys)
29
+ name = h.class.name
30
+ bm.report("#{name} aset (#{keys.size})") do
31
+ aset(h, keys)
32
+ end
33
+ bm.report("#{name} aref (#{keys.size})") do
34
+ aref(h, keys)
35
+ end
36
+ bm.report("#{name} delete (#{keys.size})") do
37
+ delete(h, keys)
38
+ end
39
+ end
40
+
41
+ [10000, 20000, 50000, 100000, 200000, 500000].each do |elements|
42
+ keys = []
43
+ elements.times do
44
+ keys << random.bytes(key_size)
45
+ end
46
+
47
+ Benchmark.bm(30) do |bm|
48
+ run(bm, Hash.new, keys)
49
+ run(bm, RadixTree.new, keys)
50
+ run(bm, AVLTree.new, keys)
51
+ end
52
+ end
data/bench/profile.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.expand_path('../lib/avl_tree', File.dirname(__FILE__))
2
+
3
+ random = Random.new(0)
4
+
5
+ TIMES = 200000
6
+ key_size = 10
7
+
8
+ h = AVLTree.new
9
+ TIMES.times do
10
+ h[random.bytes(key_size)] = 1
11
+ h[random.bytes(key_size)]
12
+ h.delete(random.bytes(key_size))
13
+ end
data/lib/avl_tree.rb ADDED
@@ -0,0 +1,454 @@
1
+ class AVLTree
2
+ include Enumerable
3
+
4
+ class Node
5
+ UNDEFINED = Object.new
6
+
7
+ class EmptyNode
8
+ def empty?
9
+ true
10
+ end
11
+
12
+ def height
13
+ 0
14
+ end
15
+
16
+ def value
17
+ nil
18
+ end
19
+
20
+ def size
21
+ 0
22
+ end
23
+
24
+ def each(&block)
25
+ # intentionally blank
26
+ end
27
+
28
+ # returns new_root
29
+ def store(key, value)
30
+ Node.new(key, value)
31
+ end
32
+
33
+ # returns value
34
+ def retrieve(key)
35
+ UNDEFINED
36
+ end
37
+
38
+ # returns [deleted_node, new_root]
39
+ def delete(key)
40
+ [self, self]
41
+ end
42
+
43
+ def dump_tree(io, indent = '')
44
+ # intentionally blank
45
+ end
46
+
47
+ def dump_sexp
48
+ # intentionally blank
49
+ end
50
+
51
+ def rotate
52
+ self
53
+ end
54
+
55
+ def update_height
56
+ # intentionally blank
57
+ end
58
+
59
+ # for debugging
60
+ def check_height
61
+ # intentionally blank
62
+ end
63
+ end
64
+ EMPTY = Node::EmptyNode.new
65
+
66
+ attr_reader :key, :value, :height
67
+ attr_reader :left, :right
68
+
69
+ def initialize(key, value)
70
+ @key, @value = key, value
71
+ @left = @right = EMPTY
72
+ @height = 1
73
+ end
74
+
75
+ def empty?
76
+ false
77
+ end
78
+
79
+ def size
80
+ @left.size + 1 + @right.size
81
+ end
82
+
83
+ # inorder
84
+ def each(&block)
85
+ @left.each(&block)
86
+ yield [@key, @value]
87
+ @right.each(&block)
88
+ end
89
+
90
+ def each_key
91
+ each do |k, v|
92
+ yield k
93
+ end
94
+ end
95
+
96
+ def each_value
97
+ each do |k, v|
98
+ yield v
99
+ end
100
+ end
101
+
102
+ def keys
103
+ collect { |k, v| k }
104
+ end
105
+
106
+ def values
107
+ collect { |k, v| v }
108
+ end
109
+
110
+ # returns new_root
111
+ def store(key, value)
112
+ case key <=> @key
113
+ when -1
114
+ @left = @left.store(key, value)
115
+ when 0
116
+ @value = value
117
+ when 1
118
+ @right = @right.store(key, value)
119
+ end
120
+ rotate
121
+ end
122
+
123
+ # returns value
124
+ def retrieve(key)
125
+ case key <=> @key
126
+ when -1
127
+ @left.retrieve(key)
128
+ when 0
129
+ @value
130
+ when 1
131
+ @right.retrieve(key)
132
+ end
133
+ end
134
+
135
+ # returns [deleted_node, new_root]
136
+ def delete(key)
137
+ case key <=> @key
138
+ when -1
139
+ deleted, @left = @left.delete(key)
140
+ [deleted, self.rotate]
141
+ when 0
142
+ [self, delete_self.rotate]
143
+ when 1
144
+ deleted, @right = @right.delete(key)
145
+ [deleted, self.rotate]
146
+ end
147
+ end
148
+
149
+ def delete_min
150
+ if @left.empty?
151
+ [self, delete_self]
152
+ else
153
+ deleted, @left = @left.delete_min
154
+ [deleted, rotate]
155
+ end
156
+ end
157
+
158
+ def delete_max
159
+ if @right.empty?
160
+ [self, delete_self]
161
+ else
162
+ deleted, @right = @right.delete_max
163
+ [deleted, rotate]
164
+ end
165
+ end
166
+
167
+ def dump_tree(io, indent = '')
168
+ @right.dump_tree(io, indent + ' ')
169
+ io << indent << sprintf("#<%s:0x%010x %d %s> => %s", self.class.name, __id__, height, @key.inspect, @value.inspect) << $/
170
+ @left.dump_tree(io, indent + ' ')
171
+ end
172
+
173
+ def dump_sexp
174
+ left = @left.dump_sexp
175
+ right = @right.dump_sexp
176
+ if left or right
177
+ '(' + [@key, left || '-', right].compact.join(' ') + ')'
178
+ else
179
+ @key
180
+ end
181
+ end
182
+
183
+ # for debugging
184
+ def check_height
185
+ @left.check_height
186
+ @right.check_height
187
+ lh = @left.height
188
+ rh = @right.height
189
+ if (lh - rh).abs > 1
190
+ puts dump_tree(STDERR)
191
+ raise "height unbalanced: #{lh} #{height} #{rh}"
192
+ end
193
+ if (lh > rh ? lh : rh) + 1 != height
194
+ puts dump_tree(STDERR)
195
+ raise "height calc failure: #{lh} #{height} #{rh}"
196
+ end
197
+ end
198
+
199
+ protected
200
+
201
+ def left=(left)
202
+ @left = left
203
+ end
204
+
205
+ def right=(right)
206
+ @right = right
207
+ end
208
+
209
+ def update_height
210
+ @height = (@left.height > @right.height ? @left.height : @right.height) + 1
211
+ end
212
+
213
+ def rotate
214
+ case @left.height - @right.height
215
+ when +2
216
+ if @left.left.height >= @left.right.height
217
+ root = rotate_LL
218
+ else
219
+ root = rotate_LR
220
+ end
221
+ when -2
222
+ if @right.left.height <= @right.right.height
223
+ root = rotate_RR
224
+ else
225
+ root = rotate_RL
226
+ end
227
+ else
228
+ root = self
229
+ end
230
+ root.update_height
231
+ root
232
+ end
233
+
234
+ private
235
+
236
+ def delete_self
237
+ if @left.empty? and @right.empty?
238
+ deleted = EMPTY
239
+ elsif @right.height < @left.height
240
+ deleted, new_left = @left.delete_max
241
+ deleted.left, deleted.right = new_left, @right
242
+ else
243
+ deleted, new_right = @right.delete_min
244
+ deleted.left, deleted.right = @left, new_right
245
+ end
246
+ deleted
247
+ end
248
+
249
+ # Right single rotation
250
+ # (B a (D c E)) where D-a > 1 && E > c --> (D (B a c) E)
251
+ #
252
+ # B D
253
+ # / \ / \
254
+ # a D -> B E
255
+ # / \ / \
256
+ # c E a c
257
+ #
258
+ def rotate_RR
259
+ root = @right
260
+ @right = root.left
261
+ root.left = self
262
+ root.left.update_height
263
+ root
264
+ end
265
+
266
+ # Left single rotation
267
+ # (D (B A c) e) where B-e > 1 && A > c --> (B A (D c e))
268
+ #
269
+ # D B
270
+ # / \ / \
271
+ # B e -> A D
272
+ # / \ / \
273
+ # A c c e
274
+ #
275
+ def rotate_LL
276
+ root = @left
277
+ @left = root.right
278
+ root.right = self
279
+ root.right.update_height
280
+ root
281
+ end
282
+
283
+ # Right double rotation
284
+ # (B a (F (D c e) g)) where F-a > 1 && D > g --> (D (B a c) (F e g))
285
+ #
286
+ # B D
287
+ # / \ / \
288
+ # a F -> B F
289
+ # / \ / \ / \
290
+ # D g a c e g
291
+ # / \
292
+ # c e
293
+ #
294
+ def rotate_RL
295
+ other = @right
296
+ root = other.left
297
+ @right = root.left
298
+ other.left = root.right
299
+ root.left = self
300
+ root.right = other
301
+ root.left.update_height
302
+ root.right.update_height
303
+ root
304
+ end
305
+
306
+ # Left double rotation
307
+ # (F (B a (D c e)) g) where B-g > 1 && D > a --> (d (B a c) (F e g))
308
+ #
309
+ # F D
310
+ # / \ / \
311
+ # B g -> B F
312
+ # / \ / \ / \
313
+ # a D a c e g
314
+ # / \
315
+ # c e
316
+ #
317
+ def rotate_LR
318
+ other = @left
319
+ root = other.right
320
+ @left = root.right
321
+ other.right = root.left
322
+ root.right = self
323
+ root.left = other
324
+ root.left.update_height
325
+ root.right.update_height
326
+ root
327
+ end
328
+
329
+ def collect
330
+ pool = []
331
+ each do |key, value|
332
+ pool << yield(key, value)
333
+ end
334
+ pool
335
+ end
336
+ end
337
+
338
+ DEFAULT = Object.new
339
+
340
+ attr_accessor :default
341
+ attr_reader :default_proc
342
+
343
+ def initialize(default = DEFAULT, &block)
344
+ if block && default != DEFAULT
345
+ raise ArgumentError, 'wrong number of arguments'
346
+ end
347
+ @root = Node::EMPTY
348
+ @default = default
349
+ @default_proc = block
350
+ end
351
+
352
+ def empty?
353
+ @root == Node::EMPTY
354
+ end
355
+
356
+ def size
357
+ @root.size
358
+ end
359
+ alias length size
360
+
361
+ def each(&block)
362
+ if block_given?
363
+ @root.each(&block)
364
+ self
365
+ else
366
+ Enumerator.new(@root)
367
+ end
368
+ end
369
+ alias each_pair each
370
+
371
+ def each_key
372
+ if block_given?
373
+ @root.each do |k, v|
374
+ yield k
375
+ end
376
+ self
377
+ else
378
+ Enumerator.new(@root, :each_key)
379
+ end
380
+ end
381
+
382
+ def each_value
383
+ if block_given?
384
+ @root.each do |k, v|
385
+ yield v
386
+ end
387
+ self
388
+ else
389
+ Enumerator.new(@root, :each_value)
390
+ end
391
+ end
392
+
393
+ def keys
394
+ @root.keys
395
+ end
396
+
397
+ def values
398
+ @root.values
399
+ end
400
+
401
+ def clear
402
+ @root = Node::EMPTY
403
+ end
404
+
405
+ def []=(key, value)
406
+ @root = @root.store(key.to_s, value)
407
+ end
408
+ alias store []=
409
+
410
+ def key?(key)
411
+ @root.retrieve(key.to_s) != Node::UNDEFINED
412
+ end
413
+ alias has_key? key?
414
+
415
+ def [](key)
416
+ value = @root.retrieve(key.to_s)
417
+ if value == Node::UNDEFINED
418
+ default_value
419
+ else
420
+ value
421
+ end
422
+ end
423
+
424
+ def delete(key)
425
+ deleted, @root = @root.delete(key.to_s)
426
+ deleted.value
427
+ end
428
+
429
+ def dump_tree(io = '')
430
+ @root.dump_tree(io)
431
+ io << $/
432
+ io
433
+ end
434
+
435
+ def dump_sexp
436
+ @root.dump_sexp || ''
437
+ end
438
+
439
+ def to_hash
440
+ inject({}) { |r, (k, v)| r[k] = v; r }
441
+ end
442
+
443
+ private
444
+
445
+ def default_value
446
+ if @default != DEFAULT
447
+ @default
448
+ elsif @default_proc
449
+ @default_proc.call
450
+ else
451
+ nil
452
+ end
453
+ end
454
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,7 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ rescue LoadError
5
+ end
6
+ require "test/unit"
7
+ require "avl_tree"
@@ -0,0 +1,445 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('./helper', File.dirname(__FILE__))
3
+
4
+ class TestAVLTree < Test::Unit::TestCase
5
+ def test_tree_rotate_RR
6
+ h = AVLTree.new
7
+ assert_equal '', h.dump_sexp
8
+ h['a'] = 1
9
+ assert_equal 'a', h.dump_sexp
10
+ h['b'] = 2
11
+ assert_equal '(a - b)', h.dump_sexp
12
+ h['c'] = 3
13
+ assert_equal '(b a c)', h.dump_sexp
14
+ h['d'] = 4
15
+ assert_equal '(b a (c - d))', h.dump_sexp
16
+ h['e'] = 5
17
+ assert_equal '(b a (d c e))', h.dump_sexp
18
+ end
19
+
20
+ def test_tree_rotate_LL
21
+ h = AVLTree.new
22
+ h['e'] = 1
23
+ h['d'] = 2
24
+ assert_equal '(e d)', h.dump_sexp
25
+ h['c'] = 3
26
+ assert_equal '(d c e)', h.dump_sexp
27
+ h['b'] = 4
28
+ assert_equal '(d (c b) e)', h.dump_sexp
29
+ h['a'] = 5
30
+ assert_equal '(d (b a c) e)', h.dump_sexp
31
+ end
32
+
33
+ def test_tree_rotate_RL
34
+ h = AVLTree.new
35
+ h['b'] = 1
36
+ h['a'] = 2
37
+ h['e'] = 3
38
+ h['d'] = 4
39
+ h['f'] = 5
40
+ assert_equal '(b a (e d f))', h.dump_sexp
41
+ h['c'] = 6
42
+ assert_equal '(d (b a c) (e - f))', h.dump_sexp
43
+ end
44
+
45
+ def test_tree_rotate_LR
46
+ h = AVLTree.new
47
+ h['g'] = 1
48
+ h['b'] = 2
49
+ h['h'] = 3
50
+ h['i'] = 4
51
+ h['a'] = 5
52
+ h['d'] = 6
53
+ h['0'] = 7
54
+ h['c'] = 8
55
+ h['e'] = 9
56
+ assert_equal '(g (b (a 0) (d c e)) (h - i))', h.dump_sexp
57
+ h['f'] = 10
58
+ assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp
59
+ end
60
+
61
+ def test_aref_nil
62
+ h = AVLTree.new
63
+ h['abc'] = 1
64
+ assert_equal nil, h['def']
65
+ end
66
+
67
+ def test_empty
68
+ h = AVLTree.new
69
+ h['abc'] = 0
70
+ assert_equal nil, h['']
71
+ h[''] = 1
72
+ assert_equal 1, h['']
73
+ h.delete('')
74
+ assert_equal nil, h['']
75
+ end
76
+
77
+ def test_aref_single
78
+ h = AVLTree.new
79
+ h['abc'] = 1
80
+ assert_equal 1, h['abc']
81
+ end
82
+
83
+ def test_aref_double
84
+ h = AVLTree.new
85
+ h['abc'] = 1
86
+ h['def'] = 2
87
+ assert_equal 1, h['abc']
88
+ assert_equal 2, h['def']
89
+ end
90
+
91
+ def test_aset_override
92
+ h = AVLTree.new
93
+ h['abc'] = 1
94
+ h['abc'] = 2
95
+ assert_equal 2, h['abc']
96
+ end
97
+
98
+ def test_split
99
+ h = AVLTree.new
100
+ h['abcd'] = 1
101
+ assert_equal 1, h['abcd']
102
+ h['abce'] = 2
103
+ assert_equal 1, h['abcd']
104
+ assert_equal 2, h['abce']
105
+ h['abd'] = 3
106
+ assert_equal 1, h['abcd']
107
+ assert_equal 2, h['abce']
108
+ assert_equal 3, h['abd']
109
+ h['ac'] = 4
110
+ assert_equal 1, h['abcd']
111
+ assert_equal 2, h['abce']
112
+ assert_equal 3, h['abd']
113
+ assert_equal 4, h['ac']
114
+ end
115
+
116
+ def test_split_and_assign
117
+ h = AVLTree.new
118
+ h['ab'] = 1
119
+ h['a'] = 2
120
+ assert_equal 1, h['ab']
121
+ assert_equal 2, h['a']
122
+ end
123
+
124
+ def test_push
125
+ h = AVLTree.new
126
+ assert_equal 0, h.size
127
+ h['a'] = 1
128
+ assert_equal 1, h['a']
129
+ h['ab'] = 2
130
+ assert_equal 1, h['a']
131
+ assert_equal 2, h['ab']
132
+ h['abc'] = 3
133
+ assert_equal 1, h['a']
134
+ assert_equal 2, h['ab']
135
+ assert_equal 3, h['abc']
136
+ h['abd'] = 4
137
+ assert_equal 1, h['a']
138
+ assert_equal 2, h['ab']
139
+ assert_equal 3, h['abc']
140
+ assert_equal 4, h['abd']
141
+ h['ac'] = 5
142
+ assert_equal 1, h['a']
143
+ assert_equal 2, h['ab']
144
+ assert_equal 3, h['abc']
145
+ assert_equal 4, h['abd']
146
+ assert_equal 5, h['ac']
147
+ h['b'] = 6
148
+ assert_equal 1, h['a']
149
+ assert_equal 2, h['ab']
150
+ assert_equal 3, h['abc']
151
+ assert_equal 4, h['abd']
152
+ assert_equal 5, h['ac']
153
+ assert_equal 6, h['b']
154
+ assert_equal ['a', 'ab', 'abc', 'abd', 'ac', 'b'].sort, h.keys.sort
155
+ assert_equal 6, h.size
156
+ end
157
+
158
+ def test_delete_leaf
159
+ h = AVLTree.new
160
+ h['b'] = 1
161
+ h['a'] = 2
162
+ h['c'] = 3
163
+ assert_equal 2, h['a']
164
+ h.delete('a')
165
+ assert_equal nil, h['a']
166
+ end
167
+
168
+ def test_delete_leaf_single_rotation
169
+ h = AVLTree.new
170
+ h['b'] = 1
171
+ h['a'] = 2
172
+ h['d'] = 3
173
+ h['c'] = 4
174
+ h['e'] = 5
175
+ assert_equal '(b a (d c e))', h.dump_sexp
176
+ h.delete('a')
177
+ assert_equal '(d (b - c) e)', h.dump_sexp
178
+ end
179
+
180
+ def test_delete_leaf_double_rotation
181
+ h = AVLTree.new
182
+ h['b'] = 1
183
+ h['a'] = 2
184
+ h['e'] = 3
185
+ h['0'] = 4
186
+ h['c'] = 5
187
+ h['f'] = 6
188
+ h['d'] = 7
189
+ assert_equal '(b (a 0) (e (c - d) f))', h.dump_sexp
190
+ h.delete('0')
191
+ assert_equal '(c (b a) (e d f))', h.dump_sexp
192
+ end
193
+
194
+ def test_delete_node_right
195
+ h = AVLTree.new
196
+ h['c'] = 1
197
+ h['b'] = 2
198
+ h['g'] = 3
199
+ h['a'] = 4
200
+ h['e'] = 5
201
+ h['i'] = 6
202
+ h['d'] = 7
203
+ h['f'] = 8
204
+ h['h'] = 9
205
+ h['j'] = 10
206
+ assert_equal '(c (b a) (g (e d f) (i h j)))', h.dump_sexp
207
+ h.delete('g')
208
+ assert_equal '(c (b a) (h (e d f) (i - j)))', h.dump_sexp
209
+ end
210
+
211
+ def test_delete_node_left
212
+ h = AVLTree.new
213
+ h['c'] = 1
214
+ h['b'] = 2
215
+ h['d'] = 3
216
+ h['a'] = 4
217
+ assert_equal '(c (b a) d)', h.dump_sexp
218
+ h.delete('b')
219
+ assert_equal '(c a d)', h.dump_sexp
220
+ end
221
+
222
+ def test_delete_root
223
+ h = AVLTree.new
224
+ h['b'] = 1
225
+ h['a'] = 2
226
+ h['c'] = 3
227
+ assert_equal 1, h['b']
228
+ assert_equal '(b a c)', h.dump_sexp
229
+ h.delete('b')
230
+ assert_equal '(c a)', h.dump_sexp
231
+ assert_equal nil, h['b']
232
+ end
233
+
234
+ def test_delete
235
+ h = AVLTree.new
236
+ h['a'] = 1
237
+ h['ab'] = 2
238
+ h['abc'] = 3
239
+ h['abd'] = 4
240
+ h['ac'] = 5
241
+ h['b'] = 6
242
+ assert_equal 6, h.size
243
+ assert_equal nil, h.delete('XXX')
244
+ # delete leaf
245
+ assert_equal 4, h.delete('abd')
246
+ assert_equal 5, h.size
247
+ assert_equal 1, h['a']
248
+ assert_equal 2, h['ab']
249
+ assert_equal 3, h['abc']
250
+ assert_equal nil, h['abd']
251
+ assert_equal 5, h['ac']
252
+ assert_equal 6, h['b']
253
+ # delete single leaf node
254
+ assert_equal 2, h.delete('ab')
255
+ assert_equal 4, h.size
256
+ assert_equal 1, h['a']
257
+ assert_equal nil, h['ab']
258
+ assert_equal 3, h['abc']
259
+ assert_equal nil, h['abd']
260
+ assert_equal 5, h['ac']
261
+ assert_equal 6, h['b']
262
+ # delete multiple leaf node
263
+ assert_equal 1, h.delete('a')
264
+ assert_equal 3, h.size
265
+ assert_equal nil, h['a']
266
+ assert_equal nil, h['ab']
267
+ assert_equal 3, h['abc']
268
+ assert_equal nil, h['abd']
269
+ assert_equal 5, h['ac']
270
+ assert_equal 6, h['b']
271
+ assert_equal ['abc', 'ac', 'b'].sort, h.keys.sort
272
+ # delete rest
273
+ assert_equal 3, h.delete('abc')
274
+ assert_equal 5, h.delete('ac')
275
+ assert_equal 6, h.delete('b')
276
+ assert_equal 0, h.size
277
+ assert h.empty?
278
+ end
279
+
280
+ def test_delete_compaction_middle
281
+ h = AVLTree.new
282
+ h['a'] = 1
283
+ h['abc'] = 2
284
+ h['bb'] = 3
285
+ h['abcdefghi'] = 4
286
+ h['abcdefghijzz'] = 5
287
+ h['abcdefghikzz'] = 6
288
+ assert_equal 6, h.dump_tree.split($/).size
289
+ h.delete('a')
290
+ assert_equal 5, h.dump_tree.split($/).size
291
+ h['a'] = 1
292
+ assert_equal 6, h.dump_tree.split($/).size
293
+ end
294
+
295
+ def test_delete_compaction_leaf
296
+ h = AVLTree.new
297
+ h['a'] = 1
298
+ h['abc'] = 2
299
+ h['bb'] = 3
300
+ h['abcdefghijzz'] = 4
301
+ assert_equal 4, h.dump_tree.split($/).size
302
+ h['abcdefghikzz'] = 5
303
+ assert_equal 5, h.dump_tree.split($/).size
304
+ h.delete('abcdefghijzz')
305
+ assert_equal 4, h.dump_tree.split($/).size
306
+ h['abcdefghijzz'] = 4
307
+ assert_equal 5, h.dump_tree.split($/).size
308
+ end
309
+
310
+ def test_each
311
+ h = AVLTree.new
312
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
313
+ s.each do |k, v|
314
+ h[k] = v
315
+ end
316
+ assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k }
317
+ #
318
+ values = []
319
+ h.each do |k, v|
320
+ values << [k, v]
321
+ end
322
+ assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k }
323
+ end
324
+
325
+ def test_each_key
326
+ h = AVLTree.new
327
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
328
+ s.each do |k, v|
329
+ h[k] = v
330
+ end
331
+ assert_equal s.keys.sort, h.each_key.sort
332
+ #
333
+ values = []
334
+ h.each_key do |k|
335
+ values << k
336
+ end
337
+ assert_equal s.keys.sort, values.sort
338
+ end
339
+
340
+ def test_each_value
341
+ h = AVLTree.new
342
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 }
343
+ s.each do |k, v|
344
+ h[k] = v
345
+ end
346
+ assert_equal s.values.sort, h.each_value.sort
347
+ #
348
+ values = []
349
+ h.each_value do |v|
350
+ values << v
351
+ end
352
+ assert_equal s.values.sort, values.sort
353
+ end
354
+
355
+ def test_keys
356
+ h = AVLTree.new
357
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
358
+ s.each do |k, v|
359
+ h[k] = v
360
+ end
361
+ assert_equal s.keys.sort, h.keys.sort
362
+ end
363
+
364
+ def test_values
365
+ h = AVLTree.new
366
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
367
+ s.each do |k, v|
368
+ h[k] = v
369
+ end
370
+ assert_equal s.values.sort, h.values.sort
371
+ end
372
+
373
+ def test_to_s
374
+ h = AVLTree.new
375
+ h[:abc] = 1
376
+ assert_equal 1, h["abc"]
377
+ assert_equal 1, h[:abc]
378
+ end
379
+
380
+ def test_key?
381
+ h = AVLTree.new
382
+ assert !h.key?('a')
383
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
384
+ s.each do |k, v|
385
+ h[k] = v
386
+ end
387
+ assert h.key?('a')
388
+ end
389
+
390
+ def test_default
391
+ assert_raise(ArgumentError) do
392
+ AVLTree.new('both') { :not_allowed }
393
+ end
394
+
395
+ h = AVLTree.new('abc')
396
+ assert_equal 'abc', h['foo']
397
+ assert_equal 'abc', h['bar']
398
+ assert h['baz'].object_id == h['qux'].object_id
399
+
400
+ h = AVLTree.new { [1, 2] }
401
+ assert_equal [1, 2], h['foo']
402
+ assert_equal [1, 2], h['bar']
403
+ assert h['baz'].object_id != h['qux'].object_id
404
+ end
405
+
406
+ def test_to_hash
407
+ h = AVLTree.new
408
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
409
+ s.each do |k, v|
410
+ h[k] = v
411
+ end
412
+ assert_equal s, h.to_hash
413
+ end
414
+
415
+ def test_clear
416
+ h = AVLTree.new
417
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
418
+ s.each do |k, v|
419
+ h[k] = v
420
+ end
421
+ assert_equal s, h.to_hash
422
+ h.clear
423
+ assert_equal 0, h.size
424
+ assert h.to_hash.empty?
425
+ end
426
+
427
+ if RUBY_VERSION >= '1.9.0'
428
+ # In contrast to RadixTree, AVLTree just uses String#<=> as-is
429
+ def test_encoding
430
+ h = AVLTree.new
431
+ s = { '$B$"$"(B' => 1, '$B$"$$(B' => 2, '$B$$$$(B' => 3, '$B$$$&(B' => 4, '$B$"(B' => 5, '$B$"$$$&(B' => 6 }
432
+ s.each do |k, v|
433
+ h[k] = v
434
+ end
435
+ assert_equal 6, h.size
436
+ s.each do |k, v|
437
+ assert_equal v, h[k]
438
+ end
439
+ str = '$B$"$"(B'
440
+ str.force_encoding('US-ASCII')
441
+ # it's nil for RadixTree because RadixTree uses char-to-char comparison
442
+ assert_equal 1, h[str]
443
+ end
444
+ end
445
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: avl_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Hiroshi Nakamura
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: nahi@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/avl_tree.rb
21
+ - bench/bench_element_size.rb
22
+ - bench/profile.rb
23
+ - bench/bench.rb
24
+ - test/helper.rb
25
+ - test/test_avl_tree.rb
26
+ - README
27
+ homepage: http://github.com/nahi/avl_tree
28
+ licenses: []
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 1.8.11
48
+ signing_key:
49
+ specification_version: 3
50
+ summary: Naive implementation of AVL tree for Ruby
51
+ test_files: []