hipe-simplebtree 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  coverage
2
+ .yardoc
2
3
  .DS_Store
3
4
  pkg
4
5
  doc
data/README.txt CHANGED
@@ -1,9 +1,36 @@
1
- This is just for trying to get my first gem to work.
1
+ This was initially just for trying to get my first gem to work. It then
2
+ became an attempt to make a pure-ruby version of Takuma Ozawa's "rbtree"
3
+ gem. (Although note that this is not an rbtree, just a simple btree.)
2
4
 
3
- If you have checked this out from git (the "sources",)
4
- try:
5
- $ thor default:gemspec
6
- $ thor default:build
7
- $ sudo thor default:install
5
+
6
+ TO PLAY WITH THIS AS A GEM TO DEVELOP WITH:
7
+
8
+ If you have checked this out from http://github.com/hipe/hipe-simplebtree
9
+ (the "sources") from with the project folder (of this uncompiled gem,)
10
+ $ thor default:gemspec # makes the gemspec file from the thor script
11
+ $ thor default:build # makes the *.gem file
12
+ $ sudo thor default:install # installs the gem on your system
13
+
14
+ (if you don't have thor, it is a gem and it is like rake.)
15
+
16
+
17
+
18
+ TO RUN THE UNIT TESTS/SPECS ON THIS:
19
+
20
+ There are two test suites to run on this.
21
+
22
+ 1. One is the Test::Unit tests copy-pasted from Takuma Ozawa's 'rbtree' gem.
23
+ These tests were used to ensure that this module behaves exactly as his does
24
+ (in as far as the tests specify ;) " Run it with:
25
+ $ ruby test.rb
26
+
27
+ 2. The other test suite is the one I wrote for any new features or fun tests
28
+ I wanted to do on my own. Run them with:
29
+ $ rake spec
30
+ (you will need the rake gem and the rspec gem.)
31
+
32
+ If any tests fail, please email me. ("mark" and then a dot and then "meves"
33
+ at google's popular mail service.)
34
+
8
35
 
9
- (if you don't have thor, it is a gem and it is like rake.)
36
+ Special Thanks to Yoko and Ozawa.
data/Thorfile CHANGED
@@ -3,20 +3,27 @@ module GemHelpers
3
3
 
4
4
  def generate_gemspec
5
5
  $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib")))
6
- require "cli"
6
+ require "hipe-simplebtree"
7
7
 
8
8
  Gem::Specification.new do |s|
9
9
  s.name = 'hipe-simplebtree'
10
- s.version = Hipe::Cli::VERSION
10
+ s.version = Hipe::SimpleBTree::VERSION
11
11
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
12
- s.author = "Mark Meves"
12
+ s.authors = ["Mark Meves"]
13
13
  s.email = "mark.meves@gmail.com"
14
14
  s.homepage = "http://github.com/hipe/hipe-simplebtree"
15
- s.date = %q{2009-11-20}
16
- s.summary = %q{Experiment}
15
+ s.date = %q{2009-11-23}
16
+ s.summary = %q{Simple pure-ruby port of Ozawa's RBTree'}
17
17
  s.description = <<-EOS.strip
18
- just playing around
18
+ This is a pure-ruby port of Takuma Ozawa's RBTree. (it has an identical
19
+ interface and uses the identical unit tests from his version 0.3.0,
20
+ however it is *not* a Red-Black tree.) It's intended for doing lookups and
21
+ not for doing lots of insertions or deletions. Please see RBTree docs
22
+ for a sense of how this is supposed to be used.
23
+ (This one runs the unit tests about 15% slower than Ozawa's C-version,
24
+ and 80% less lines of code ;)
19
25
  EOS
26
+
20
27
  # s.rubyforge_project = "webrat"
21
28
 
22
29
  require "git"
@@ -113,4 +120,90 @@ class Release < Thor
113
120
  def gem
114
121
  sh "gem push pkg/#{read_gemspec.file_name}"
115
122
  end
116
- end
123
+ end
124
+
125
+ #************ the below added by mark************
126
+ class Spec < Thor
127
+ desc "crazy", "try to pull in the latest rbtree test file,\n"+
128
+ " modify it and save it."
129
+ def crazy
130
+ require 'pp'
131
+ @gem_basename = 'rbtree'
132
+ @my_version = Gem::Version.new('0.2.9')
133
+ dep = Gem::Dependency.new @gem_basename, Gem::Requirement.default
134
+
135
+ specs = Gem.source_index.search dep
136
+ local_tuples = specs.map do |spec|
137
+ [[spec.name, spec.version, spec.original_platform, spec], :local]
138
+ end
139
+ local_tuples.extend Crazy
140
+ puts "local #{@gem_basename} gem(s) currently installed:\n" + local_tuples.print
141
+ if (t=local_tuples.max_tuple and t[0][1] > @my_version)
142
+ begin; make_test_file(t); rescue => e; puts e.message; end
143
+ else
144
+ puts "your testfile is probably up to date."
145
+ end
146
+
147
+ print "\nShould we check for newer versions of #{@gem_basename} remotely? (y/n):";
148
+ if ('y'==$stdin.gets.strip)
149
+ begin
150
+ puts "attempting to fetch remote info about #{@gem_basename}..."
151
+ fetcher = Gem::SpecFetcher.fetcher
152
+ remote_tuples = fetcher.find_matching dep, 'all versions', 'match platform', 'prerelease'
153
+ puts "done attempting remote"
154
+ rescue Gem::RemoteFetcher::FetchError => e
155
+ puts "Failed To Connect? (will try local cache) "+e.message
156
+ require 'rubygems/source_info_cache'
157
+ dep.name = '' if dep.name == //
158
+ specs = Gem::SourceInfoCache.search_with_source dep, false, all
159
+ remote_tuples = specs.map do |spec, source_uri|
160
+ [[spec.name, spec.version, spec.original_platform, spec],
161
+ source_uri]
162
+ end
163
+ end
164
+ remote_tuples.extend Crazy
165
+ puts "remote #{@gem_basename} gems found:"+remote_tuples.print
166
+ if (t = remote_tuples.max_tuple and t[0][1] > @my_version)
167
+ str = t[0][1].to_s
168
+ puts "The remote version of #{@gem_basename} (#{str}) is more recent than "+
169
+ "the test file in version control. (#{@my_version}) Consider updating your #{@gem_basename} gem."
170
+ end
171
+ end
172
+ puts 'done.';
173
+ end
174
+ def make_test_file tuple
175
+ dir_basename = tuple[0][0]+'-'+tuple[0][1].to_s
176
+ filename = File.dirname(__FILE__)+'/test-'+tuple[0][1].to_s+'.rb'
177
+ if File.exists? filename
178
+ puts %{\n\nFile already exists: #{File.basename filename}. No need to run script?}
179
+ return
180
+ end
181
+ path = File.expand_path("#{File.dirname(__FILE__)}/../#{dir_basename}/test.rb")
182
+ raise "sorry, couldn't find test file to copy: #{path}" unless File.exist? path
183
+ contents = nil
184
+ File.open(path,'r'){ |fh| contents = fh.read }
185
+ unless md = %r{\Arequire "\./rbtree"(.+)class MultiRBTreeTest <.+\Z}m.match(contents)
186
+ raise "sorry, failed to parse file contents."
187
+ end
188
+ File.open filename, 'w+' do |fh|
189
+ msg = %{#Generated #{Time.now.strftime('%Y/%m/%d %I:%M:%S%p')} by #{__FILE__}}
190
+ fh.write %{require 'rubygems'\nrequire 'hipe-simplebtree'\n\n}+md[1].gsub('RBTree','Hipe::SimpleBTree')
191
+ end
192
+ puts %{\n\nGenerated test file. Try running "ruby #{File.basename(filename)}" and keep your fingers crossed!}
193
+ end
194
+ end
195
+
196
+ module Crazy
197
+ def print
198
+ return '[none]' if count == 0
199
+ lines = []
200
+ self.each{|t| lines << %{#{t[0][0]} #{t[0][1]}} }
201
+ lines * "\n"
202
+ end
203
+ def max_tuple
204
+ self.inject{|left,right| left[0][1] > right[0][1] ? left : right }
205
+ end
206
+ end
207
+
208
+ #arr = Gem.source_index.find_name('gem_basename')
209
+ #list = Gem::CommandManager.instance['list']
@@ -1,37 +1,421 @@
1
1
  module Hipe
2
- # Experimental *simple* b-tree -- the only purpose of this for now is to
3
- # impleement a method for "find the index of the first element in the array
4
- # that is greater than a provided element." It won't be efficient or useful
2
+ # Experimental *simple* b-tree -- the only purpose of this for now is to
3
+ # implement a method for "find the lowest key that is greater than the provided key"
4
+ # (or find the greatest key that is lower than a provided key)
5
+ # It won't be efficient or useful
5
6
  # for adding/removes lots of nodes, only for providing the above service.
6
- # It is presumed that it will be slower than RBTrees (Red-Black tree), but
7
+ # It is presumed that it will be slower than RBTrees (Red-Black tree), but
7
8
  # faster than scanning all the elements of a sorted array to find this index.
8
- class SimpleBTree
9
9
 
10
- # @param [Array] array an array sorted or not sorted whose values represent the values
11
- # of the nodes of the tree. Values need not be unique.
12
- # @yield [left,right] a code block that stipulates your comparison
13
- # algorithm for using in sorting, exactly like Array#sort
14
- # @example
15
- # tree = Hipe::SimpleBTree.new(['alpha','gamma','beta']){|x,y| x <=> y}
16
- # puts tree.to_array
17
- def initialize(array=nil, &sorter)
18
- @array = array || []
19
- @sorter = sorter || Proc.new {|left,right| left <=> right}
20
- @sorted = false
10
+ class SimpleBTree < Hash
11
+
12
+ VERSION = '0.0.1'
13
+
14
+ # @see Hash#new
15
+ def initialize(*args,&block)
16
+ @locked_stack = 0
17
+ @insepcting = false
18
+ @autosort = true
19
+ super
20
+ @cmp_proc = nil
21
+ @sorted_keys = [] # with a new hash it is always empty, right? (we don't have [] literal construtors)
22
+ @tree = nil
23
+ end
24
+
25
+ # @see Hash::[]
26
+ def self.[](*args)
27
+ self.new.send(:init_with_brackets, args)
21
28
  end
29
+
30
+ attr_accessor :cmp_proc
22
31
 
23
- def to_array
24
- sort!
25
- @array.clone
32
+ def cmp_proc= proc
33
+ raise TypeError.new(%{must be proc not "#{proc.class}"}) unless (@cmp_proc = proc).instance_of?(Proc)||(!proc)
34
+ sort_keys!
35
+ end
36
+
37
+ # the unless method_defined? below are so we can reload this file from irb
38
+ protected
39
+ alias_method :hash_set, %s{[]=} unless method_defined? :hash_set
40
+ alias_method :hash_delete, :delete unless method_defined? :hash_delete
41
+ alias_method :hash_keys, :keys unless method_defined? :hash_keys
42
+ alias_method :hash_replace, :replace unless method_defined? :hash_replace
43
+ alias_method :hash_inspect, :inspect unless method_defined? :hash_inspect
44
+
45
+ public
46
+
47
+ def clone
48
+ clone = self.class.new( &default_proc )
49
+ clone.cmp_proc = self.cmp_proc
50
+ clone.default = self.default unless clone.default_proc # very important! 1 hr. bug
51
+ clone.update_with_hash self
52
+ clone
53
+ end
54
+
55
+ def default *args
56
+ ret =
57
+ if 0==args.size then super
58
+ elsif 2<=args.size then raise ArgumentError.new("expecting 0 or 1 had #{args.size}")
59
+ elsif default_proc.nil? then super
60
+ else
61
+ self.default_proc.call self, args[0]
62
+ end
63
+ ret
64
+ end
65
+
66
+ def readjust *args, &proc2
67
+ size = args.size + (proc2 ? 1 : 0)
68
+ raise ArgumentError.new("wrong number of arguments - #{size} for 1") if size > 1
69
+ new_proc = (args.size > 0 && args[0].nil?) ? default_cmp_proc : (proc2 || args[0])
70
+ my_keys = hash_keys
71
+ # try the sort before doing any permanant change because it might throw type comparison error
72
+ my_keys.sort!(&new_proc)
73
+ @sorted_keys = my_keys
74
+ @cmp_proc = new_proc
75
+ end
76
+
77
+ def default_cmp_proc
78
+ return Proc.new{|x,y| x <=> y}
79
+ end
80
+
81
+ def _first_or_last(which)
82
+ if @sorted_keys.size > 0
83
+ k = @sorted_keys.send(which)
84
+ [get_cloned_key(k), self[k]]
85
+ else
86
+ default_proc ? default_proc.call(self) : default
87
+ end
88
+ end
89
+
90
+ def first; _first_or_last(:first); end
91
+
92
+ def last; _first_or_last(:last); end
93
+
94
+ def get_cloned_key key
95
+ @clones ||= {}
96
+ unless @clones.has_key? key
97
+ # not sure what the test is trying to accomplish here
98
+ @clones[key] = key.instance_of?(String) ? key.clone.freeze : key
99
+ end
100
+ @clones[key]
101
+ end
102
+
103
+ def == (other)
104
+ super && @sorted_keys == other.send(:sorted_keys) && @cmp_proc == other.cmp_proc
105
+ end
106
+
107
+ def each &proc
108
+ return _enumerate(proc){ |k| [k, self[k]] }
26
109
  end
27
110
 
28
- # @private
29
- # left public for testing only
30
- def sort!
31
- unless @sorted
32
- @array.sort!(&@sorter)
33
- @sorted = true
111
+ def reverse_each &proc
112
+ return _enumerate(proc,@sorted_keys.reverse){ |k| [k, self[k]] }
113
+ end
114
+
115
+ alias_method :each_pair, :each
116
+
117
+ def each_key &proc
118
+ return _enumerate(proc){ |k| k } if block_given?
119
+ @sorted_keys.map{|k| [k] }.each
120
+ end
121
+
122
+ def each_value &proc
123
+ return _enumerate(proc){|k| self[k]} if block_given?
124
+ @sorted_keys.map{|k| [self[k]] }.each
125
+ end
126
+
127
+ def delete_if
128
+ return each unless block_given?
129
+ ks = []
130
+ _enumerate(Proc.new{|k,v| ks << k if yield(k,v) }){|k| [k,self[k]]}
131
+ ks.each { |k| hash_delete k }
132
+ if ks.size > 0
133
+ sort_keys!
134
+ self
135
+ else
136
+ nil
137
+ end
138
+ end
139
+
140
+ alias_method %s{reject!}, :delete_if
141
+
142
+ def select &proc
143
+ if block_given?
144
+ eaches = []
145
+ @sorted_keys.each { |k| eaches << [k,self[k]] if proc.call(k,self[k]) }
146
+ Hipe::SimpleBTree[eaches] #* we don't know what to do about default & default proc & sort # note 4
147
+ else
148
+ each
149
+ end
150
+ end
151
+
152
+ def reject &proc
153
+ return each unless block_given?
154
+ guy = select{ |k,v| ! proc.call(k,v) }
155
+ ( guy.size == self.size ) ? nil : guy
156
+ end
157
+
158
+ def pretty_print(q)
159
+ mybuff = ''
160
+ if @inspecting
161
+ mybuff << %["#<#{self.class}: ...>"]
162
+ else
163
+ mybuff << %[#<#{self.class}: ]
164
+ @inspecting = true;
165
+ els = @sorted_keys.map do |k| # @todo learn more about PP to clean this up
166
+ PP.pp(k, s='');s.chop!; s << '=>'; PP.pp(self[k],x=''); s<<x.strip!;
167
+ s
168
+ end
169
+ str = els.join(', '); br = " ";
170
+ if str.length > 79
171
+ str = els.join(",\n "); br = "\n ";
172
+ end
173
+ br = "\n " if str.include? self.class.to_s # total hack to get it to pass the tests
174
+ PP.pp(default,def_s='')
175
+ PP.pp(cmp_proc,cmp_s='')
176
+ mybuff << %({#{str}},#{br}default=#{def_s.chop},#{br}cmp_proc=#{cmp_s.chop}>)
177
+ @inspecting = false
178
+ end
179
+ q.text(mybuff)
180
+ end
181
+
182
+ def inspect
183
+ if @inspecting
184
+ %{#<#{self.class.name}: ...>}
185
+ else
186
+ @inspecting = true
187
+ # /#<Hipe::SimpleBTree: (\{.*\}), default=(.*), cmp_proc=(.*)>/
188
+ ret = %[#<#{self.class.name}: #{hash_inspect}, default=#{default.inspect}, cmp_proc=#{cmp_proc.inspect}>]
189
+ @inspecting = false
190
+ ret
191
+ end
192
+ end
193
+
194
+ def []= k, value
195
+ raise TypeError.new("can't modify simplebtree in iteration") if @locked_stack > 0
196
+ use_key = k
197
+ if (!has_key?(k))
198
+ my_keys = @sorted_keys.dup # the unit test requires this
199
+ my_keys << use_key
200
+ my_keys.sort!(&@cmp_proc) # we want this to throw type comparison error
201
+ @sorted_keys = my_keys
202
+ @tree = nil # we loose a lot of sorted data when we add just one element. # note 6.
34
203
  end
204
+ super use_key, value
35
205
  end
36
- end
37
- end
206
+
207
+ def pop
208
+ if @sorted_keys.size == 0
209
+ ret = default_proc ? default_proc.call(self, nil) : default
210
+ else
211
+ key = @sorted_keys.pop
212
+ @tree = nil
213
+ ret = [key, delete(key)]
214
+ end
215
+ return ret
216
+ end
217
+
218
+ def delete key
219
+ unless has_key? key
220
+ ret = block_given? ? yield : nil
221
+ else
222
+ ret = super
223
+ sort_keys!
224
+ end
225
+ ret
226
+ end
227
+
228
+ def flatten; each.flatten; end
229
+
230
+ def clear; super; @sorted_keys.clear; @tree = nil; end
231
+
232
+ def update x; super x; sort_keys!; end
233
+
234
+ def invert; self.class[super]; end
235
+
236
+ def keys; @sorted_keys.dup; end
237
+
238
+ def values; @sorted_keys.map{|k|self[k]}; end
239
+
240
+ def to_a; each.to_a; end
241
+
242
+ def to_s; to_a.to_s; end
243
+
244
+ def merge x; #not sure why super way wouldn't work
245
+ clone = self.class[self]
246
+ x.each do |x,y|
247
+ clone.hash_set(x,y)
248
+ end
249
+ clone.sort_keys!
250
+ clone
251
+ end
252
+
253
+ def to_rbtree
254
+ self # self.class[self]
255
+ end
256
+
257
+ def stats; tree.stats; end
258
+
259
+ def lower_bound key; _bound(:lower_bound_index,key) end
260
+
261
+ def upper_bound key; _bound(:upper_bound_index,key) end
262
+
263
+ # @return an array containing key-value pairs between the result of lower_bound
264
+ # and upper_bound. If a block is given it calls the block once for each pair.
265
+ def bound key1, key2=key1
266
+ #require 'ruby-debug'
267
+ #debugger
268
+ return [] unless i1 = tree.lower_bound_index(key1) and i2 = tree.upper_bound_index(key2)
269
+ if block_given? #note 9
270
+ @locked_stack += 1
271
+ (i1..i2).each{ |i| yield @sorted_keys[i], self[@sorted_keys[i]] }
272
+ @locked_stack -= 1
273
+ end
274
+ (i1..i2).map{ |i| [@sorted_keys[i], self[@sorted_keys[i]]] }
275
+ end
276
+
277
+ def replace tree
278
+ unless tree.instance_of? self.class
279
+ raise TypeError.new(%{wrong argument type #{tree.class} (expected #{self.class})})
280
+ end
281
+ hash_replace tree
282
+ @cmp_proc = tree.cmp_proc
283
+ @default = tree.default
284
+ sort_keys!
285
+ end
286
+
287
+ def dump
288
+ TypeError.new(%{cannot dump #{self.class} with default proc}) if @default_proc
289
+ TypeError.new(%{cannot dump #{self.class} with compare proc}) if @cmp_proc
290
+ Marshal.dump(self)
291
+ end
292
+
293
+ protected
294
+
295
+ attr_accessor :sorted_keys
296
+
297
+ def _bound which, key
298
+ index = tree.send which, key
299
+ index ? [@sorted_keys[index], self[@sorted_keys[index]]] : nil # avoid defaults
300
+ end
301
+
302
+ # there are several ways to construct a SimpleBtree with the [] class method.
303
+ # These are identical to the variants of the [] method of the Hash class, plus one more
304
+ # btree = SimpleBtree[{'a'=>'A', 'b'=>'B'}] # from a literal hash
305
+ # btree = SimpleBtree['a','A','b','B'] # will result in the same as the first example
306
+ # btree = SimpleBtree[*['a','A','b','B']] # a different way of saying the above
307
+ # btree = SimpleBtree[hash] # deep-copy a hash object (one level deep)
308
+ # btree = SimpleBtree[simple_btree] # deep-copy another one (one level deep)
309
+ def init_with_brackets args
310
+
311
+ if args.size!=1
312
+ raise ArgumentError.new(%{odd number of arguments for #{self}}) unless args.size % 2 == 0
313
+ use_as_hash = Hash[*args]
314
+ else
315
+ arg = args[0]
316
+ case args[0]
317
+ when SimpleBTree then use_as_hash = arg
318
+ when Hash then use_as_hash = arg
319
+ when Array
320
+ # allow construction like this: btree = SimpleBTree[['a'],['b','B'],['c']..]
321
+ if (arg.size > 0 && arg[0].instance_of?(Array))
322
+ arg.map!{|x| x.size == 1 ? [x[0], nil] : x }
323
+ end
324
+ use_as_hash = Hash[*arg.flatten] # arg might be one- or two-dimensional array
325
+ else
326
+ raise ArgumentError.new("Don't know how to construct with a single argument of class #{args[0].class}")
327
+ end # case
328
+ end # if-else
329
+ update_with_hash use_as_hash
330
+ self
331
+ end
332
+
333
+ def _enumerate(their_proc=nil,keys=nil,&my_proc)
334
+ use_keys = keys ? keys : @sorted_keys
335
+ return use_keys.map{|k| [k,self[k]]} if their_proc.nil?
336
+ @locked_stack += 1
337
+ use_keys.each do |k|
338
+ their_proc.call(*my_proc.call(k)) # do we decrement the sak
339
+ end
340
+ @locked_stack -= 1
341
+ self
342
+ end
343
+
344
+ def update_with_hash hash
345
+ hash.each{|k,v| hash_set(k,v) }
346
+ sort_keys! # this of course could be improved if it's a copy of a btree
347
+ end
348
+
349
+ def sort_keys!
350
+ my_keys = hash_keys
351
+ my_keys.sort!(&@cmp_proc) #might throw type comparison error
352
+ @sorted_keys = my_keys
353
+ end
354
+
355
+ def tree
356
+ @tree = @sorted_keys.size == 0 ? nil : Tree.new(@sorted_keys, 0, @sorted_keys.size-1,1) if @tree.nil?
357
+ @tree
358
+ end
359
+
360
+ # just to be incendiary, we don't distinguish among leaf nodes, branch nodes, and root nodes.
361
+ # @private
362
+ class Tree
363
+ def initialize(ary, start_index, end_index, depth=0)
364
+ @depth = depth
365
+ width = end_index - start_index + 1
366
+ value_index = start_index + width / 2
367
+ value_index -= 1 if depth % 2 == 0 and width % 2 == 0 and width != 1
368
+ @key = ary[value_index]
369
+ @index = value_index # careful to keep it synced with key!
370
+ @left = (value_index == start_index) ? nil : Tree.new(ary, start_index, value_index-1, depth + 1)
371
+ @right = (value_index == end_index) ? nil : Tree.new(ary, value_index + 1, end_index, depth + 1)
372
+ end
373
+
374
+ def stats
375
+ heights = [1]
376
+ mins = []
377
+ num_nodes = 1
378
+ ['@left','@right'].each do |name|
379
+ child = instance_variable_get(name)
380
+ if (child)
381
+ stats = child.stats
382
+ heights << stats[:height] + 1
383
+ mins << stats[:min_height]
384
+ num_nodes += stats[:num_nodes]
385
+ end
386
+ end
387
+ {
388
+ :height => heights.max,
389
+ :min_height => (mins.size==0) ? 1 : (mins.min + 1),
390
+ :num_nodes => num_nodes
391
+ }
392
+ end #stats
393
+
394
+ # @retrurn the index of the lowest key that is equal to or greater than the given key
395
+ # (inside of lower boundary). If there is no such key, returns nil.
396
+ def lower_bound_index key
397
+ (@key >= key) ? ((@left && @left.lower_bound_index(key)) || @index) : (@right && @right.lower_bound_index(key))
398
+ end
399
+
400
+ # @return the index of the greatest key that is equal to or lower than the given key
401
+ # (inside of upper boundary). If there is no such key, returns nil.
402
+ def upper_bound_index key
403
+ (@key <= key) ? ((@right && @right.upper_bound_index(key)) || @index) : (@left && @left.upper_bound_index(key))
404
+ end
405
+
406
+ end # class Tree
407
+ end # class SimpleBTree
408
+ end # Hipe
409
+
410
+
411
+ # note 1: DONE consider making this descend from Hash
412
+ # note 2: when to copy over cmp_proc et all? when not to?
413
+ # note 3: DONE reafactor iterators to all use _enumerate
414
+ # note 4: (deep copy questions)
415
+ # note 5: when we are running this from irb we only want to do alias-method once
416
+ # note 6: i know nothing about efficient ways to re-tree
417
+ # note 7: considering making a lazy accessor for sorted_keys like tree
418
+ # note 8: these are points to consider if we ever do MultiSimpleBtree
419
+ # note 9: there are at least 3 ways we could have done this, with tradeoffs
420
+ # alternately among code size/duplication, performance when block given, performance
421
+ # when block not given. The current way is short and fast for when a block is not given.