avl_tree 1.0.0 → 1.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/test/helper.rb CHANGED
@@ -5,3 +5,4 @@ rescue LoadError
5
5
  end
6
6
  require "test/unit"
7
7
  require "avl_tree"
8
+ require "red_black_tree"
@@ -0,0 +1,595 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('./helper', File.dirname(__FILE__))
3
+
4
+ class TestRedBlackTree < Test::Unit::TestCase
5
+ def __test_random
6
+ h = RedBlackTree.new
7
+ 10000.times do |idx|
8
+ key = rand(100)
9
+ h[key] = key
10
+ key = rand(100)
11
+ h.delete(key)
12
+ end
13
+ end
14
+
15
+ def test_tree_rotate_RR
16
+ h = RedBlackTree.new
17
+ assert_equal '', h.dump_sexp
18
+ h['a'] = 1
19
+ assert_equal 'a', h.dump_sexp
20
+ h['b'] = 2
21
+ assert_equal '(a - b)', h.dump_sexp
22
+ h['c'] = 3
23
+ assert_equal '(b a c)', h.dump_sexp
24
+ h['d'] = 4
25
+ assert_equal '(b a (c - d))', h.dump_sexp
26
+ h['e'] = 5
27
+ assert_equal '(b a (d c e))', h.dump_sexp
28
+ end
29
+
30
+ def test_tree_rotate_LL
31
+ h = RedBlackTree.new
32
+ h['e'] = 1
33
+ h['d'] = 2
34
+ assert_equal '(e d)', h.dump_sexp
35
+ h['c'] = 3
36
+ assert_equal '(d c e)', h.dump_sexp
37
+ h['b'] = 4
38
+ assert_equal '(d (c b) e)', h.dump_sexp
39
+ h['a'] = 5
40
+ assert_equal '(d (b a c) e)', h.dump_sexp
41
+ end
42
+
43
+ def test_tree_rotate_RL
44
+ h = RedBlackTree.new
45
+ h['b'] = 1
46
+ h['a'] = 2
47
+ h['g'] = 3
48
+ h['d'] = 4
49
+ h['h'] = 5
50
+ assert_equal '(b a (g d h))', h.dump_sexp
51
+ h['c'] = 6
52
+ assert_equal '(b a (g (d c) h))', h.dump_sexp
53
+ h['e'] = 6
54
+ assert_equal '(b a (g (d c e) h))', h.dump_sexp
55
+ h['f'] = 6
56
+ assert_equal '(d (b a c) (g (e - f) h))', h.dump_sexp
57
+ end
58
+
59
+ def test_tree_rotate_LR
60
+ h = RedBlackTree.new
61
+ h['g'] = 1
62
+ h['b'] = 2
63
+ h['h'] = 3
64
+ h['i'] = 4
65
+ h['a'] = 5
66
+ h['d'] = 6
67
+ h['0'] = 7
68
+ h['c'] = 8
69
+ h['e'] = 9
70
+ assert_equal '(g (b (a 0) (d c e)) (h - i))', h.dump_sexp
71
+ h['f'] = 10
72
+ assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp
73
+ end
74
+
75
+ def test_aref_nil
76
+ h = RedBlackTree.new
77
+ h['abc'] = 1
78
+ assert_equal nil, h['def']
79
+ end
80
+
81
+ def test_empty
82
+ h = RedBlackTree.new
83
+ h['abc'] = 0
84
+ assert_equal nil, h['']
85
+ h[''] = 1
86
+ assert_equal 1, h['']
87
+ h.delete('')
88
+ assert_equal nil, h['']
89
+ end
90
+
91
+ def test_aref_single
92
+ h = RedBlackTree.new
93
+ h['abc'] = 1
94
+ assert_equal 1, h['abc']
95
+ end
96
+
97
+ def test_aref_double
98
+ h = RedBlackTree.new
99
+ h['abc'] = 1
100
+ h['def'] = 2
101
+ assert_equal 1, h['abc']
102
+ assert_equal 2, h['def']
103
+ end
104
+
105
+ def test_aset_override
106
+ h = RedBlackTree.new
107
+ h['abc'] = 1
108
+ h['abc'] = 2
109
+ assert_equal 2, h['abc']
110
+ end
111
+
112
+ def test_split
113
+ h = RedBlackTree.new
114
+ h['abcd'] = 1
115
+ assert_equal 1, h['abcd']
116
+ h['abce'] = 2
117
+ assert_equal 1, h['abcd']
118
+ assert_equal 2, h['abce']
119
+ h['abd'] = 3
120
+ assert_equal 1, h['abcd']
121
+ assert_equal 2, h['abce']
122
+ assert_equal 3, h['abd']
123
+ h['ac'] = 4
124
+ assert_equal 1, h['abcd']
125
+ assert_equal 2, h['abce']
126
+ assert_equal 3, h['abd']
127
+ assert_equal 4, h['ac']
128
+ end
129
+
130
+ def test_split_and_assign
131
+ h = RedBlackTree.new
132
+ h['ab'] = 1
133
+ h['a'] = 2
134
+ assert_equal 1, h['ab']
135
+ assert_equal 2, h['a']
136
+ end
137
+
138
+ def test_push
139
+ h = RedBlackTree.new
140
+ assert_equal 0, h.size
141
+ h['a'] = 1
142
+ assert_equal 1, h['a']
143
+ h['ab'] = 2
144
+ assert_equal 1, h['a']
145
+ assert_equal 2, h['ab']
146
+ h['abc'] = 3
147
+ assert_equal 1, h['a']
148
+ assert_equal 2, h['ab']
149
+ assert_equal 3, h['abc']
150
+ h['abd'] = 4
151
+ assert_equal 1, h['a']
152
+ assert_equal 2, h['ab']
153
+ assert_equal 3, h['abc']
154
+ assert_equal 4, h['abd']
155
+ h['ac'] = 5
156
+ assert_equal 1, h['a']
157
+ assert_equal 2, h['ab']
158
+ assert_equal 3, h['abc']
159
+ assert_equal 4, h['abd']
160
+ assert_equal 5, h['ac']
161
+ h['b'] = 6
162
+ assert_equal 1, h['a']
163
+ assert_equal 2, h['ab']
164
+ assert_equal 3, h['abc']
165
+ assert_equal 4, h['abd']
166
+ assert_equal 5, h['ac']
167
+ assert_equal 6, h['b']
168
+ assert_equal ['a', 'ab', 'abc', 'abd', 'ac', 'b'].sort, h.keys.sort
169
+ assert_equal 6, h.size
170
+ end
171
+
172
+ def test_delete_leaf
173
+ h = RedBlackTree.new
174
+ h['b'] = 1
175
+ h['a'] = 2
176
+ h['c'] = 3
177
+ assert_equal 2, h['a']
178
+ h.delete('a')
179
+ assert_equal nil, h['a']
180
+ end
181
+
182
+ def test_delete_leaf_single_rotation
183
+ h = RedBlackTree.new
184
+ h['b'] = 1
185
+ h['a'] = 2
186
+ h['d'] = 3
187
+ h['c'] = 4
188
+ h['e'] = 5
189
+ assert_equal '(b a (d c e))', h.dump_sexp
190
+ h.delete('a')
191
+ assert_equal '(d (b - c) e)', h.dump_sexp
192
+ end
193
+
194
+ def test_delete_leaf_single_rotation_right
195
+ h = RedBlackTree.new
196
+ h['d'] = 1
197
+ h['e'] = 2
198
+ h['b'] = 3
199
+ h['c'] = 4
200
+ h['a'] = 5
201
+ assert_equal '(d (b a c) e)', h.dump_sexp
202
+ h.delete('e')
203
+ assert_equal '(b a (d c))', h.dump_sexp
204
+ end
205
+
206
+ def test_delete_leaf_double_rotation
207
+ h = RedBlackTree.new
208
+ h['b'] = 1
209
+ h['a'] = 2
210
+ h['e'] = 3
211
+ h['0'] = 4
212
+ h['c'] = 5
213
+ h['f'] = 6
214
+ h['d'] = 7
215
+ assert_equal '(b (a 0) (e (c - d) f))', h.dump_sexp
216
+ h.delete('0')
217
+ assert_equal '(b a (e (c - d) f))', h.dump_sexp
218
+ h.delete('a')
219
+ assert_equal '(e (c b d) f)', h.dump_sexp
220
+ end
221
+
222
+ def test_delete_leaf_double_rotation_right
223
+ h = RedBlackTree.new
224
+ h['d'] = 1
225
+ h['e'] = 2
226
+ h['a'] = 3
227
+ h['f'] = 4
228
+ h['c'] = 5
229
+ h['0'] = 6
230
+ h['b'] = 7
231
+ assert_equal '(d (a 0 (c b)) (e - f))', h.dump_sexp
232
+ h.delete('f')
233
+ assert_equal '(d (a 0 (c b)) e)', h.dump_sexp
234
+ h.delete('e')
235
+ assert_equal '(a 0 (c b d))', h.dump_sexp
236
+ end
237
+
238
+ def test_delete_node_right
239
+ h = RedBlackTree.new
240
+ h['c'] = 1
241
+ h['b'] = 2
242
+ h['g'] = 3
243
+ h['a'] = 4
244
+ h['e'] = 5
245
+ h['i'] = 6
246
+ h['d'] = 7
247
+ h['f'] = 8
248
+ h['h'] = 9
249
+ h['j'] = 10
250
+ assert_equal '(c (b a) (g (e d f) (i h j)))', h.dump_sexp
251
+ h.delete('g')
252
+ assert_equal '(c (b a) (h (e d f) (i - j)))', h.dump_sexp
253
+ end
254
+
255
+ def test_delete_node_left
256
+ h = RedBlackTree.new
257
+ h['h'] = 1
258
+ h['i'] = 2
259
+ h['d'] = 3
260
+ h['j'] = 4
261
+ h['f'] = 5
262
+ h['b'] = 6
263
+ h['g'] = 7
264
+ h['e'] = 8
265
+ h['c'] = 9
266
+ h['a'] = 10
267
+ assert_equal '(h (d (b a c) (f e g)) (i - j))', h.dump_sexp
268
+ h.delete('d')
269
+ assert_equal '(h (e (b a c) (f - g)) (i - j))', h.dump_sexp
270
+ end
271
+
272
+ def test_delete_root
273
+ h = RedBlackTree.new
274
+ h['b'] = 1
275
+ h['a'] = 2
276
+ h['c'] = 3
277
+ assert_equal 1, h['b']
278
+ assert_equal '(b a c)', h.dump_sexp
279
+ h.delete('b')
280
+ assert_equal '(c a)', h.dump_sexp
281
+ assert_equal nil, h['b']
282
+ end
283
+
284
+ def test_delete
285
+ h = RedBlackTree.new
286
+ h['a'] = 1
287
+ h['ab'] = 2
288
+ h['abc'] = 3
289
+ h['abd'] = 4
290
+ h['ac'] = 5
291
+ h['b'] = 6
292
+ assert_equal 6, h.size
293
+ assert_equal nil, h.delete('XXX')
294
+ # delete leaf
295
+ assert_equal 4, h.delete('abd')
296
+ assert_equal 5, h.size
297
+ assert_equal 1, h['a']
298
+ assert_equal 2, h['ab']
299
+ assert_equal 3, h['abc']
300
+ assert_equal nil, h['abd']
301
+ assert_equal 5, h['ac']
302
+ assert_equal 6, h['b']
303
+ # delete single leaf node
304
+ assert_equal 2, h.delete('ab')
305
+ assert_equal 4, h.size
306
+ assert_equal 1, h['a']
307
+ assert_equal nil, h['ab']
308
+ assert_equal 3, h['abc']
309
+ assert_equal nil, h['abd']
310
+ assert_equal 5, h['ac']
311
+ assert_equal 6, h['b']
312
+ # delete multiple leaf node
313
+ assert_equal 1, h.delete('a')
314
+ assert_equal 3, h.size
315
+ assert_equal nil, h['a']
316
+ assert_equal nil, h['ab']
317
+ assert_equal 3, h['abc']
318
+ assert_equal nil, h['abd']
319
+ assert_equal 5, h['ac']
320
+ assert_equal 6, h['b']
321
+ assert_equal ['abc', 'ac', 'b'].sort, h.keys.sort
322
+ # delete rest
323
+ assert_equal 3, h.delete('abc')
324
+ assert_equal 5, h.delete('ac')
325
+ assert_equal 6, h.delete('b')
326
+ assert_equal 0, h.size
327
+ assert h.empty?
328
+ end
329
+
330
+ def test_delete_right
331
+ h = RedBlackTree.new
332
+ h['f'] = 1
333
+ h['e'] = 2
334
+ h['d'] = 3
335
+ h['c'] = 4
336
+ h['b'] = 5
337
+ h['a'] = 6
338
+ assert_equal 6, h.size
339
+ assert_equal nil, h.delete('XXX')
340
+ # delete leaf
341
+ assert_equal 4, h.delete('c')
342
+ assert_equal 5, h.size
343
+ assert_equal 1, h['f']
344
+ assert_equal 2, h['e']
345
+ assert_equal 3, h['d']
346
+ assert_equal nil, h['c']
347
+ assert_equal 5, h['b']
348
+ assert_equal 6, h['a']
349
+ # delete single leaf node
350
+ assert_equal 2, h.delete('e')
351
+ assert_equal 4, h.size
352
+ assert_equal 1, h['f']
353
+ assert_equal nil, h['e']
354
+ assert_equal 3, h['d']
355
+ assert_equal nil, h['c']
356
+ assert_equal 5, h['b']
357
+ assert_equal 6, h['a']
358
+ # delete multiple leaf node
359
+ assert_equal 1, h.delete('f')
360
+ assert_equal 3, h.size
361
+ assert_equal nil, h['f']
362
+ assert_equal nil, h['e']
363
+ assert_equal 3, h['d']
364
+ assert_equal nil, h['c']
365
+ assert_equal 5, h['b']
366
+ assert_equal 6, h['a']
367
+ assert_equal ['a', 'b', 'd'].sort, h.keys.sort
368
+ # delete rest
369
+ assert_equal 3, h.delete('d')
370
+ assert_equal 5, h.delete('b')
371
+ assert_equal 6, h.delete('a')
372
+ assert_equal 0, h.size
373
+ assert h.empty?
374
+ end
375
+
376
+ def test_delete_compaction_middle
377
+ h = RedBlackTree.new
378
+ h['a'] = 1
379
+ h['abc'] = 2
380
+ h['bb'] = 3
381
+ h['abcdefghi'] = 4
382
+ h['abcdefghijzz'] = 5
383
+ h['abcdefghikzz'] = 6
384
+ assert_equal 6, h.dump_tree.split($/).size
385
+ h.delete('a')
386
+ assert_equal 5, h.dump_tree.split($/).size
387
+ h['a'] = 1
388
+ assert_equal 6, h.dump_tree.split($/).size
389
+ end
390
+
391
+ def test_delete_compaction_leaf
392
+ h = RedBlackTree.new
393
+ h['a'] = 1
394
+ h['abc'] = 2
395
+ h['bb'] = 3
396
+ h['abcdefghijzz'] = 4
397
+ assert_equal 4, h.dump_tree.split($/).size
398
+ h['abcdefghikzz'] = 5
399
+ assert_equal 5, h.dump_tree.split($/).size
400
+ h.delete('abcdefghijzz')
401
+ assert_equal 4, h.dump_tree.split($/).size
402
+ h['abcdefghijzz'] = 4
403
+ assert_equal 5, h.dump_tree.split($/).size
404
+ end
405
+
406
+ def test_delete_balanced_rotate_left
407
+ h = RedBlackTree.new
408
+ h['f'] = 1
409
+ h['c'] = 100
410
+ h['l'] = 1
411
+ h['b'] = 100
412
+ h['e'] = 1
413
+ h['i'] = 1
414
+ h['m'] = 1
415
+ h['a'] = 100
416
+ h['d'] = 1
417
+ h['h'] = 1
418
+ h['k'] = 1
419
+ h['n'] = 1
420
+ h['j'] = 1
421
+ h['g'] = 1
422
+ assert_equal '(f (c (b a) (e d)) (l (i (h g) (k j)) (m - n)))', h.dump_sexp
423
+ assert_equal 14, h.size
424
+ # reduce black from the left node
425
+ assert_equal 100, h.delete('b')
426
+ assert_equal 100, h.delete('a')
427
+ # double rotation at 'l' and 'f' node
428
+ assert_equal 100, h.delete('c')
429
+ assert_equal 11, h.size
430
+ assert_equal '(i (f (d - e) (h g)) (l (k j) (m - n)))', h.dump_sexp
431
+ end
432
+
433
+ def test_delete_balanced_rotate_right
434
+ h = RedBlackTree.new
435
+ h['i'] = 1
436
+ h['l'] = 100
437
+ h['c'] = 1
438
+ h['m'] = 100
439
+ h['j'] = 1
440
+ h['f'] = 1
441
+ h['b'] = 1
442
+ h['n'] = 100
443
+ h['k'] = 1
444
+ h['g'] = 1
445
+ h['d'] = 1
446
+ h['a'] = 1
447
+ h['e'] = 1
448
+ h['h'] = 1
449
+ assert_equal '(i (c (b a) (f (d - e) (g - h))) (l (j - k) (m - n)))', h.dump_sexp
450
+ assert_equal 14, h.size
451
+ # reduce black from the left node
452
+ assert_equal 100, h.delete('m')
453
+ assert_equal 100, h.delete('n')
454
+ # double rotation at 'c' and 'i' node
455
+ assert_equal 100, h.delete('l')
456
+ assert_equal 11, h.size
457
+ assert_equal '(f (c (b a) (d - e)) (i (g - h) (k j)))', h.dump_sexp
458
+ end
459
+
460
+ def test_each
461
+ h = RedBlackTree.new
462
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
463
+ s.each do |k, v|
464
+ h[k] = v
465
+ end
466
+ assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k }
467
+ #
468
+ values = []
469
+ h.each do |k, v|
470
+ values << [k, v]
471
+ end
472
+ assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k }
473
+ end
474
+
475
+ def test_each_key
476
+ h = RedBlackTree.new
477
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
478
+ s.each do |k, v|
479
+ h[k] = v
480
+ end
481
+ assert_equal s.keys.sort, h.each_key.sort
482
+ #
483
+ values = []
484
+ h.each_key do |k|
485
+ values << k
486
+ end
487
+ assert_equal s.keys.sort, values.sort
488
+ end
489
+
490
+ def test_each_value
491
+ h = RedBlackTree.new
492
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 }
493
+ s.each do |k, v|
494
+ h[k] = v
495
+ end
496
+ assert_equal s.values.sort, h.each_value.sort
497
+ #
498
+ values = []
499
+ h.each_value do |v|
500
+ values << v
501
+ end
502
+ assert_equal s.values.sort, values.sort
503
+ end
504
+
505
+ def test_keys
506
+ h = RedBlackTree.new
507
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
508
+ s.each do |k, v|
509
+ h[k] = v
510
+ end
511
+ assert_equal s.keys.sort, h.keys.sort
512
+ end
513
+
514
+ def test_values
515
+ h = RedBlackTree.new
516
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
517
+ s.each do |k, v|
518
+ h[k] = v
519
+ end
520
+ assert_equal s.values.sort, h.values.sort
521
+ end
522
+
523
+ def test_to_s
524
+ h = RedBlackTree.new
525
+ h[:abc] = 1
526
+ assert_equal 1, h["abc"]
527
+ assert_equal 1, h[:abc]
528
+ end
529
+
530
+ def test_key?
531
+ h = RedBlackTree.new
532
+ assert !h.key?('a')
533
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
534
+ s.each do |k, v|
535
+ h[k] = v
536
+ end
537
+ assert h.key?('a')
538
+ end
539
+
540
+ def test_default
541
+ assert_raise(ArgumentError) do
542
+ RedBlackTree.new('both') { :not_allowed }
543
+ end
544
+
545
+ h = RedBlackTree.new('abc')
546
+ assert_equal 'abc', h['foo']
547
+ assert_equal 'abc', h['bar']
548
+ assert h['baz'].object_id == h['qux'].object_id
549
+
550
+ h = RedBlackTree.new { [1, 2] }
551
+ assert_equal [1, 2], h['foo']
552
+ assert_equal [1, 2], h['bar']
553
+ assert h['baz'].object_id != h['qux'].object_id
554
+ end
555
+
556
+ def test_to_hash
557
+ h = RedBlackTree.new
558
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
559
+ s.each do |k, v|
560
+ h[k] = v
561
+ end
562
+ assert_equal s, h.to_hash
563
+ end
564
+
565
+ def test_clear
566
+ h = RedBlackTree.new
567
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
568
+ s.each do |k, v|
569
+ h[k] = v
570
+ end
571
+ assert_equal s, h.to_hash
572
+ h.clear
573
+ assert_equal 0, h.size
574
+ assert h.to_hash.empty?
575
+ end
576
+
577
+ if RUBY_VERSION >= '1.9.0'
578
+ # In contrast to RadixTree, RedBlackTree just uses String#<=> as-is
579
+ def test_encoding
580
+ h = RedBlackTree.new
581
+ s = { '$B$"$"(B' => 1, '$B$"$$(B' => 2, '$B$$$$(B' => 3, '$B$$$&(B' => 4, '$B$"(B' => 5, '$B$"$$$&(B' => 6 }
582
+ s.each do |k, v|
583
+ h[k] = v
584
+ end
585
+ assert_equal 6, h.size
586
+ s.each do |k, v|
587
+ assert_equal v, h[k]
588
+ end
589
+ str = '$B$"$"(B'
590
+ str.force_encoding('US-ASCII')
591
+ # it's nil for RadixTree because RadixTree uses char-to-char comparison
592
+ assert_equal 1, h[str]
593
+ end
594
+ end
595
+ end