btree 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.bnsignore ADDED
@@ -0,0 +1,18 @@
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
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.0 / 2010-12-20
2
+
3
+ * 1 major enhancement
4
+ * Initial Release!
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ Ruby-BTree
2
+ ==========
3
+
4
+ Pure ruby implementation of a btree as described in Introduction to Algorithms by
5
+ Cormen, Leiserson, Rivest and Stein, Chapter 18.
6
+
7
+ Features
8
+ --------
9
+
10
+ * Create B-Tree of arbitrary degree (2 by default)
11
+ * Insert key value pairs using a Map like interface
12
+ * Query for values associated with keys using a Map like interface
13
+ * Keys can be any object supporting comparison operators: <, > and ==
14
+
15
+ Examples
16
+ --------
17
+
18
+ require 'btree'
19
+ tree = Btree.create # default degree = 2
20
+ tree = Btree.create(5) # degree = 5
21
+ tree['foo'] = 'foo value'
22
+ tree['bar'] = 'bar value'
23
+
24
+ puts "key 'foo' has value: #{tree['foo']}"
25
+ puts "BTree has #{tree.size} key-value pairs"
26
+
27
+ Future
28
+ ------
29
+
30
+ * Deletion is not implemented. TODO: Implement deletion
31
+ * Attempt to insert existing key raises RuntimeError.
32
+ TODO: Allow replacement of value associated with existing key instead of raising RuntimeError.
33
+ * Persist state to backing store
34
+
35
+ Install
36
+ -------
37
+
38
+ * gem install btree
39
+
40
+ Author
41
+ ------
42
+
43
+ Original author: Douglas A. Seifert (doug@dseifert.net)
44
+
45
+ License
46
+ -------
47
+
48
+ (The MIT License)
49
+
50
+ Copyright (c) 2010,2011 Douglas A. Seifert
51
+
52
+ Permission is hereby granted, free of charge, to any person obtaining
53
+ a copy of this software and associated documentation files (the
54
+ 'Software'), to deal in the Software without restriction, including
55
+ without limitation the rights to use, copy, modify, merge, publish,
56
+ distribute, sublicense, and/or sell copies of the Software, and to
57
+ permit persons to whom the Software is furnished to do so, subject to
58
+ the following conditions:
59
+
60
+ The above copyright notice and this permission notice shall be
61
+ included in all copies or substantial portions of the Software.
62
+
63
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
64
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
65
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
66
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
67
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
68
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
69
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+
2
+ begin
3
+ require 'bones'
4
+ rescue LoadError
5
+ abort '### Please install the "bones" gem ###'
6
+ end
7
+
8
+ task :default => 'test:run'
9
+ task 'gem:release' => 'test:run'
10
+
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
+ }
18
+
data/lib/btree.rb ADDED
@@ -0,0 +1,70 @@
1
+ # :main: README.md
2
+ module Btree
3
+
4
+ # :stopdoc:
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ @version ||= File.read(path('version.txt')).strip
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args, &block )
20
+ rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ if block
22
+ begin
23
+ $LOAD_PATH.unshift LIBPATH
24
+ rv = block.call
25
+ ensure
26
+ $LOAD_PATH.shift
27
+ end
28
+ end
29
+ return rv
30
+ end
31
+
32
+ # Returns the lpath for the module. If any arguments are given,
33
+ # they will be joined to the end of the path using
34
+ # <tt>File.join</tt>.
35
+ #
36
+ def self.path( *args, &block )
37
+ rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
38
+ if block
39
+ begin
40
+ $LOAD_PATH.unshift PATH
41
+ rv = block.call
42
+ ensure
43
+ $LOAD_PATH.shift
44
+ end
45
+ end
46
+ return rv
47
+ end
48
+
49
+ # Utility method used to require all files ending in .rb that lie in the
50
+ # directory below this file that has the same name as the filename passed
51
+ # in. Optionally, a specific _directory_ name can be passed in such that
52
+ # the _filename_ does not have to be equivalent to the directory.
53
+ #
54
+ def self.require_all_libs_relative_to( fname, dir = nil )
55
+ dir ||= ::File.basename(fname, '.*')
56
+ search_me = ::File.expand_path(
57
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
58
+
59
+ Dir.glob(search_me).sort.each {|rb| require rb}
60
+ end
61
+
62
+ def self.create(degree)
63
+ raise "Degree of Btree must be >= 2" if degree < 2
64
+ return Btree::Tree.new(degree)
65
+ end
66
+
67
+ end # module Btree
68
+
69
+ Btree.require_all_libs_relative_to(__FILE__)
70
+
Binary file
data/lib/btree/node.rb ADDED
@@ -0,0 +1,139 @@
1
+ class Btree::Node
2
+ attr_accessor :leaf
3
+ def initialize(degree)
4
+ @degree = degree
5
+ @keys = []
6
+ @children = []
7
+ @leaf = true
8
+ end
9
+
10
+ def dump(level = 0)
11
+ @keys.each_with_index do |key, idx|
12
+ if @children[idx]
13
+ @children[idx].dump(level + 1)
14
+ end
15
+ puts "#{level}: #{key.first}: full? #{full?} leaf? #{leaf?}"
16
+ end
17
+ (@children[@keys.size..-1] || []).each do |c|
18
+ c.dump(level+1)
19
+ end
20
+ nil
21
+ end
22
+
23
+ def add_child(node)
24
+ @children << node
25
+ end
26
+
27
+ def children
28
+ @children.dup.freeze
29
+ end
30
+
31
+ def keys
32
+ @keys.map(&:first).freeze
33
+ end
34
+
35
+ def values
36
+ @keys.map(&:last).freeze
37
+ end
38
+
39
+ def full?
40
+ size >= 2 * @degree - 1
41
+ end
42
+
43
+ def leaf?
44
+ @leaf
45
+ end
46
+
47
+ def size
48
+ @keys.size
49
+ end
50
+
51
+ def value_of(key)
52
+ i = 1
53
+ while i <= size && key > @keys[i-1].first
54
+ i += 1
55
+ end
56
+
57
+ if i <= size && key == @keys[i-1].first
58
+ return @keys[i-1].last
59
+ elsif leaf?
60
+ return nil
61
+ else
62
+ return @children[i-1].value_of(key)
63
+ end
64
+ end
65
+
66
+ def insert(key, value)
67
+ i = size - 1
68
+ #puts "INSERTING #{key} INTO NODE: #{self.inspect}"
69
+ 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]
76
+ else
77
+ while i >= 0 && @keys[i] && key < @keys[i].first
78
+ i -= 1
79
+ 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)
88
+ end
89
+ end
90
+
91
+ def split(child_idx)
92
+ raise "Invalid child index #{child_idx} in split, num_children = #{@children.size}" if child_idx < 0 || child_idx >= @children.size
93
+ #puts "SPLIT1: #{self.inspect}"
94
+ splitee = @children[child_idx]
95
+ y = Btree::Node.new(@degree)
96
+ z = Btree::Node.new(@degree)
97
+ z.leaf = splitee.leaf
98
+ (@degree-1).times do |j|
99
+ z._keys[j] = splitee._keys[j+@degree]
100
+ y._keys[j] = splitee._keys[j]
101
+ end
102
+ if !splitee.leaf?
103
+ @degree.times do |j|
104
+ z._children[j] = splitee._children[j+@degree]
105
+ y._children[j] = splitee._children[j]
106
+ end
107
+ end
108
+ mid_val = splitee._keys[@degree-1]
109
+ #puts "SPLIT2: #{self.inspect}"
110
+ (@keys.size).downto(child_idx) do |j|
111
+ @children[j+1] = @children[j]
112
+ end
113
+
114
+ @children[child_idx+1] = z
115
+ @children[child_idx] = y
116
+
117
+ #puts "SPLIT3: #{self.inspect}"
118
+
119
+ (@keys.size - 1).downto(child_idx) do |j|
120
+ @keys[j+1] = @keys[j]
121
+ end
122
+
123
+ #puts "SPLIT4: #{self.inspect}"
124
+
125
+ @keys[child_idx] = mid_val
126
+ #puts "SPLIT5: #{self.inspect}"
127
+ end
128
+
129
+ protected
130
+
131
+ def _keys
132
+ @keys
133
+ end
134
+
135
+ def _children
136
+ @children
137
+ end
138
+
139
+ end
data/lib/btree/tree.rb ADDED
@@ -0,0 +1,46 @@
1
+ class Btree::Tree
2
+ attr_reader :root, :degree, :size
3
+
4
+ # Creates a BTree of degree 2 by default. Keys
5
+ # Must support being compared using >, < and == methods.
6
+ def initialize(degree = 2)
7
+ @degree = degree
8
+ @root = Btree::Node.new(@degree)
9
+ @root.leaf = true
10
+ @size = 0
11
+ end
12
+
13
+ # Insert a key-value pair into the btree
14
+ def insert(key, value = nil)
15
+ node = @root
16
+ if node.full?
17
+ @root = Btree::Node.new(@degree)
18
+ @root.leaf = false
19
+ @root.add_child(node)
20
+ @root.split(@root.children.size - 1)
21
+ #puts "After split, root = #{@root.inspect}"
22
+ # split child(@root, 1)
23
+ node = @root
24
+ end
25
+ node.insert(key, value)
26
+ @size += 1
27
+ return self
28
+ end
29
+
30
+ # puts internal state
31
+ def dump
32
+ @root.dump
33
+ end
34
+
35
+ # Get value associated with the specified key
36
+ def value_of(key)
37
+ @root.value_of(key)
38
+ end
39
+
40
+ # Support map like access
41
+ alias_method :"[]", :value_of
42
+
43
+ # Support map like key-value setting
44
+ alias_method :"[]=", :insert
45
+
46
+ end
@@ -0,0 +1,110 @@
1
+ require 'test/unit'
2
+ require 'btree'
3
+ require 'shoulda'
4
+
5
+ class TestBtree < Test::Unit::TestCase
6
+ def test_insert_notfull
7
+ t = Btree.create(5)
8
+ t.insert(5, "5")
9
+ assert_equal 1, t.root.size
10
+ assert !t.root.full?
11
+ assert_equal 5, t.degree
12
+ assert_equal 1, t.size
13
+ end
14
+
15
+ def test_degree_too_small
16
+ assert_raises(RuntimeError) do
17
+ Btree.create(1)
18
+ end
19
+ end
20
+
21
+ def test_insert_duplicate_key
22
+ t = Btree.create(2)
23
+ t.insert(1, "1")
24
+ assert_raises(RuntimeError) do
25
+ t.insert(1, "1")
26
+ end
27
+ end
28
+
29
+ def test_value_of
30
+ t = Btree.create(5)
31
+ t.insert(1, "foo")
32
+ t.insert(5, "bar")
33
+ t.insert(7, "baz")
34
+ t.insert(3, "findme")
35
+ assert_equal "findme", t.value_of(3)
36
+ assert_equal "baz", t.value_of(7)
37
+ assert_equal "bar", t.value_of(5)
38
+ assert_equal "foo", t.value_of(1)
39
+ assert_nil t.value_of(11)
40
+ assert_equal 4, t.size
41
+ end
42
+
43
+ def test_fill_root
44
+ t = Btree.create(2)
45
+ 3.times {|n| t.insert(n, n.to_s)}
46
+ assert t.root.full?
47
+ assert_equal [0,1,2], t.root.keys
48
+ assert_equal ['0','1','2'], t.root.values
49
+ assert_equal 3, t.size
50
+ end
51
+
52
+ context 'full root' do
53
+ setup do
54
+ @t = Btree.create(2)
55
+ 3.times {|n| @t.insert(n*2, n.to_s)}
56
+ end
57
+ should "be able to insert at end" do
58
+ @t.insert(10, "10")
59
+ assert_equal "10", @t.value_of(10)
60
+ assert_equal "0", @t.value_of(0)
61
+ assert_equal "1", @t.value_of(2)
62
+ assert_equal "2", @t.value_of(4)
63
+ assert_equal 4, @t.size
64
+ end
65
+ should "be able to insert at front" do
66
+ @t.insert(-1, "10")
67
+ assert_equal "10", @t.value_of(-1)
68
+ assert_equal "0", @t.value_of(0)
69
+ assert_equal "1", @t.value_of(2)
70
+ assert_equal "2", @t.value_of(4)
71
+ assert_equal 4, @t.size
72
+ end
73
+ should "be able to insert in the middle" do
74
+ @t.insert(3, "10")
75
+ assert_equal "10", @t.value_of(3)
76
+ assert_equal "0", @t.value_of(0)
77
+ assert_equal "1", @t.value_of(2)
78
+ assert_equal "2", @t.value_of(4)
79
+ assert_equal 4, @t.size
80
+ end
81
+ context 'full last child' do
82
+ setup do
83
+ 2.times {|n| @t.insert(n*2 + 10, "foo") }
84
+ end
85
+ should "have full last child" do
86
+ assert @t.root.children.last.full?, "Last child of root should be full"
87
+ assert_equal 5, @t.size
88
+ end
89
+ should "be able to add to end" do
90
+ @t.insert(100, "YEAH!")
91
+ assert_equal "YEAH!", @t.value_of(100)
92
+ assert_equal 6, @t.size
93
+ end
94
+ should "be able to insert many" do
95
+ 10.times { |n| @t.insert((n+1) * 100, "YEAH!") }
96
+ assert_equal "YEAH!", @t.value_of(100)
97
+ assert_equal 15, @t.size
98
+ end
99
+ end
100
+ context 'second split of root' do
101
+ setup do
102
+ 6.times {|n| @t.insert(n*2 + 10, "foo") }
103
+ end
104
+ should "work" do
105
+ assert_equal 'foo', @t.value_of(10)
106
+ assert_equal 9, @t.size
107
+ end
108
+ end
109
+ end
110
+ end
data/version.txt ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
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
11
+ platform: ruby
12
+ authors:
13
+ - Douglas A. Seifert
14
+ autorequire:
15
+ bindir: bin
16
+ 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.
40
+ email: doug@dseifert.net
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files:
46
+ - History.txt
47
+ - lib/btree/.node.rb.swp
48
+ - version.txt
49
+ files:
50
+ - .bnsignore
51
+ - History.txt
52
+ - README.md
53
+ - Rakefile
54
+ - lib/btree.rb
55
+ - lib/btree/.node.rb.swp
56
+ - lib/btree/node.rb
57
+ - lib/btree/tree.rb
58
+ - test/test_btree.rb
59
+ - 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
67
+ - README.md
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
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:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ 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