btree 0.0.0 → 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5a5640c8a1cddfdfb4f56acbc605aaa5722ab154605f133a8dfa8b089cc32e1c
4
+ data.tar.gz: edf0f1348d367ddacb3337d42df137201064c981b5becc84ab6a0b1368990217
5
+ SHA512:
6
+ metadata.gz: a7ded3fe792d31605f5a115402419f819c0bebfc72d38ac50fac86300bd595b871be8d73a19d538a75445a76579b327bdc2884f38e5606f94ca8761df976a26d
7
+ data.tar.gz: 0cee704dabf324cddfab2e6edac454fe7698cfdd8b936f013969aec0b4b22e75ef6ac2ca272ab2d04313b0bdd5b1b6d35ce0d4e54f5eb6d6789988c95d64121e
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ # Runtime dependencies come from btree.gemspec, which declares none.
5
+ gemspec
6
+
7
+ # shoulda-context supplies the context/should DSL the tests use. The shoulda
8
+ # meta-gem would also pull in shoulda-matchers, whose only purpose is Rails
9
+ # model assertions this project has no use for, and which drags in the whole
10
+ # activesupport tree behind it.
11
+ group :development, :test do
12
+ gem "rake", "~> 13"
13
+ gem "minitest", "~> 6.0"
14
+ gem "shoulda-context", "~> 2.0"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ btree (1.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ drb (2.2.3)
10
+ minitest (6.0.6)
11
+ drb (~> 2.0)
12
+ prism (~> 1.5)
13
+ prism (1.9.0)
14
+ rake (13.4.2)
15
+ shoulda-context (2.0.0)
16
+
17
+ PLATFORMS
18
+ ruby
19
+ x86_64-linux
20
+
21
+ DEPENDENCIES
22
+ btree!
23
+ minitest (~> 6.0)
24
+ rake (~> 13)
25
+ shoulda-context (~> 2.0)
26
+
27
+ CHECKSUMS
28
+ btree (1.0.1)
29
+ drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
30
+ minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
31
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
32
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
33
+ shoulda-context (2.0.0) sha256=7adf45342cd800f507d2a053658cb1cce2884b616b26004d39684b912ea32c34
34
+
35
+ BUNDLED WITH
36
+ 4.0.17
data/History.txt CHANGED
@@ -1,3 +1,22 @@
1
+ == 1.0.1 / 2026-07-23
2
+
3
+ * 2 bug fixes
4
+ * Range queries now visit every subtree that can contain an in-range key.
5
+ Previously the descent stopped at the first key at or above the range end,
6
+ so values held in later subtrees were silently omitted from the result.
7
+ * Inserting a key that a node split had promoted into an internal node now
8
+ raises "Duplicate key". Previously the duplicate check ran only at the
9
+ leaf, so such a key was descended past and inserted a second time, leaving
10
+ one copy unreachable in a subtree.
11
+
12
+ * 1 minor enhancement
13
+ * Nodes locate keys by a hybrid linear/binary search, scanning small nodes
14
+ and using bsearch on large ones.
15
+
16
+ == 1.0.0 / 2017-11-09
17
+
18
+ * Initial gem release.
19
+
1
20
  == 0.0.0 / 2010-12-20
2
21
 
3
22
  * 1 major enhancement
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
- Ruby-BTree
2
- ==========
1
+ btree
2
+ =====
3
3
 
4
4
  Pure ruby implementation of a btree as described in Introduction to Algorithms by
5
5
  Cormen, Leiserson, Rivest and Stein, Chapter 18.
data/Rakefile CHANGED
@@ -1,18 +1,177 @@
1
+ require 'rake/testtask'
2
+ require 'rake/clean'
3
+ require 'rubygems/package_task'
1
4
 
2
- begin
3
- require 'bones'
5
+ SPEC = Gem::Specification.load('btree.gemspec')
6
+ VERSION = SPEC.version.to_s
7
+ GEM_FILE = "pkg/#{SPEC.full_name}.gem"
8
+
9
+ CLOBBER.include('pkg', 'doc')
10
+
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib' << 'test'
13
+ t.test_files = FileList['test/test_*.rb']
14
+ end
15
+
16
+ task :default => :test
17
+
18
+ Gem::PackageTask.new(SPEC) do |pkg|
19
+ pkg.need_zip = false
20
+ pkg.need_tar = false
21
+ end
22
+
23
+ desc 'Build the gem into pkg/'
24
+ task :build => :package
25
+
26
+ desc 'Install the built gem locally'
27
+ task :install => :build do
28
+ sh 'gem', 'install', GEM_FILE
29
+ end
30
+
31
+ # Shells out with Bundler's environment stripped. Anything that inspects the
32
+ # gem as an end user would -- installing it, requiring it -- has to run outside
33
+ # this project's bundle, or it picks up the working tree instead of the package.
34
+ def unbundled(*cmd)
35
+ require 'bundler'
36
+ Bundler.with_unbundled_env { sh(*cmd) }
4
37
  rescue LoadError
5
- abort '### Please install the "bones" gem ###'
38
+ sh(*cmd)
39
+ end
40
+
41
+ def rubygems_versions
42
+ require 'open-uri'
43
+ require 'json'
44
+ JSON.parse(URI.open("https://rubygems.org/api/v1/versions/#{SPEC.name}.json").read)
45
+ rescue OpenURI::HTTPError, SocketError, JSON::ParserError => e
46
+ warn "WARNING: could not reach rubygems.org (#{e.class}: #{e.message})."
47
+ nil
6
48
  end
7
49
 
8
- task :default => 'test:run'
9
- task 'gem:release' => 'test:run'
50
+ namespace :release do
51
+ desc 'Check the working tree, metadata and credentials are fit to release'
52
+ task :check do
53
+ problems = []
54
+
55
+ problems << 'working tree is dirty; commit or stash first' unless
56
+ `git status --porcelain`.strip.empty?
57
+
58
+ # The tag has to point at a commit that exists somewhere other than this
59
+ # laptop, otherwise a published gem can be built from work nobody else has.
60
+ if `git branch -r --contains HEAD 2>/dev/null`.strip.empty?
61
+ problems << 'HEAD is not on any remote branch; push it before releasing'
62
+ end
63
+
64
+ problems << "tag v#{VERSION} already exists; bump version.txt" if
65
+ `git tag -l v#{VERSION}`.strip == "v#{VERSION}"
66
+
67
+ problems << "History.txt has no entry for #{VERSION}" unless
68
+ File.read('History.txt').start_with?("== #{VERSION} ")
10
69
 
11
- Bones {
12
- name 'btree'
13
- authors 'Douglas A. Seifert'
14
- email 'doug@dseifert.net'
15
- url 'http://www.dseifert.net/btree'
16
- readme_file 'README.md'
17
- }
70
+ # A version number is the one genuinely unrecoverable resource in a
71
+ # release: RubyGems refuses to re-push it even after a yank.
72
+ if (published = rubygems_versions)
73
+ problems << "#{SPEC.name} #{VERSION} is already on rubygems.org" if
74
+ published.any? {|v| v['number'] == VERSION }
75
+ end
18
76
 
77
+ # Surfaced now rather than as a 403 after the tag has been pushed.
78
+ # Ask RubyGems where the credentials file lives rather than assuming
79
+ # ~/.gem/credentials: modern installs use the XDG path
80
+ # (~/.local/share/gem/credentials), and hardcoding the old one reports a
81
+ # missing key that is really there.
82
+ require 'rubygems/config_file'
83
+ creds = Gem.configuration.credentials_path
84
+ unless ENV['GEM_HOST_API_KEY'] || File.exist?(creds)
85
+ problems << "no rubygems credentials at #{creds}; run `gem signin` (the key needs the push_rubygem scope)"
86
+ end
87
+
88
+ branch = `git rev-parse --abbrev-ref HEAD`.strip
89
+ warn "WARNING: releasing from #{branch}, not master." unless branch == 'master'
90
+
91
+ unless problems.empty?
92
+ abort "ERROR: not ready to release #{SPEC.name} #{VERSION}:\n" +
93
+ problems.map {|p| " - #{p}" }.join("\n")
94
+ end
95
+
96
+ puts "OK: #{SPEC.name} #{VERSION} passes pre-release checks."
97
+ end
98
+
99
+ desc 'Build the gem and prove the built artifact actually works'
100
+ task :verify => :repackage do
101
+ require 'tmpdir'
102
+
103
+ puts "Packaged #{GEM_FILE}:"
104
+ Gem::Package.new(GEM_FILE).spec.files.each {|f| puts " #{f}" }
105
+
106
+ # Installing into a throwaway GEM_HOME is the only check that covers what
107
+ # was actually packaged rather than what is sitting in the working tree --
108
+ # a file missing from the manifest looks fine locally and breaks on install.
109
+ Dir.mktmpdir do |dir|
110
+ unbundled 'gem', 'install', '--no-document', '--install-dir', dir, GEM_FILE
111
+
112
+ script = <<~RUBY
113
+ gem 'btree'
114
+ require 'btree'
115
+ raise "version mismatch: \#{Btree.version} != #{VERSION}" unless Btree.version == '#{VERSION}'
116
+ t = Btree.create(3)
117
+ (1..50).each {|i| t[i] = i.to_s }
118
+ got = t[10..14]
119
+ raise "bad range query: \#{got.inspect}" unless got == %w[10 11 12 13 14]
120
+ raise "bad lookup" unless t[37] == '37'
121
+ puts "smoke test passed on the installed gem"
122
+ RUBY
123
+
124
+ Dir.chdir(dir) do
125
+ unbundled({'GEM_HOME' => dir, 'RUBYOPT' => nil}, RbConfig.ruby, '-e', script)
126
+ end
127
+ end
128
+ end
129
+
130
+ desc 'Tag the release and push the tag to origin (reversible)'
131
+ task :tag do
132
+ sh 'git', 'tag', '-a', "v#{VERSION}", '-m', "#{SPEC.name} #{VERSION}"
133
+ sh 'git', 'push', 'origin', "v#{VERSION}"
134
+ puts "Tagged v#{VERSION}. To undo: git tag -d v#{VERSION} && git push origin :v#{VERSION}"
135
+ end
136
+
137
+ # repackage, not build: the packaged gem is a file task, so an existing
138
+ # pkg/*.gem newer than its sources satisfies :build without rebuilding. Fine
139
+ # locally, wrong when the next step is irreversible.
140
+ desc 'Push the gem to rubygems.org (IRREVERSIBLE)'
141
+ task :push => :repackage do
142
+ puts
143
+ puts "About to push #{GEM_FILE} to rubygems.org."
144
+ puts "This cannot be undone: #{SPEC.name} #{VERSION} can never be re-pushed,"
145
+ puts 'even if it is yanked afterwards.'
146
+
147
+ unless ENV['FORCE'] == '1'
148
+ abort 'ERROR: refusing to push from a non-interactive shell without FORCE=1.' unless $stdin.tty?
149
+ print "Type the version (#{VERSION}) to confirm: "
150
+ abort 'Aborted.' unless $stdin.gets.to_s.strip == VERSION
151
+ end
152
+
153
+ sh 'gem', 'push', GEM_FILE
154
+ end
155
+
156
+ desc 'Confirm the version is live on rubygems.org'
157
+ task :confirm do
158
+ published = rubygems_versions
159
+ if published&.any? {|v| v['number'] == VERSION }
160
+ puts "Confirmed: #{SPEC.name} #{VERSION} is live on rubygems.org."
161
+ else
162
+ warn "WARNING: #{SPEC.name} #{VERSION} is not visible on rubygems.org yet (indexing can lag)."
163
+ end
164
+ end
165
+ end
166
+
167
+ desc 'Full release: check, test, verify the built gem, tag, then push'
168
+ task :release do
169
+ # Ordered so that everything reversible happens first. The tag is cheap to
170
+ # delete; the push is permanent, so it goes last and only after the packaged
171
+ # gem has been installed and exercised.
172
+ %w[release:check test release:verify release:tag release:push release:confirm].each do |t|
173
+ puts "\n==> #{t}"
174
+ Rake::Task[t].invoke
175
+ end
176
+ puts "\nReleased #{SPEC.name} #{VERSION}."
177
+ end
data/btree.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'btree'
5
+ # version.txt stays the single source of truth: Btree.version reads it back
6
+ # at runtime, so it has to ship in the gem regardless.
7
+ s.version = File.read(File.expand_path('version.txt', __dir__)).strip
8
+ s.authors = ['Douglas A. Seifert']
9
+ s.email = 'doug@dseifert.net'
10
+ s.homepage = 'https://github.com/seifertd/Ruby-BTree'
11
+ s.license = 'MIT'
12
+
13
+ s.summary = 'Pure ruby implementation of a btree'
14
+ s.description = <<~DESC
15
+ Pure ruby implementation of a btree as described in Introduction to
16
+ Algorithms by Cormen, Leiserson, Rivest and Stein, Chapter 18.
17
+ DESC
18
+
19
+ # Array#bsearch_index, used by Node#key_index, is the oldest thing here that
20
+ # is not universally available. CI only exercises 3.2 and up, because that is
21
+ # the floor imposed by the development gems, not by the library.
22
+ s.required_ruby_version = '>= 2.3.0'
23
+
24
+ # Rejecting dotfiles drops .github/, .gitignore and the .ruby-* files, which
25
+ # is the same set the old Bones exclude list named by hand. Building outside
26
+ # a git checkout is not supported, which matches how this gem is released.
27
+ s.files = `git ls-files -z`.split("\x0").reject {|f| f.start_with?('.') }
28
+
29
+ s.require_paths = ['lib']
30
+ s.extra_rdoc_files = ['History.txt', 'README.md']
31
+ s.rdoc_options = ['--main', 'README.md']
32
+
33
+ s.metadata = {
34
+ 'homepage_uri' => s.homepage,
35
+ 'source_code_uri' => s.homepage,
36
+ 'changelog_uri' => "#{s.homepage}/blob/master/History.txt",
37
+ 'bug_tracker_uri' => "#{s.homepage}/issues",
38
+
39
+ # Requires that whoever pushes this gem has MFA enabled on their
40
+ # rubygems.org account, so a leaked API key is not enough on its own.
41
+ # This needs MFA turned on for the account first, or the push is rejected.
42
+ 'rubygems_mfa_required' => 'true'
43
+ }
44
+ end
data/lib/btree/node.rb CHANGED
@@ -1,18 +1,28 @@
1
1
  class Btree::Node
2
- attr_accessor :leaf
2
+
3
+ # Nodes holding at most this many keys are searched by a straight scan
4
+ # rather than by bsearch, whose per-probe block dispatch costs more than the
5
+ # comparisons it saves on a short array. The measured crossover is around
6
+ # 23 keys and the curves are within a couple of percent of each other from
7
+ # roughly 19 to 23, so the exact value here matters little. Note that a
8
+ # degree of 12 or less can never exceed the limit (a node holds at most
9
+ # 2*degree - 1 keys), and that a node under a larger degree still scans
10
+ # while it is sparsely filled.
11
+ LINEAR_SCAN_LIMIT = 20
12
+ private_constant :LINEAR_SCAN_LIMIT
13
+
3
14
  def initialize(degree)
4
15
  @degree = degree
5
16
  @keys = []
6
17
  @children = []
7
- @leaf = true
8
18
  end
9
19
 
10
20
  def dump(level = 0)
11
21
  @keys.each_with_index do |key, idx|
22
+ puts "LEVEL: #{level} => #{key.first}: full? #{full?} leaf? #{leaf?} children: #{values.inspect}"
12
23
  if @children[idx]
13
24
  @children[idx].dump(level + 1)
14
25
  end
15
- puts "#{level}: #{key.first}: full? #{full?} leaf? #{leaf?}"
16
26
  end
17
27
  (@children[@keys.size..-1] || []).each do |c|
18
28
  c.dump(level+1)
@@ -41,50 +51,76 @@ class Btree::Node
41
51
  end
42
52
 
43
53
  def leaf?
44
- @leaf
54
+ @children.length == 0
45
55
  end
46
56
 
47
57
  def size
48
58
  @keys.size
49
59
  end
50
60
 
51
- def value_of(key)
52
- i = 1
53
- while i <= size && key > @keys[i-1].first
61
+ # Values whose keys fall in the range, in key order. Subtrees that cannot
62
+ # contain an in-range key are never visited.
63
+ def values_of(range)
64
+
65
+ result = Array.new
66
+ lo = range.begin
67
+ hi = range.end
68
+
69
+ # Skip the keys, and the subtrees between them, that sort entirely below
70
+ # the range. When every key is below it, this lands on the rightmost
71
+ # child, which may still hold in-range keys.
72
+ i = lo ? key_index(lo) : 0
73
+
74
+ result += @children[i].values_of(range) if !leaf? && @children[i]
75
+
76
+ while i < size
77
+ key = @keys[i].first
78
+ # Everything from here on sorts above the range.
79
+ break if hi && (range.exclude_end? ? key >= hi : key > hi)
80
+ result << @keys[i].last if range.cover? key
81
+ result += @children[i+1].values_of(range) if !leaf? && @children[i+1]
54
82
  i += 1
55
83
  end
56
84
 
57
- if i <= size && key == @keys[i-1].first
58
- return @keys[i-1].last
85
+ result
86
+
87
+ end
88
+
89
+
90
+ def value_of(key)
91
+
92
+ return values_of(key) if key.kind_of? Range
93
+
94
+ i = key_index(key)
95
+
96
+ if i < size && key == @keys[i].first
97
+ return @keys[i].last
59
98
  elsif leaf?
60
99
  return nil
61
100
  else
62
- return @children[i-1].value_of(key)
101
+ return @children[i].value_of(key)
63
102
  end
64
103
  end
65
104
 
66
105
  def insert(key, value)
67
- i = size - 1
68
- #puts "INSERTING #{key} INTO NODE: #{self.inspect}"
106
+ # The slot the key would occupy is also the slot it would already occupy if
107
+ # it were present, so locating it and rejecting a duplicate are one step.
108
+ # Checked at every node on the way down, not just at the leaf: a key that a
109
+ # split has promoted into an internal node would otherwise be descended
110
+ # past and inserted a second time, hiding one copy in a subtree.
111
+ i = key_index(key)
112
+ raise "Duplicate key" if i < size && @keys[i].first == key
113
+
69
114
  if leaf?
70
- raise "Duplicate key" if @keys.any?{|(k,v)| k == key } #OPTIMIZE: This is inefficient
71
- while i >= 0 && @keys[i] && key < @keys[i].first
72
- @keys[i+1] = @keys[i]
73
- i -= 1
74
- end
75
- @keys[i+1] = [key, value]
115
+ @keys.insert(i, [key, value])
76
116
  else
77
- while i >= 0 && @keys[i] && key < @keys[i].first
78
- i -= 1
117
+ if @children[i] && @children[i].full?
118
+ split(i)
119
+ # The split just promoted a key into this node, after the check above.
120
+ raise "Duplicate key" if @keys[i].first == key
121
+ i += 1 if key > @keys[i].first
79
122
  end
80
- #puts " -- INSERT KEY INDEX #{i}"
81
- if @children[i+1] && @children[i+1].full?
82
- split(i+1)
83
- if key > @keys[i+1].first
84
- i += 1
85
- end
86
- end
87
- @children[i+1].insert(key, value)
123
+ @children[i].insert(key, value)
88
124
  end
89
125
  end
90
126
 
@@ -94,7 +130,6 @@ class Btree::Node
94
130
  splitee = @children[child_idx]
95
131
  y = Btree::Node.new(@degree)
96
132
  z = Btree::Node.new(@degree)
97
- z.leaf = splitee.leaf
98
133
  (@degree-1).times do |j|
99
134
  z._keys[j] = splitee._keys[j+@degree]
100
135
  y._keys[j] = splitee._keys[j]
@@ -136,4 +171,21 @@ class Btree::Node
136
171
  @children
137
172
  end
138
173
 
174
+ private
175
+
176
+ # Index of the first key that does not sort below the given one, or size if
177
+ # every key sorts below it. For an internal node that is also the index of
178
+ # the child to descend into. Both branches test only < so that keys need
179
+ # only support <, > and == -- bsearch's natural k >= key would widen that.
180
+ def key_index(key)
181
+ n = size
182
+ if n <= LINEAR_SCAN_LIMIT
183
+ i = 0
184
+ i += 1 while i < n && @keys[i].first < key
185
+ i
186
+ else
187
+ @keys.bsearch_index {|(k, _)| !(k < key) } || n
188
+ end
189
+ end
190
+
139
191
  end
data/lib/btree/tree.rb CHANGED
@@ -6,7 +6,6 @@ class Btree::Tree
6
6
  def initialize(degree = 2)
7
7
  @degree = degree
8
8
  @root = Btree::Node.new(@degree)
9
- @root.leaf = true
10
9
  @size = 0
11
10
  end
12
11
 
@@ -15,7 +14,6 @@ class Btree::Tree
15
14
  node = @root
16
15
  if node.full?
17
16
  @root = Btree::Node.new(@degree)
18
- @root.leaf = false
19
17
  @root.add_child(node)
20
18
  @root.split(@root.children.size - 1)
21
19
  #puts "After split, root = #{@root.inspect}"
data/test/test_btree.rb CHANGED
@@ -1,8 +1,50 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'btree'
3
- require 'shoulda'
3
+ require 'shoulda/context'
4
+
5
+ class TestBtree < Minitest::Test
6
+ # Degree 2 tree with a real internal node:
7
+ # root [10, 20]
8
+ # children [5,6,7] [12,17] [30]
9
+ def multi_level_tree
10
+ t = Btree.create(2)
11
+ [10, 20, 5, 6, 12, 30, 7, 17].each {|k| t.insert(k, k.to_s)}
12
+ t
13
+ end
14
+
15
+ def test_range_query_spans_multiple_children
16
+ t = multi_level_tree
17
+ assert_equal %w[5 6 7 10 12 17], t.value_of(5..17)
18
+ end
19
+
20
+ def test_range_query_finds_keys_in_rightmost_child
21
+ t = Btree.create(2)
22
+ (1..10).each {|k| t.insert(k, k.to_s)}
23
+ assert_equal %w[8 9 10], t.value_of(8..10)
24
+ end
25
+
26
+ def test_insert_duplicate_of_internal_key
27
+ t = multi_level_tree
28
+ internal_key = t.root.keys.first
29
+ refute t.root.leaf?, "fixture should have an internal root"
30
+ assert_raises(RuntimeError) do
31
+ t.insert(internal_key, "duplicate")
32
+ end
33
+ end
34
+
35
+ def test_duplicate_of_internal_key_does_not_corrupt_size
36
+ t = multi_level_tree
37
+ internal_key = t.root.keys.first
38
+ size_before = t.size
39
+ begin
40
+ t.insert(internal_key, "duplicate")
41
+ rescue RuntimeError
42
+ # expected once duplicate detection descends past the leaves
43
+ end
44
+ assert_equal size_before, t.size
45
+ assert_equal internal_key.to_s, t.value_of(internal_key)
46
+ end
4
47
 
5
- class TestBtree < Test::Unit::TestCase
6
48
  def test_insert_notfull
7
49
  t = Btree.create(5)
8
50
  t.insert(5, "5")
@@ -26,6 +68,17 @@ class TestBtree < Test::Unit::TestCase
26
68
  end
27
69
  end
28
70
 
71
+ def test_insert_a_lot
72
+ t = Btree.create(2)
73
+ 45.times do |i|
74
+ t.insert i, i*i
75
+ end
76
+ assert_equal 45, t.size
77
+ 45.times do |i|
78
+ assert_equal i*i, t.value_of(i)
79
+ end
80
+ end
81
+
29
82
  def test_value_of
30
83
  t = Btree.create(5)
31
84
  t.insert(1, "foo")
@@ -38,6 +91,7 @@ class TestBtree < Test::Unit::TestCase
38
91
  assert_equal "foo", t.value_of(1)
39
92
  assert_nil t.value_of(11)
40
93
  assert_equal 4, t.size
94
+ assert_equal ["findme", "bar"], t.value_of(3..6)
41
95
  end
42
96
 
43
97
  def test_fill_root
data/version.txt CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 1.0.1
metadata CHANGED
@@ -1,96 +1,61 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: btree
3
- version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 0
9
- - 0
10
- version: 0.0.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Douglas A. Seifert
14
- autorequire:
15
8
  bindir: bin
16
9
  cert_chain: []
17
-
18
- date: 2010-12-20 00:00:00 -08:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- name: bones
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 27
30
- segments:
31
- - 3
32
- - 5
33
- - 4
34
- version: 3.5.4
35
- type: :development
36
- version_requirements: *id001
37
- description: |-
38
- Pure ruby implementation of a btree as described in Introduction to Algorithms by
39
- Cormen, Leiserson, Rivest and Stein, Chapter 18.
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: |
13
+ Pure ruby implementation of a btree as described in Introduction to
14
+ Algorithms by Cormen, Leiserson, Rivest and Stein, Chapter 18.
40
15
  email: doug@dseifert.net
41
16
  executables: []
42
-
43
17
  extensions: []
44
-
45
- extra_rdoc_files:
18
+ extra_rdoc_files:
46
19
  - History.txt
47
- - lib/btree/.node.rb.swp
48
- - version.txt
49
- files:
50
- - .bnsignore
20
+ - README.md
21
+ files:
22
+ - Gemfile
23
+ - Gemfile.lock
51
24
  - History.txt
52
25
  - README.md
53
26
  - Rakefile
27
+ - btree.gemspec
54
28
  - lib/btree.rb
55
- - lib/btree/.node.rb.swp
56
29
  - lib/btree/node.rb
57
30
  - lib/btree/tree.rb
58
31
  - test/test_btree.rb
59
32
  - version.txt
60
- has_rdoc: true
61
- homepage: http://www.dseifert.net/btree
62
- licenses: []
63
-
64
- post_install_message:
65
- rdoc_options:
66
- - --main
33
+ homepage: https://github.com/seifertd/Ruby-BTree
34
+ licenses:
35
+ - MIT
36
+ metadata:
37
+ homepage_uri: https://github.com/seifertd/Ruby-BTree
38
+ source_code_uri: https://github.com/seifertd/Ruby-BTree
39
+ changelog_uri: https://github.com/seifertd/Ruby-BTree/blob/master/History.txt
40
+ bug_tracker_uri: https://github.com/seifertd/Ruby-BTree/issues
41
+ rubygems_mfa_required: 'true'
42
+ rdoc_options:
43
+ - "--main"
67
44
  - README.md
68
- require_paths:
45
+ require_paths:
69
46
  - lib
70
- required_ruby_version: !ruby/object:Gem::Requirement
71
- none: false
72
- requirements:
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
73
49
  - - ">="
74
- - !ruby/object:Gem::Version
75
- hash: 3
76
- segments:
77
- - 0
78
- version: "0"
79
- required_rubygems_version: !ruby/object:Gem::Requirement
80
- none: false
81
- requirements:
50
+ - !ruby/object:Gem::Version
51
+ version: 2.3.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
82
54
  - - ">="
83
- - !ruby/object:Gem::Version
84
- hash: 3
85
- segments:
86
- - 0
87
- version: "0"
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
88
57
  requirements: []
89
-
90
- rubyforge_project: btree
91
- rubygems_version: 1.3.7
92
- signing_key:
93
- specification_version: 3
94
- summary: Pure ruby implementation of a btree as described in Introduction to Algorithms by Cormen, Leiserson, Rivest and Stein, Chapter 18.
95
- test_files:
96
- - test/test_btree.rb
58
+ rubygems_version: 4.0.17
59
+ specification_version: 4
60
+ summary: Pure ruby implementation of a btree
61
+ test_files: []
data/.bnsignore DELETED
@@ -1,18 +0,0 @@
1
- # The list of files that should be ignored by Mr Bones.
2
- # Lines that start with '#' are comments.
3
- #
4
- # A .gitignore file can be used instead by setting it as the ignore
5
- # file in your Rakefile:
6
- #
7
- # Bones {
8
- # ignore_file '.gitignore'
9
- # }
10
- #
11
- # For a project with a C extension, the following would be a good set of
12
- # exclude patterns (uncomment them if you want to use them):
13
- # *.[oa]
14
- # *~
15
- announcement.txt
16
- coverage
17
- doc
18
- pkg
Binary file