radix_tree 1.1.0 → 1.2.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.
Files changed (4) hide show
  1. data/README +1 -0
  2. data/lib/radix_tree.rb +216 -14
  3. data/test/test_radix_tree.rb +235 -35
  4. metadata +4 -9
data/README CHANGED
@@ -20,6 +20,7 @@ See comments in lib/radix_tree.rb
20
20
 
21
21
  * 1.0.0 - Initial release.
22
22
  * 1.1.0 - 1.8 support, speed/memory perf improvement.
23
+ * 1.2.0 - Hash methods implemented by Leeheng. Thanks!
23
24
 
24
25
 
25
26
  == Author
@@ -5,23 +5,9 @@
5
5
  # 10 times slower for 10 bytes key, 100000 elements retrieval
6
6
  #
7
7
  # TODO: Implement following methods for Hash compatibility.
8
- # * delete_if
9
- # * reject
10
- # * reject!
11
- # * fetch
12
- # * values_at
13
- # * replace
14
- # * key
15
- # * shift
16
- # * has_value?/value?
17
- # * ==
18
- # * eql?
19
8
  # * hash
20
9
  #
21
10
  # TODO: Implement following features for utilizing strength of Radix Tree.
22
- # * find predecessor
23
- # * find successor
24
- # * find_all by start string
25
11
  # * delete_all by start string
26
12
  #
27
13
  class RadixTree
@@ -155,6 +141,80 @@ class RadixTree
155
141
  end
156
142
  end
157
143
 
144
+ def dup
145
+ if @children
146
+ children_dup = Hash.new
147
+ @children.each do |k,v|
148
+ children_dup[k] = v.dup
149
+ end
150
+ else
151
+ children_dup = nil
152
+ end
153
+ Node.new(@key, @index, @value, children_dup)
154
+ end
155
+ alias clone dup
156
+
157
+ def find_pre(key, head, p_key)
158
+ if same_key?(key)
159
+ (p_key == "" ? nil : p_key)
160
+ else
161
+ if @children
162
+ pos = head_match_size(key, head)
163
+ if child = find_child(key[pos])
164
+ return child.find_pre(key, @index, self.label)
165
+ end
166
+ end
167
+ nil
168
+ end
169
+ end
170
+
171
+ def find_suc(key, head, flag)
172
+ # check before the key or after
173
+ flag = true if !flag && same_key?(key)
174
+ if flag
175
+ # after tje key and omit undefined value
176
+ if undefined? || self.label == key
177
+ # if undefined keep going
178
+ if @children
179
+ # next by lexicographic order
180
+ k = @children.keys.sort.shift
181
+ return @children[k].find_suc(key, nil, flag)
182
+ end
183
+ nil
184
+ else
185
+ return self.label
186
+ end
187
+ else
188
+ # before the key
189
+ if @children
190
+ pos = head_match_size(key, head)
191
+ if child = find_child(key[pos])
192
+ return child.find_suc(key, @index, flag)
193
+ end
194
+ end
195
+ nil
196
+ end
197
+ end
198
+
199
+ def find_all(prefix, head, flag)
200
+ flag = true if !flag && same_key?(prefix)
201
+ if flag
202
+ strs = []
203
+ self.each do |l,v|
204
+ strs << l if v != UNDEFINED
205
+ end
206
+ return strs
207
+ else
208
+ if @children
209
+ pos = head_match_size(prefix, head)
210
+ if child = find_child(prefix[pos])
211
+ return child.find_all(prefix, @index, flag)
212
+ end
213
+ end
214
+ nil
215
+ end
216
+ end
217
+
158
218
  private
159
219
 
160
220
  def same_key?(key)
@@ -325,4 +385,146 @@ class RadixTree
325
385
  def to_hash
326
386
  inject({}) { |r, (k, v)| r[k] = v; r }
327
387
  end
388
+
389
+ def delete_if(&block)
390
+ if block_given?
391
+ temp = []
392
+ @root.each do |key, value|
393
+ if block.call key, value
394
+ temp << key
395
+ end
396
+ end
397
+ temp.each do |k|
398
+ @root.delete(k, 0)
399
+ end
400
+ self
401
+ else
402
+ Enumerator.new(@root)
403
+ end
404
+ end
405
+
406
+ def dup
407
+ if @default != DEFAULT then
408
+ rt = RadixTree.new(@default)
409
+ elsif @default_proc then
410
+ rt = RadixTree.new(@default_proc.to_proc)
411
+ else
412
+ rt = RadixTree.new
413
+ end
414
+ rt.root = @root.dup
415
+ rt
416
+ end
417
+ alias clone dup
418
+
419
+ def reject(&block)
420
+ if block_given?
421
+ self.dup.delete_if(&block)
422
+ else
423
+ Enumerator.new(@root)
424
+ end
425
+ end
426
+
427
+ def reject!(&block)
428
+ if block_given?
429
+ temp = []
430
+ @root.each do |key, value|
431
+ if block.call key, value
432
+ temp << key
433
+ end
434
+ end
435
+ if temp.empty?
436
+ nil
437
+ else
438
+ temp.each do |k|
439
+ @root.delete(k, 0)
440
+ end
441
+ self
442
+ end
443
+ else
444
+ Enumerator.new(@root)
445
+ end
446
+ end
447
+
448
+ def fetch(key, *args, &block)
449
+ if args.size > 0 && block
450
+ raise ArgumentError, 'wrong number of arguments'
451
+ elsif self[key]
452
+ self[key]
453
+ elsif args.size == 1
454
+ args[0]
455
+ elsif block
456
+ block.call key
457
+ else
458
+ raise KeyError, 'can\'t find the key'
459
+ end
460
+ end
461
+
462
+ def values_at(*args)
463
+ vs = []
464
+ args.each do |a|
465
+ vs << self[a]
466
+ end
467
+ vs
468
+ end
469
+
470
+ def replace(h)
471
+ self.clear
472
+ h.each do |k,v|
473
+ self[k] = v
474
+ end
475
+ end
476
+
477
+ def key(value)
478
+ self.each do |k,v|
479
+ return k if v == value
480
+ nil
481
+ end
482
+ end
483
+
484
+ def shift
485
+ self.each do |k,v|
486
+ self.delete(k)
487
+ return [k, v]
488
+ end
489
+ end
490
+
491
+ def has_value?(value)
492
+ self.each do |k,v|
493
+ return true if value == v
494
+ end
495
+ false
496
+ end
497
+ alias value? has_value?
498
+
499
+ def ==(oh)
500
+ return false if self.size != oh.size
501
+ self.each_key do |k|
502
+ return false if self[k] != oh[k]
503
+ end
504
+ true
505
+ end
506
+
507
+ def eql?(oh)
508
+ return false if self.size != oh.size
509
+ self.each_key do |k|
510
+ return false unless self[k].eql?(oh[k])
511
+ end
512
+ true
513
+ end
514
+
515
+ def find_predecessor(key)
516
+ @root.find_pre(key, 0, nil)
517
+ end
518
+
519
+ def find_successor(key)
520
+ @root.find_suc(key, 0, false)
521
+ end
522
+
523
+ def find_all(prefix)
524
+ @root.find_all(prefix, 0, false)
525
+ end
526
+
527
+ protected
528
+ attr_accessor :root
529
+
328
530
  end
@@ -2,6 +2,16 @@
2
2
  require File.expand_path('./helper', File.dirname(__FILE__))
3
3
 
4
4
  class TestRadixTree < Test::Unit::TestCase
5
+ def setup
6
+ # rt for RadixTree
7
+ # ip for input
8
+ @rt = RadixTree.new
9
+ @ip = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
10
+ @ip.each do |k, v|
11
+ @rt[k] = v
12
+ end
13
+ end
14
+
5
15
  def test_aref_nil
6
16
  h = RadixTree.new
7
17
  h['abc'] = 1
@@ -176,26 +186,18 @@ class TestRadixTree < Test::Unit::TestCase
176
186
  end
177
187
 
178
188
  def test_each
179
- h = RadixTree.new
180
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
181
- s.each do |k, v|
182
- h[k] = v
183
- end
189
+ h, s = @rt, @ip
184
190
  assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k }
185
191
  #
186
192
  values = []
187
193
  h.each do |k, v|
188
194
  values << [k, v]
189
195
  end
190
- assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k }
196
+ assert_equal h.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k }
191
197
  end
192
198
 
193
199
  def test_each_key
194
- h = RadixTree.new
195
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
196
- s.each do |k, v|
197
- h[k] = v
198
- end
200
+ h, s = @rt, @ip
199
201
  assert_equal s.keys.sort, h.each_key.sort
200
202
  #
201
203
  values = []
@@ -206,35 +208,23 @@ class TestRadixTree < Test::Unit::TestCase
206
208
  end
207
209
 
208
210
  def test_each_value
209
- h = RadixTree.new
210
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 }
211
- s.each do |k, v|
212
- h[k] = v
213
- end
211
+ h, s = @rt, @ip
214
212
  assert_equal s.values.sort, h.each_value.sort
215
213
  #
216
214
  values = []
217
215
  h.each_value do |v|
218
216
  values << v
219
217
  end
220
- assert_equal s.values.sort, values.sort
218
+ assert_equal h.values.sort, values.sort
221
219
  end
222
220
 
223
221
  def test_keys
224
- h = RadixTree.new
225
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
226
- s.each do |k, v|
227
- h[k] = v
228
- end
222
+ h, s = @rt, @ip
229
223
  assert_equal s.keys.sort, h.keys.sort
230
224
  end
231
225
 
232
226
  def test_values
233
- h = RadixTree.new
234
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
235
- s.each do |k, v|
236
- h[k] = v
237
- end
227
+ h, s = @rt, @ip
238
228
  assert_equal s.values.sort, h.values.sort
239
229
  end
240
230
 
@@ -272,24 +262,234 @@ class TestRadixTree < Test::Unit::TestCase
272
262
  end
273
263
 
274
264
  def test_to_hash
265
+ h, s = @rt, @ip
266
+ assert_equal s, h.to_hash
267
+ end
268
+
269
+ def test_clear
270
+ assert_equal @ip, @rt.to_hash
271
+ @rt.clear
272
+ assert_equal 0, @rt.size
273
+ assert @rt.to_hash.empty?
274
+ end
275
+
276
+ def test_delete_if
277
+ h, s = @rt, @ip
278
+ assert_equal 6, h.size
279
+ h.delete_if do |k,v|
280
+ v > 3
281
+ end
282
+ assert_equal 3, h.size
283
+ assert_equal 1, h['aa']
284
+ assert_equal 2, h['ab']
285
+ assert_equal 3, h['bb']
286
+ assert_equal nil, h['bc']
287
+ assert_equal nil, h['a']
288
+ assert_equal nil, h['abc']
289
+ end
290
+
291
+ def test_dup
275
292
  h = RadixTree.new
276
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
293
+ s = { 'aa' => 1, 'ab' => 2, 'bb' => 3 }
277
294
  s.each do |k, v|
278
295
  h[k] = v
279
296
  end
280
- assert_equal s, h.to_hash
297
+ assert_equal 3, h.size
298
+ assert_equal 1, h['aa']
299
+ assert_equal 2, h['ab']
300
+ assert_equal 3, h['bb']
301
+ h2 = h.dup
302
+ h2['aa'] = 4
303
+ h2['a'] = 5
304
+
305
+ assert_equal 3, h.size
306
+ assert_equal 1, h['aa']
307
+ assert_equal 2, h['ab']
308
+ assert_equal 3, h['bb']
309
+ assert_equal 4, h2.size
310
+ assert_equal 4, h2['aa']
311
+ assert_equal 2, h2['ab']
312
+ assert_equal 3, h2['bb']
313
+ assert_equal 5, h2['a']
281
314
  end
282
315
 
283
- def test_clear
316
+ def test_reject
317
+ h, s = @rt, @ip
318
+ h2 = h.reject do |k,v|
319
+ v > 3
320
+ end
321
+ assert_equal 6, h.size
322
+ assert_equal 1, h['aa']
323
+ assert_equal 2, h['ab']
324
+ assert_equal 3, h['bb']
325
+ assert_equal 4, h['bc']
326
+ assert_equal 5, h['a']
327
+ assert_equal 6, h['abc']
328
+
329
+ assert_equal 3, h2.size
330
+ assert_equal 1, h2['aa']
331
+ assert_equal 2, h2['ab']
332
+ assert_equal 3, h2['bb']
333
+ assert_equal nil, h2['bc']
334
+ assert_equal nil, h2['a']
335
+ assert_equal nil, h2['abc']
336
+ end
337
+
338
+ def test_reject!
339
+ h, s = @rt, @ip
340
+ h2 = h.reject! do |k,v|
341
+ v > 8
342
+ end
343
+ assert_equal 6, h.size
344
+ assert_equal 1, h['aa']
345
+ assert_equal 2, h['ab']
346
+ assert_equal 3, h['bb']
347
+ assert_equal 4, h['bc']
348
+ assert_equal 5, h['a']
349
+ assert_equal 6, h['abc']
350
+
351
+ assert_equal nil, h2
352
+ end
353
+
354
+ def fetch!
284
355
  h = RadixTree.new
285
- s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
356
+ s = { 'aa' => 1, 'ab' => 2 }
286
357
  s.each do |k, v|
287
358
  h[k] = v
288
359
  end
289
- assert_equal s, h.to_hash
290
- h.clear
291
- assert_equal 0, h.size
292
- assert h.to_hash.empty?
360
+ assert_equal 1, h.fetch('aa')
361
+ assert_equal 'df value', h.fetch('aac', 'df value')
362
+ assert_equal "aac:df value from block", h.fetch('aac') {|k| "#{k}:df value from block" }
363
+ end
364
+
365
+ def test_values_at
366
+ h, s = @rt, @ip
367
+ ks = s.keys.shuffle[0..2]
368
+ vs = h.values_at(*ks)
369
+ for i in (0..(ks.size)) do
370
+ assert_equal vs[i], h[ks[i]]
371
+ end
372
+ end
373
+
374
+ def test_replace
375
+ h = RadixTree.new
376
+ s = { 'aa' => 1, 'ab' => 2 }
377
+ s2 = { 'bz' => 3, 'kk' => 4 }
378
+ s.each do |k, v|
379
+ h[k] = v
380
+ end
381
+ assert_equal 1, h['aa']
382
+ assert_equal 2, h['ab']
383
+ h.replace(s2)
384
+ assert_equal 3, h['bz']
385
+ assert_equal 4, h['kk']
386
+ end
387
+
388
+ def test_key
389
+ h, s = @rt, @ip
390
+ assert_equal 'aa', h.key(1)
391
+ assert_equal 'bb', h.key(3)
392
+ assert_equal 'bc', h.key(4)
393
+ end
394
+
395
+ def test_shift
396
+ h, s = @rt, @ip
397
+ k, v = h.shift
398
+ assert_equal 'a', k
399
+ assert_equal 5, v
400
+ assert_equal 1, h['aa']
401
+ assert_equal 2, h['ab']
402
+ assert_equal 3, h['bb']
403
+ assert_equal 4, h['bc']
404
+ assert_equal 6, h['abc']
405
+ end
406
+
407
+ def test_has_value?
408
+ h, s = @rt, @ip
409
+ assert_equal true, h.has_value?(3)
410
+ assert_equal true, h.has_value?(4)
411
+ assert_equal true, h.value?(5)
412
+ assert_equal true, h.value?(6)
413
+ assert_equal false, h.has_value?(7)
414
+ end
415
+
416
+ def test_=
417
+ h, s = @rt, @ip
418
+ h2 = RadixTree.new
419
+ s.each do |k, v|
420
+ h2[k] = v
421
+ end
422
+ assert_equal true, (h==h2)
423
+ tk, tv= h2.shift
424
+ assert_equal false, (h==h2)
425
+ h2[tk] = tv+3
426
+ assert_equal false, (h==h2)
427
+ end
428
+
429
+ def test_eql?
430
+ h, s = @rt, @ip
431
+ h2 = RadixTree.new
432
+ s.each do |k, v|
433
+ h2[k] = v
434
+ end
435
+ assert_equal true, (h.eql?(h2))
436
+ tk, tv= h2.shift
437
+ assert_equal false, (h.eql?(h2))
438
+ h2[tk] = tv.to_s
439
+ assert_equal false, (h.eql?(h2))
440
+ end
441
+
442
+ def test_find_predecessor
443
+ h, s = @rt, @ip
444
+ assert_equal nil, h.find_predecessor('a')
445
+ assert_equal nil, h.find_predecessor('b')
446
+ assert_equal 'a', h.find_predecessor('aa')
447
+ assert_equal 'a', h.find_predecessor('ab')
448
+ assert_equal 'ab', h.find_predecessor('abc')
449
+ assert_equal 'b', h.find_predecessor('bb')
450
+ assert_equal 'b', h.find_predecessor('bc')
451
+ end
452
+
453
+ def test_find_successor
454
+ h, s = @rt, @ip
455
+ h['cde'] = 7
456
+ h['cdf'] = 8
457
+ h['cf'] = 9
458
+ h['c'] = 10
459
+ assert_equal 'aa', h.find_successor('a')
460
+ assert_equal nil, h.find_successor('aa')
461
+ assert_equal 'abc', h.find_successor('ab')
462
+ assert_equal nil, h.find_successor('abc')
463
+ assert_equal 'bb', h.find_successor('b')
464
+ assert_equal nil, h.find_successor('bb')
465
+ assert_equal nil, h.find_successor('bc')
466
+ assert_equal 'cde', h.find_successor('c')
467
+ assert_equal 'cde', h.find_successor('cd')
468
+ assert_equal nil, h.find_successor('cde')
469
+ assert_equal nil, h.find_successor('cdf')
470
+ assert_equal nil, h.find_successor('cf')
471
+ end
472
+
473
+ def test_find_all
474
+ h, s = @rt, @ip
475
+ h['cde'] = 7
476
+ h['cdf'] = 8
477
+ h['cf'] = 9
478
+ h['c'] = 10
479
+ assert_equal ['ab', 'abc'].sort, h.find_all('ab').sort
480
+ assert_equal ['a', 'aa', 'ab', 'abc'].sort, h.find_all('a').sort
481
+ assert_equal ['aa'], h.find_all('aa')
482
+ assert_equal ['abc'], h.find_all('abc')
483
+
484
+ assert_equal ['bb', 'bc'].sort, h.find_all('b').sort
485
+ assert_equal ['bb'], h.find_all('bb')
486
+ assert_equal ['bc'], h.find_all('bc')
487
+
488
+ assert_equal ['c', 'cde', 'cdf', 'cf'].sort, h.find_all('c').sort
489
+ assert_equal ['cde', 'cdf'].sort, h.find_all('cd').sort
490
+ assert_equal ['cde'], h.find_all('cde')
491
+ assert_equal ['cdf'], h.find_all('cdf')
492
+ assert_equal ['cf'], h.find_all('cf')
293
493
  end
294
494
 
295
495
  if RUBY_VERSION >= '1.9.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radix_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-09 00:00:00.000000000 Z
12
+ date: 2012-10-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: nahi@ruby-lang.org
@@ -33,22 +33,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
33
33
  - - ! '>='
34
34
  - !ruby/object:Gem::Version
35
35
  version: '0'
36
- segments:
37
- - 0
38
- hash: 2336052632168712106
39
36
  required_rubygems_version: !ruby/object:Gem::Requirement
40
37
  none: false
41
38
  requirements:
42
39
  - - ! '>='
43
40
  - !ruby/object:Gem::Version
44
41
  version: '0'
45
- segments:
46
- - 0
47
- hash: 2336052632168712106
48
42
  requirements: []
49
43
  rubyforge_project:
50
- rubygems_version: 1.8.11
44
+ rubygems_version: 1.8.23
51
45
  signing_key:
52
46
  specification_version: 3
53
47
  summary: Naive implementation of Radix Tree for Ruby
54
48
  test_files: []
49
+ has_rdoc: