codders-trie 0.0.3
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/COPYING +340 -0
- data/INSTALL +232 -0
- data/MANIFEST +8 -0
- data/README +6 -0
- data/lib/trie.rb +437 -0
- data/setup.rb +1551 -0
- data/test/tests.rb +275 -0
- data/trie.gemspec +14 -0
- metadata +54 -0
data/test/tests.rb
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
#
|
3
|
+
# = Name
|
4
|
+
# TestTrie
|
5
|
+
#
|
6
|
+
# == Description
|
7
|
+
# This file contains unit tests for the Trie class.
|
8
|
+
#
|
9
|
+
# == Author
|
10
|
+
# Daniel Erat <dan-ruby@erat.org>
|
11
|
+
#
|
12
|
+
# == Copyright
|
13
|
+
# Copyright 2005 Daniel Erat
|
14
|
+
#
|
15
|
+
# == License
|
16
|
+
# GNU GPL; see COPYING
|
17
|
+
|
18
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
19
|
+
|
20
|
+
require 'test/unit'
|
21
|
+
require 'trie'
|
22
|
+
|
23
|
+
# Unit tests for the Trie class.
|
24
|
+
class TestTrie < Test::Unit::TestCase
|
25
|
+
# Test a compressed key with a single value at the root.
|
26
|
+
def test_find_compressed_key_single_value_at_root
|
27
|
+
t = Trie.new.insert('abc', 1)
|
28
|
+
assert_equal([1], t.find('abc').values)
|
29
|
+
assert_equal([1], t.find('abc').find('').values)
|
30
|
+
assert_equal([], t.find('a').values)
|
31
|
+
assert_equal([], t.find('').values)
|
32
|
+
assert_equal([], t.find('b').values)
|
33
|
+
assert_equal([1], t.find_prefix('abc').values)
|
34
|
+
assert_equal([1], t.find_prefix('abc').find_prefix('').values)
|
35
|
+
assert_equal([1], t.find_prefix('ab').values)
|
36
|
+
assert_equal([1], t.find_prefix('a').values)
|
37
|
+
assert_equal([1], t.find_prefix('').values)
|
38
|
+
assert_equal([], t.find_prefix('b').values)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Test a compressed key with multiple values at the root.
|
42
|
+
def test_find_compressed_key_multiple_values_at_root
|
43
|
+
t = Trie.new.insert('ab', 1).insert('ab', 2).insert('ab', 3)
|
44
|
+
assert_equal([1, 2, 3], t.find('ab').values.sort)
|
45
|
+
assert_equal([], t.find('a').values)
|
46
|
+
assert_equal([], t.find('').values)
|
47
|
+
assert_equal([1, 2, 3], t.find_prefix('ab').values.sort)
|
48
|
+
assert_equal([1, 2, 3], t.find_prefix('a').values.sort)
|
49
|
+
assert_equal([1, 2, 3], t.find_prefix('').values.sort)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Test a more complex Trie that contains a few compressed keys.
|
53
|
+
def test_find_complex
|
54
|
+
t = Trie.new.insert('a', 1).insert('ab', 2).insert('abcdef', 3).
|
55
|
+
insert('b', 4).insert('bcd', 5).insert('b', 6).insert('bcd', 7)
|
56
|
+
assert_equal([1], t.find('a').values)
|
57
|
+
assert_equal([2], t.find('ab').values)
|
58
|
+
assert_equal([3], t.find('abcdef').values)
|
59
|
+
assert_equal([4, 6], t.find('b').values.sort)
|
60
|
+
assert_equal([5, 7], t.find('bcd').values.sort)
|
61
|
+
assert_equal([], t.find('bcde').values)
|
62
|
+
assert_equal([], t.find('').values)
|
63
|
+
assert_equal([1, 2, 3], t.find_prefix('a').values)
|
64
|
+
assert_equal([2, 3], t.find_prefix('ab').values)
|
65
|
+
assert_equal([3], t.find_prefix('abcdef').values)
|
66
|
+
assert_equal([4, 5, 6, 7], t.find_prefix('b').values.sort)
|
67
|
+
assert_equal([5, 7], t.find_prefix('bcd').values.sort)
|
68
|
+
assert_equal([], t.find_prefix('bcde').values)
|
69
|
+
assert_equal([1, 2, 3, 4, 5, 6, 7], t.find_prefix('').values.sort)
|
70
|
+
end
|
71
|
+
|
72
|
+
# We have a compressed key at the root and then do one-or
|
73
|
+
# two-characters-at-a-time searches against it.
|
74
|
+
def test_find_multiple_lookups_compressed_key
|
75
|
+
t = Trie.new.insert('alphabet', 1)
|
76
|
+
t2 = t.find_prefix('')
|
77
|
+
assert_equal([1], t2.values)
|
78
|
+
t2 = t2.find_prefix('al')
|
79
|
+
assert_equal([1], t2.values)
|
80
|
+
t2 = t2.find_prefix('p')
|
81
|
+
assert_equal([1], t2.values)
|
82
|
+
t2 = t2.find_prefix('ha')
|
83
|
+
assert_equal([1], t2.values)
|
84
|
+
t2 = t2.find_prefix('bet')
|
85
|
+
assert_equal([1], t2.values)
|
86
|
+
t2 = t2.find_prefix('')
|
87
|
+
assert_equal([1], t2.values)
|
88
|
+
t2 = t2.find_prefix('a')
|
89
|
+
assert_equal([], t2.values)
|
90
|
+
end
|
91
|
+
|
92
|
+
# We construct a trie with multiple values and then walk down it,
|
93
|
+
# searching for one or two characters at a time.
|
94
|
+
def test_find_multiple_lookups
|
95
|
+
t = Trie.new.insert('happy', 1).insert('hop', 2).insert('hey', 3).
|
96
|
+
insert('hello!', 4).insert('help', 5).insert('foo', 6)
|
97
|
+
assert_equal([6], t.find_prefix('fo').values)
|
98
|
+
t2 = t.find_prefix('h')
|
99
|
+
assert_equal([1, 2, 3, 4, 5], t2.values.sort)
|
100
|
+
t2 = t2.find_prefix('e')
|
101
|
+
assert_equal([3, 4, 5], t2.values.sort)
|
102
|
+
assert_equal([3], t2.find_prefix('y').values)
|
103
|
+
t2 = t2.find_prefix('l')
|
104
|
+
assert_equal([4, 5], t2.values.sort)
|
105
|
+
t2 = t2.find_prefix('lo')
|
106
|
+
assert_equal([4], t2.values)
|
107
|
+
t2 = t2.find_prefix('!')
|
108
|
+
assert_equal([4], t2.values)
|
109
|
+
t2 = t2.find_prefix('')
|
110
|
+
assert_equal([4], t2.values)
|
111
|
+
t2 = t2.find_prefix('!')
|
112
|
+
assert_equal([], t2.values)
|
113
|
+
end
|
114
|
+
|
115
|
+
# We construct a trie with multiple elements and test the size
|
116
|
+
# method.
|
117
|
+
def test_size
|
118
|
+
t = Trie.new.insert('ha', 1).insert('hat', 2).insert('hate', 3).
|
119
|
+
insert('hated', 4).insert('test', 5)
|
120
|
+
assert_equal(5, t.size)
|
121
|
+
assert_equal(4, t.find_prefix('ha').size)
|
122
|
+
assert_equal(2, t.find_prefix('hate').size)
|
123
|
+
assert_equal(1, t.find_prefix('test').size)
|
124
|
+
assert_equal(0, t.find_prefix('testing').size)
|
125
|
+
end
|
126
|
+
|
127
|
+
# We build a trie and test the empty? method.
|
128
|
+
def test_empty
|
129
|
+
t = Trie.new.insert('foo', 1).insert('bar', 2).insert('food', 3)
|
130
|
+
assert_equal(false, t.empty?)
|
131
|
+
assert_equal(false, t.find('foo').empty?)
|
132
|
+
assert_equal(false, t.find_prefix('foo').empty?)
|
133
|
+
assert_equal(true, t.find('fool').empty?)
|
134
|
+
end
|
135
|
+
|
136
|
+
# We insert keys that are actually lists containing objects of varying
|
137
|
+
# classes.
|
138
|
+
def test_mixed_classes_in_keys
|
139
|
+
t = Trie.new.insert([0, 1, 2], 0).insert([0, 'a'], 1).insert([1000], 2).
|
140
|
+
insert([0, 'a'], 3).insert('blah', 4)
|
141
|
+
assert_equal([0, 1, 3], t.find_prefix([0]).values.sort)
|
142
|
+
assert_equal([0], t.find_prefix([0, 1]).values)
|
143
|
+
assert_equal([1, 3], t.find_prefix([0, 'a']).values.sort)
|
144
|
+
assert_equal([2], t.find_prefix([1000]).values)
|
145
|
+
assert_equal([], t.find([0]).values)
|
146
|
+
assert_equal([1, 3], t.find([0, 'a']).values.sort)
|
147
|
+
assert_equal([4], t.find('blah').values)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Test delete.
|
151
|
+
def test_delete
|
152
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('a', 3).
|
153
|
+
insert('ab', 4).insert('ab', 5).insert('abc', 6)
|
154
|
+
assert_equal([1, 2, 3, 4, 5, 6], t.values.sort)
|
155
|
+
t.delete('a')
|
156
|
+
assert_equal([4, 5, 6], t.values.sort)
|
157
|
+
t.delete('abc')
|
158
|
+
assert_equal([4, 5], t.values.sort)
|
159
|
+
t.delete('ab')
|
160
|
+
assert_equal([], t.values)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Test delete_pair.
|
164
|
+
def test_delete_pair
|
165
|
+
t = Trie.new.insert('apple', 1).insert('apples', 2)
|
166
|
+
assert_equal([1], t.find('apple').values)
|
167
|
+
assert_equal([1, 2], t.find_prefix('apple').values.sort)
|
168
|
+
t.delete_pair('apple', 1)
|
169
|
+
assert_equal([], t.find('apple').values)
|
170
|
+
assert_equal([2], t.find('apples').values)
|
171
|
+
assert_equal([2], t.find_prefix('apple').values)
|
172
|
+
t.delete_pair('apples', 1) # key/value pair isn't in trie
|
173
|
+
assert_equal([2], t.find('apples').values)
|
174
|
+
t.delete_pair('apples', 2)
|
175
|
+
assert_equal([], t.find('apples').values)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Test delete_value.
|
179
|
+
def test_delete_value
|
180
|
+
t = Trie.new.insert('a', 1).insert('ab', 1).insert('abc', 2).
|
181
|
+
insert('a', 2).insert('b', 1).insert('c', 1)
|
182
|
+
assert_equal(6, t.size)
|
183
|
+
t.delete_value(1)
|
184
|
+
assert_equal(2, t.size)
|
185
|
+
t.delete_value(2)
|
186
|
+
assert_equal(true, t.empty?)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Test delete_prefix.
|
190
|
+
def test_delete_prefix
|
191
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('a', 3).
|
192
|
+
insert('ab', 4).insert('ab', 5).insert('abc', 6)
|
193
|
+
assert_equal([1, 2, 3, 4, 5, 6], t.values.sort)
|
194
|
+
t.delete_prefix('ab')
|
195
|
+
assert_equal([1, 2, 3], t.values.sort)
|
196
|
+
t.delete_prefix('a')
|
197
|
+
assert_equal(true, t.empty?)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Test clear.
|
201
|
+
def test_clear
|
202
|
+
t = Trie.new.insert('a', 1).insert('ab', 2)
|
203
|
+
assert_equal(2, t.size)
|
204
|
+
t.clear
|
205
|
+
assert_equal(true, t.empty?)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Test each_key.
|
209
|
+
def test_each_key
|
210
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('b', 3).insert('ab', 4)
|
211
|
+
keys = []
|
212
|
+
t.each_key {|k| keys.push(k.join) }
|
213
|
+
assert_equal(['a', 'ab', 'b'], keys.sort)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Test each_value.
|
217
|
+
def test_each_value
|
218
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('b', 1)
|
219
|
+
values = []
|
220
|
+
t.each_value {|v| values.push(v) }
|
221
|
+
assert_equal([1, 1, 2], values.sort)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Test each.
|
225
|
+
def test_each
|
226
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('b', 3).insert('ab', 4)
|
227
|
+
pairs = []
|
228
|
+
t.each {|k, v| pairs.push([k.join, v]) }
|
229
|
+
assert_equal([['a', 1], ['a', 2], ['ab', 4], ['b', 3]], pairs.sort)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Test keys.
|
233
|
+
def test_keys
|
234
|
+
t = Trie.new.insert('a', 1).insert('a', 2).insert('abc', 3).insert('b', 4)
|
235
|
+
keys = t.keys.collect {|k| k.join }.sort
|
236
|
+
assert_equal(['a', 'abc', 'b'], keys)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Test the composition of the tries by using the num_nodes method.
|
240
|
+
def test_composition
|
241
|
+
t = Trie.new.insert('a', 1)
|
242
|
+
assert_equal(1, t.num_nodes) # a
|
243
|
+
t.insert('a', 2)
|
244
|
+
assert_equal(1, t.num_nodes) # a
|
245
|
+
t.insert('abc', 3)
|
246
|
+
assert_equal(3, t.num_nodes) # '' -> a -> bc
|
247
|
+
t.insert('ab', 4)
|
248
|
+
assert_equal(4, t.num_nodes) # '' -> a -> b -> c
|
249
|
+
t.insert('b', 5)
|
250
|
+
assert_equal(5, t.num_nodes) # '' -> (a -> b -> c | b)
|
251
|
+
t.insert('b', 6)
|
252
|
+
assert_equal(5, t.num_nodes) # '' -> (a -> b -> c | b)
|
253
|
+
t.insert('abcdef', 7)
|
254
|
+
assert_equal(6, t.num_nodes) # '' -> (a -> b -> c -> def | b)
|
255
|
+
t.insert('abcdeg', 8)
|
256
|
+
# '' -> (a -> b -> c -> d -> e -> (f | g) | b)
|
257
|
+
assert_equal(9, t.num_nodes)
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_marshalling
|
261
|
+
t = Trie.new.insert('a', 1).insert('a', 13).insert('at', 2).insert('b', 3)
|
262
|
+
t2 = Marshal.load(Marshal.dump(t))
|
263
|
+
assert_equal(t.size, t2.size)
|
264
|
+
%w(a at b x).each do |k|
|
265
|
+
assert_equal(t[k], t2[k])
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_to_a
|
270
|
+
t = Trie.new.insert('a', 1).insert('a', 13).insert('at', 2).insert('b', 3)
|
271
|
+
assert_equal([['a', 1], ['a', 13], ['at', 2], ['b', 3]],
|
272
|
+
t.to_a.map{|k,v| [k.join(''), v]})
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
data/trie.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
SPEC = Gem::Specification.new do |s|
|
2
|
+
s.name = "codders-trie"
|
3
|
+
s.version = "0.0.3"
|
4
|
+
s.author = "Daniel Erat"
|
5
|
+
s.email = "dan-ruby@erat.org"
|
6
|
+
s.homepage = "http://www.erat.org/ruby/"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = "Implemention of a trie data structure"
|
9
|
+
candidates = %w(COPYING INSTALL MANIFEST README setup.rb trie.gemspec lib/trie.rb test/tests.rb)
|
10
|
+
s.files = candidates.delete_if {|i| i =~ /CVS/ }
|
11
|
+
s.require_path = "lib"
|
12
|
+
s.test_file = "test/tests.rb"
|
13
|
+
s.has_rdoc = true
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codders-trie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Daniel Erat
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-22 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email: dan-ruby@erat.org
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- COPYING
|
21
|
+
- INSTALL
|
22
|
+
- MANIFEST
|
23
|
+
- README
|
24
|
+
- setup.rb
|
25
|
+
- trie.gemspec
|
26
|
+
- lib/trie.rb
|
27
|
+
- test/tests.rb
|
28
|
+
homepage: http://www.erat.org/ruby/
|
29
|
+
licenses: []
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.24
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: Implemention of a trie data structure
|
52
|
+
test_files:
|
53
|
+
- test/tests.rb
|
54
|
+
has_rdoc: true
|