trie 0.0.1

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