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.
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 +437 -0
  6. data/setup.rb +1551 -0
  7. data/test/tests.rb +275 -0
  8. data/trie.gemspec +14 -0
  9. 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