salgo 1.0.1

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 (3) hide show
  1. data/lib/salgo/btree.rb +529 -0
  2. data/tests/btree_test.rb +238 -0
  3. metadata +86 -0
@@ -0,0 +1,529 @@
1
+ #
2
+ # The MIT License
3
+ #
4
+ # Copyright (c) 2010 Samuel R. Baskinger
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+ #
24
+
25
+ module Salgo
26
+
27
+ class Btree
28
+
29
+ class Node
30
+ attr_reader :keys, :nodes
31
+ attr_writer :keys, :nodes
32
+
33
+ def initialize()
34
+ @keys = []
35
+ @nodes = []
36
+ end
37
+
38
+ # Assume we have enough space to insert in the node.
39
+ # If two sub-trees are specified, it is assumed that they are replacing the subtree
40
+ # that the new_key was in. That sub-tree is replaced with the left_node
41
+ # and the right_node is inserted after the left_node's index.
42
+ def insert(new_key, left_node=nil, right_node=nil)
43
+
44
+ # Caution code.
45
+ throw Exception.new(
46
+ "Both right and left nodes must be nil or defined. One is not: #{right_node} #{left_node}") if (
47
+ right_node.nil? ^ left_node.nil? )
48
+
49
+ insertion_point = 0
50
+
51
+ catch(:foundI) do
52
+ @keys.each_with_index do |node_key, index|
53
+ if ( new_key < node_key )
54
+ insertion_point = index
55
+ throw :foundI
56
+ end
57
+ end
58
+
59
+ insertion_point = @keys.size
60
+ end
61
+
62
+ @keys.insert(insertion_point, new_key)
63
+ @nodes[insertion_point] = left_node if left_node
64
+ @nodes.insert(insertion_point+1, right_node) if right_node
65
+
66
+ end
67
+
68
+ # This node will split itself and return a list of 3 items, [key, left_node, right_node ].
69
+ def split()
70
+
71
+ node_partition = @nodes.size / 2
72
+ key_partition = @keys.size / 2
73
+
74
+ left_node = Node.new()
75
+ right_node = Node.new()
76
+
77
+ left_node.nodes = @nodes[0...node_partition]
78
+ right_node.nodes = @nodes[node_partition..-1]
79
+
80
+ left_node.keys = @keys[0...key_partition]
81
+ right_node.keys = @keys[key_partition+1..-1]
82
+
83
+ [ @keys[key_partition], left_node, right_node ]
84
+ end
85
+
86
+ def leaf?()
87
+ @nodes.size == 0
88
+ end
89
+
90
+ # Similar to find_node_containing_key, but
91
+ # considers keys as they node is iterated through.
92
+ # An array of Salgo::Btree::Node or Salgo::Btree::Key object will be returned with the second element
93
+ # set to the index of the node.
94
+ #
95
+ # If it is a key, then the key holds a match. If a node, then the node
96
+ # subtree that should be expanded and searched.
97
+ def find_node_or_key_containing_key(key)
98
+
99
+ @keys.each_with_index do |node_key, i|
100
+ if ( key == node_key )
101
+ return [ node_key, i ]
102
+ elsif ( key < node_key )
103
+ return [ @nodes[i], i ]
104
+ end
105
+ end
106
+
107
+ return [ @nodes[-1], @nodes.size - 1 ]
108
+ end
109
+
110
+ def find_node_containing_key(key)
111
+ @keys.each_with_index do |node_key, i|
112
+ if ( key < node_key )
113
+ return @nodes[i]
114
+ end
115
+ end
116
+
117
+ # If no throw, we assign the last node because key is bigger (or equal to) all our keys.
118
+ return @nodes[-1]
119
+ end
120
+
121
+ # Return an array of the min-key and min-node from this node.
122
+ # There may or may not be a min-node.
123
+ def take_min()
124
+ [ @keys.shift, @nodes.shift ]
125
+ end
126
+
127
+ def take(index)
128
+ [ @keys.delete_at(index), @nodes.delete_at(index) ]
129
+ end
130
+
131
+ # Return an array of the max-key and the max-node from this node.
132
+ # There may or may not be a max-node.
133
+ def take_max()
134
+ [ @keys.pop, @nodes.pop ]
135
+ end
136
+
137
+ def put_max(key, node)
138
+ @keys.push(key)
139
+ @nodes.push(node) if node
140
+ end
141
+
142
+ def put(index, key, node)
143
+ @keys.insert(index, key)
144
+ @nodes.insert(index, node) if node
145
+ end
146
+
147
+ def put_min(key, node)
148
+ @keys.unshift(key)
149
+ @nodes.unshift(node) if node
150
+ end
151
+
152
+ def last_node?(node)
153
+ @nodes[-1].equal?(node)
154
+ end
155
+
156
+ def first_node?(node)
157
+ @nodes[0].equal?(node)
158
+ end
159
+
160
+ end
161
+
162
+ # The key value should support >, < and ==.
163
+ class Key
164
+ attr_reader :val, :key
165
+ attr_writer :val, :key
166
+
167
+ def initialize(key, val=true)
168
+ @key = key
169
+ @val = val
170
+ end
171
+
172
+ def < (k)
173
+ @key < k.key
174
+ end
175
+
176
+ def > (k)
177
+ @key > k.key
178
+ end
179
+
180
+ def == (k)
181
+ @key == k.key
182
+ end
183
+
184
+ end
185
+
186
+ attr_reader :size
187
+
188
+ def initialize(minnodes=2)
189
+ @minnodes = ( minnodes < 2 )? 2 : minnodes
190
+ @maxnodes = 2 * minnodes
191
+ @root = Node.new()
192
+ @size = 0
193
+ end
194
+
195
+ def root?(node)
196
+ @root.equal? node
197
+ end
198
+
199
+ def full?(node)
200
+ node.keys.size == @maxnodes-1
201
+ end
202
+
203
+ # Does the given node have enough keys (and perhaps nodes) to merge with another node?
204
+ def mergable?(node)
205
+ node.keys.size < @minnodes
206
+ end
207
+
208
+ def has_minimum_keys?(node)
209
+ node.keys.size < @minnodes
210
+ end
211
+
212
+ # Is there an extra key to take, should we need it.
213
+ def has_extra_keys?(node)
214
+ node.keys.size >= @minnodes
215
+ end
216
+
217
+ def split_root
218
+ node = Node.new()
219
+
220
+ node.insert(*@root.split())
221
+
222
+ @root = node
223
+ end
224
+
225
+ # Insert a new value at the given key. Duplicate values are allowed in this data structure
226
+ # and insert does not prevent them. The []= method will replace values.
227
+ def insert(key, val)
228
+ key = Key.new(key, val)
229
+
230
+ # Always make sure our special friend "root" is OK and has room for an insert.
231
+ split_root if full?(@root)
232
+
233
+ parent_node = nil
234
+ node = @root
235
+
236
+ not_inserted = true
237
+
238
+ while(not_inserted)
239
+ if ( full? node )
240
+
241
+ median_key, lnode, rnode = node.split()
242
+
243
+ # NOTE: Because we always split full root nodes, we will never enter here with parent_node=nil
244
+ # Oh good, we can do a normal split and insert the result in the parent.
245
+ parent_node.insert(median_key, lnode, rnode)
246
+
247
+ if ( key < median_key )
248
+ node = lnode
249
+ else
250
+ node = rnode
251
+ end
252
+ end
253
+
254
+ # Can we insert?
255
+ if ( node.leaf? )
256
+ node.insert(key)
257
+ @size += 1
258
+ not_inserted = false
259
+ else
260
+ # which node to examine?
261
+ parent_node = node
262
+ node = node.find_node_containing_key(key)
263
+ end
264
+ end
265
+ end
266
+
267
+ def find_key(key)
268
+
269
+ node = @root
270
+
271
+ candidate, candidate_idx = node.find_node_or_key_containing_key(key)
272
+
273
+ while( ! candidate.nil?)
274
+ if ( candidate.is_a?(Key) )
275
+
276
+ return candidate
277
+ end
278
+
279
+ node = candidate
280
+ candidate, candidate_idx = node.find_node_or_key_containing_key(key)
281
+ end
282
+
283
+ return nil
284
+ end
285
+
286
+ def find(key)
287
+ k = find_key(Key.new(key, nil))
288
+
289
+ (k.nil?) ? nil : k.val
290
+ end
291
+
292
+ # Given the parent node and the child_index of a child node,
293
+ # this method will merge with the sibling to the left of the
294
+ # child. The resulting "unknown" tree will be placed on
295
+ # the left and the known-filled node will be placed in the child
296
+ # node's current spot.
297
+ # The new target node is returned.
298
+ def merge_with_left(parent, child_index)
299
+
300
+ child = parent.nodes[child_index]
301
+ sibling = parent.nodes[child_index-1]
302
+ node = Node.new()
303
+
304
+ node.nodes = sibling.nodes + child.nodes
305
+
306
+ node.keys = sibling.keys + [ parent.keys[child_index-1] ] + child.keys
307
+ parent.take(child_index-1)
308
+ parent.nodes[child_index-1] = node
309
+ node
310
+ end
311
+
312
+ # Same as merge_with_right, but the roles are reversed as
313
+ # are the locations of the resulting subtrees.
314
+ # The new target node is returned.
315
+ def merge_with_right(parent, child_index)
316
+ child = parent.nodes[child_index]
317
+ sibling = parent.nodes[child_index+1]
318
+ node = Node.new()
319
+
320
+ node.nodes = child.nodes + sibling.nodes
321
+
322
+ node.keys = child.keys + [ parent.keys[child_index] ] + sibling.keys
323
+ parent.take(child_index)
324
+ parent.nodes[child_index] = node
325
+ node
326
+ end
327
+
328
+ # Delete from a subtree. We assume the node can withstand delete when called.
329
+ # They key object is returned.
330
+ def delete_max_key(node=@root)
331
+
332
+ return nil if @size == 0
333
+
334
+ if root?(node) and @root.keys.size == 0 and @root.nodes.size == 1
335
+ @root = node = @root.nodes[0]
336
+ end
337
+
338
+ while(true) do
339
+ if ( node.leaf? )
340
+ @size -= 1
341
+ return node.take_max()[0]
342
+ else
343
+
344
+ # Fix up the node before deleting from it.
345
+ if has_minimum_keys?(node.nodes[-1])
346
+ if has_minimum_keys?(node.nodes[-2])
347
+
348
+ node = merge_with_left(node, node.nodes.size-1)
349
+
350
+ else
351
+
352
+ # Pull the max key and node from our "left" sibling.
353
+ # Make the left key be our parent and put the node
354
+ # in the minimum of the right tree node.
355
+ another_key, another_node = node.nodes[-2].take_max
356
+
357
+ node.nodes[-1].put_min(node.keys[-1], another_node)
358
+ node.keys[-1] = another_key
359
+
360
+ node = node.nodes[-1]
361
+
362
+ end
363
+ else
364
+ node = node.nodes[-1]
365
+ end
366
+ end
367
+ end
368
+ end
369
+
370
+ # Delete from a subtree. We assume the node can withstand delete when called.
371
+ # The key object is returned.
372
+ def delete_min_key(node=@root)
373
+
374
+ return nil if @size == 0
375
+
376
+ if root?(node) and @root.keys.size == 0 and @root.nodes.size == 1
377
+ @root = node = @root.nodes[0]
378
+ end
379
+
380
+ while(true) do
381
+ if ( node.leaf? )
382
+ @size -= 1
383
+ return node.take_min()[0]
384
+ else
385
+ # Fix up the node before deleting from it.
386
+ if has_minimum_keys?(node.nodes[0])
387
+ if has_minimum_keys?(node.nodes[1])
388
+ node = merge_with_right(node, 0)
389
+
390
+ else
391
+
392
+ # Pull the min key and node from our "right" sibling.
393
+ # Make the right key be our parent and put the node
394
+ # in the minimum of the right tree node.
395
+ another_key, another_node = node.nodes[1].take_min
396
+
397
+ node.nodes[0].put_max(node.keys[0], another_node)
398
+ node.keys[0] = another_key
399
+
400
+ node = node.nodes[0]
401
+ end
402
+ else
403
+ node = node.nodes[0]
404
+ end
405
+
406
+ end
407
+ end
408
+ end
409
+
410
+ def delete_key(key, node=@root)
411
+
412
+ candidate, candidate_idx = node.find_node_or_key_containing_key(key)
413
+
414
+ return nil if candidate.nil?
415
+
416
+ # Delete from this node.
417
+ if ( candidate.is_a?(Key) )
418
+
419
+ # If it's a simple delete...
420
+ if node.leaf?
421
+ node.keys.delete_at(candidate_idx)
422
+ @size -= 1
423
+ return candidate
424
+ elsif has_extra_keys?(node.nodes[candidate_idx])
425
+ node.keys[candidate_idx] = delete_max_key(node.nodes[candidate_idx])
426
+ return candidate
427
+ elsif has_extra_keys?(node.nodes[candidate_idx+1])
428
+ node.keys[candidate_idx] = delete_min_key(node.nodes[candidate_idx+1])
429
+ return candidate
430
+ else
431
+ node = merge_with_right(node, candidate_idx)
432
+
433
+ # The merge_with_right call left the root with no keys and 1 child node.
434
+ # Replace the root and delete from the root.
435
+ @root = @root.nodes[0] if ( @root.nodes.size == 1 )
436
+
437
+ return delete_key(key, node)
438
+ end
439
+
440
+ elsif candidate.is_a?(Node)
441
+
442
+ # Ensure that the node can sustain a delete BEFORE entering it...
443
+ unless has_extra_keys?(candidate)
444
+
445
+ if ( node.first_node?(candidate) )
446
+ if ( has_extra_keys?(node.nodes[1]))
447
+ another_key, another_node = node.nodes[1].take_min
448
+ candidate.put_max(node.keys[0], another_node)
449
+ node.keys[0] = another_key
450
+ else
451
+ merge_with_right(node, candidate_idx)
452
+ end
453
+ #elsif ( node.last_node?(candidate) )
454
+ else
455
+ if ( has_extra_keys?(node.nodes[candidate_idx-1]) )
456
+ another_key, another_node = node.nodes[candidate_idx-1].take_max
457
+ candidate.put_min(node.keys[candidate_idx-1], another_node)
458
+ node.keys[candidate_idx-1] = another_key
459
+ else
460
+ merge_with_left(node, candidate_idx)
461
+ end
462
+ end
463
+ end
464
+
465
+ # If one of the above merges removed all keys from the root, then there is only 1 node.
466
+ # Promote that node as the root.
467
+ candidate = @root = @root.nodes[0] if ( @root.nodes.size == 1 )
468
+
469
+ delete_key(key, candidate)
470
+ end
471
+ end
472
+
473
+ def delete(key)
474
+
475
+ key = delete_key(Key.new(key))
476
+
477
+ (key.nil?)? nil : key.val
478
+ end
479
+
480
+ alias [] find
481
+
482
+ # Set the key in this tree to the given value.
483
+ # If there is already a value at the given key, it is replaced and the old value is returned.
484
+ # Nil is returned otherwise.
485
+ def []=(key, val)
486
+
487
+ k = find_key(Key.new(key))
488
+
489
+ if ( k.nil? )
490
+ insert(key, val)
491
+ nil
492
+ else
493
+ v = k.val
494
+ k.val = val
495
+ v
496
+ end
497
+ end
498
+
499
+ def each(node=@root, &call)
500
+
501
+ proc_child = ( node.leaf?() )? lambda { |x| } : lambda { |child_node| each(child_node, &call) }
502
+
503
+ index = 0
504
+
505
+
506
+ node.keys.each do |key|
507
+ proc_child.call(node.nodes[index])
508
+
509
+ call.call(key.key, key.val)
510
+
511
+ index+=1
512
+
513
+ end
514
+
515
+ proc_child.call(node.nodes[index])
516
+ end
517
+
518
+ def has_key?(key)
519
+ ! find_key(Key.new(key)).nil?
520
+ end
521
+
522
+ alias member? has_key?
523
+ alias include? has_key?
524
+ alias key? has_key?
525
+
526
+ alias store []=
527
+ end
528
+ end
529
+
@@ -0,0 +1,238 @@
1
+ require 'test/unit'
2
+
3
+ require 'salgo/btree'
4
+
5
+ require 'pp'
6
+
7
+ class BTreeTest < Test::Unit::TestCase
8
+ include Salgo
9
+
10
+ def test_nodeinsert()
11
+ assert(true, "OK")
12
+
13
+ n = Btree::Node.new()
14
+
15
+ begin
16
+ n.insert(1, 2)
17
+ assert(false, "Failed to report undefined right or left subtree.")
18
+ rescue Exception => e
19
+ assert(true, "Caught expected exception.")
20
+ end
21
+
22
+ n.insert(1, 2, 3)
23
+ assert(n.nodes[0] == 2)
24
+ assert(n.nodes[1] == 3)
25
+ assert(! n.nodes[0].nil?)
26
+ end
27
+
28
+ def test_nodesplit()
29
+ n = Btree::Node.new()
30
+ n.nodes = [1,1,3,3]
31
+ n.keys = [1,2,3]
32
+ key, lnode, rnode = n.split()
33
+ assert(key == 2, "Key was #{key}")
34
+ assert(lnode.keys==[1])
35
+ assert(rnode.keys==[3])
36
+ assert(lnode.nodes==[1,1])
37
+ assert(rnode.nodes==[3,3])
38
+
39
+ n2 = Btree::Node.new()
40
+
41
+ n2.insert(*n.split())
42
+
43
+ assert(n2.keys[0]==2)
44
+ assert(n2.nodes[0].keys==[1])
45
+ assert(n2.nodes[1].keys==[3])
46
+ assert(n2.nodes[0].nodes==[1,1])
47
+ assert(n2.nodes[1].nodes==[3,3])
48
+ end
49
+
50
+ def test_find_node_containing_key()
51
+ n = Btree::Node.new()
52
+
53
+ n.keys = [Btree::Key.new(1,1), Btree::Key.new(2,2), Btree::Key.new(3,3)]
54
+ n.nodes = [ Btree::Node.new(), Btree::Node.new(), Btree::Node.new(), Btree::Node.new() ]
55
+
56
+ assert(n.find_node_containing_key(Btree::Key.new(0)).equal? n.nodes[0])
57
+ assert(n.find_node_containing_key(Btree::Key.new(1)).equal? n.nodes[1])
58
+ assert(n.find_node_containing_key(Btree::Key.new(2)).equal? n.nodes[2])
59
+ assert(n.find_node_containing_key(Btree::Key.new(3)).equal? n.nodes[3])
60
+ end
61
+
62
+ def test_split()
63
+ bt = Btree.new()
64
+ bt.insert(1, 1)
65
+ bt.insert(2, 2)
66
+ bt.insert(3, 3)
67
+ rt = bt.instance_variable_get("@root")
68
+
69
+ key, lnode, rnode = rt.split()
70
+
71
+ assert(key.val == 2)
72
+ assert(lnode.keys[0].val == 1)
73
+ assert(rnode.keys[0].val == 3)
74
+ end
75
+
76
+ def test_insert()
77
+ bt = Btree.new()
78
+ bt.insert(1, 'a')
79
+ bt.insert(2, 'b')
80
+ bt.insert(3, 'c')
81
+ bt.insert(4, 'd')
82
+ bt.insert(5, 'e')
83
+ bt.insert(6, 'f')
84
+
85
+ assert(bt.size == 6, "Size was not 5 but #{bt.size}")
86
+
87
+ rt = bt.instance_variable_get("@root")
88
+
89
+ assert(rt.nodes[0].keys[0].key == 1)
90
+ assert(rt.keys[0].key == 2)
91
+ assert(rt.nodes[1].keys[0].key == 3)
92
+ assert(rt.keys[1].key == 4)
93
+ assert(rt.nodes[2].keys[0].key == 5)
94
+ assert(rt.nodes[2].keys[1].key == 6)
95
+
96
+ assert(bt.find(1) == 'a')
97
+ assert(bt.find(2) == 'b')
98
+ assert(bt.find(3) == 'c')
99
+ assert(bt.find(4) == 'd')
100
+ assert(bt.find(5) == 'e')
101
+ assert(bt.find(6) == 'f')
102
+ end
103
+
104
+ def test_delete_max_key()
105
+ bt = Btree.new()
106
+ bt.insert(1, 'a')
107
+ bt.insert(2, 'b')
108
+ bt.insert(3, 'c')
109
+ bt.insert(4, 'd')
110
+ bt.insert(5, 'e')
111
+ bt.insert(6, 'f')
112
+
113
+ assert(bt.size == 6, "Size was not 5 but #{bt.size}")
114
+
115
+ assert(bt.delete_max_key().val == 'f')
116
+ assert(bt.delete_max_key().val == 'e')
117
+ assert(bt.delete_max_key().val == 'd')
118
+ assert(bt.delete_max_key().val == 'c')
119
+ assert(bt.delete_max_key().val == 'b')
120
+ assert(bt.delete_max_key().val == 'a')
121
+ assert(bt.size == 0)
122
+ assert(bt.delete_max_key() == nil)
123
+ assert(bt.size == 0)
124
+
125
+ rt = bt.instance_variable_get("@root")
126
+
127
+ end
128
+
129
+ def test_delete_min_key()
130
+ bt = Btree.new()
131
+ bt.insert(1, 'a')
132
+ bt.insert(2, 'b')
133
+ bt.insert(3, 'c')
134
+ bt.insert(4, 'd')
135
+ bt.insert(5, 'e')
136
+ bt.insert(6, 'f')
137
+
138
+ assert(bt.size == 6, "Size was not 5 but #{bt.size}")
139
+ assert(bt.delete_min_key().val == 'a')
140
+ assert(bt.delete_min_key().val == 'b')
141
+ assert(bt.delete_min_key().val == 'c')
142
+ assert(bt.delete_min_key().val == 'd')
143
+ assert(bt.delete_min_key().val == 'e')
144
+ assert(bt.delete_min_key().val == 'f')
145
+ assert(bt.size == 0)
146
+ assert(bt.delete_min_key() == nil)
147
+ assert(bt.size == 0)
148
+
149
+ rt = bt.instance_variable_get("@root")
150
+
151
+ end
152
+
153
+ def test_delete_from_root()
154
+ bt = Btree.new()
155
+ bt.insert(1, 'a')
156
+ bt.insert(2, 'b')
157
+ bt.insert(3, 'c')
158
+ bt.insert(4, 'd')
159
+ bt.insert(5, 'e')
160
+ bt.insert(6, 'f')
161
+
162
+ assert(bt.delete(2)=='b')
163
+ assert(bt.delete(3)=='c')
164
+ assert(bt.delete(1)=='a')
165
+ assert(bt.delete(4)=='d')
166
+
167
+ rt = bt.instance_variable_get("@root")
168
+ assert(bt.size == 2)
169
+ assert(rt.nodes.size == 0)
170
+ assert(rt.keys.size == 2)
171
+ end
172
+
173
+ def test_list_add_remove()
174
+ bt = Btree.new()
175
+
176
+ added_items = [975, 801, 916, 648, 259, 103, 212, 230, 336, 371]
177
+
178
+ added_items.each do |r|
179
+ bt.insert(r, r)
180
+ end
181
+
182
+ assert(bt.size == added_items.size)
183
+
184
+ added_items.each do |k|
185
+ prev_size = bt.size
186
+ assert(bt.delete(k) == k)
187
+ assert(bt.size == prev_size -1)
188
+ end
189
+ end
190
+
191
+ def test_random_add_remove()
192
+ bt = Btree.new()
193
+
194
+ added_items = []
195
+
196
+ sz = 10
197
+ sz.times do
198
+ r = (rand * 1000).to_i
199
+ added_items << r
200
+ bt.insert(r, r)
201
+ end
202
+
203
+ assert(bt.size == sz)
204
+
205
+ added_items.each do |k|
206
+ prev_size = bt.size
207
+ assert(bt.delete(k) == k)
208
+ assert(bt.size == prev_size -1)
209
+ end
210
+ end
211
+
212
+ def test_adds()
213
+ bt = Btree.new()
214
+
215
+ bt[1] = 1
216
+ bt[1] = 'a'
217
+
218
+ assert(bt[1] == 'a')
219
+ end
220
+
221
+ def test_each()
222
+
223
+ i = 0
224
+
225
+ bt = Btree.new()
226
+ bt.insert(1, 'a')
227
+ bt.insert(2, 'b')
228
+ bt.insert(3, 'c')
229
+ bt.insert(4, 'd')
230
+ bt.insert(5, 'e')
231
+ bt.insert(6, 'f')
232
+
233
+ bt.each { |k,v| i+=1 }
234
+
235
+ assert(i == bt.size)
236
+
237
+ end
238
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salgo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Sam Baskinger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: log4r
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 29
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 5
34
+ version: 1.0.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: A collection of algorithms and datastructure for ruby written in ruby.
38
+ email: basking2@rubyforge.org.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - lib/salgo/btree.rb
47
+ - tests/btree_test.rb
48
+ has_rdoc: true
49
+ homepage: http://salgo.rubyforge.org
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 31
63
+ segments:
64
+ - 1
65
+ - 6
66
+ - 8
67
+ version: 1.6.8
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project: http://salgo.rubyforge.org/
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: simply algorithms
84
+ test_files:
85
+ - tests/btree_test.rb
86
+ - tests/btree_test.rb