renaudb-batfish 0.1.0

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.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = Batfish
2
+
3
+ Just a bunch of functions.
4
+
5
+ Batfish is a collection of useful algorithms and data structures that I developed through the years for various projects. I decided to publish in case my hard drive comes to crash and for everyone to use.
6
+
7
+ == Install
8
+
9
+ If you haven't already, add github's gem server to your sources:
10
+
11
+ gem sources -a http://gems.github.com
12
+
13
+ Then, it's as easy as:
14
+
15
+ sudo gem install renaudb-batfish
16
+
17
+ And voila! You are now setup to use batfish in any of your projects.
18
+
19
+ == Usage
20
+
21
+ To use batfish in one of your project, all you have to do is to include the library.
22
+
23
+ require 'batfish'
24
+
25
+ And then use the classes like any other ruby class.
26
+
27
+ class Dictionary
28
+ attr_accessor :words
29
+
30
+ def initialize
31
+ words = Batfish::Trie.new()
32
+ end
33
+ end
34
+
35
+ == Content
36
+
37
+ Here is a list of the algorithms and data structures batfish implements.
38
+
39
+ === Data Structures
40
+
41
+ - BK-Tree
42
+ - Trie
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "batfish"
9
+ gemspec.summary = "Just a bunch of functions."
10
+ gemspec.email = "me@renaudbourassa.com"
11
+ gemspec.homepage = "http://github.com/renaudb/batfish"
12
+ gemspec.authors = ["Renaud Bourassa"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ Rake::TestTask.new do |t|
19
+ t.test_files = FileList['test/test*.rb']
20
+ t.verbose = true
21
+ end
22
+
23
+ Rake::RDocTask.new do |t|
24
+ t.main = "README.rdoc"
25
+ t.rdoc_files.include("README.rdoc", "lib/**/*.rb")
26
+ t.rdoc_dir = "doc"
27
+ end
28
+
29
+
30
+
31
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/batfish.gemspec ADDED
@@ -0,0 +1,49 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{batfish}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Renaud Bourassa"]
12
+ s.date = %q{2009-08-23}
13
+ s.email = %q{me@renaudbourassa.com}
14
+ s.extra_rdoc_files = [
15
+ "README.rdoc"
16
+ ]
17
+ s.files = [
18
+ "README.rdoc",
19
+ "Rakefile",
20
+ "VERSION",
21
+ "batfish.gemspec",
22
+ "lib/batfish.rb",
23
+ "lib/data/bktree.rb",
24
+ "lib/data/trie.rb",
25
+ "test/helper.rb",
26
+ "test/test_bktree.rb",
27
+ "test/test_trie.rb"
28
+ ]
29
+ s.homepage = %q{http://github.com/renaudb/batfish}
30
+ s.rdoc_options = ["--charset=UTF-8"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.3.3}
33
+ s.summary = %q{Just a bunch of functions.}
34
+ s.test_files = [
35
+ "test/helper.rb",
36
+ "test/test_bktree.rb",
37
+ "test/test_trie.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
+ else
46
+ end
47
+ else
48
+ end
49
+ end
data/lib/batfish.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'data/trie'
2
+ require 'data/bktree'
@@ -0,0 +1,139 @@
1
+ module Batfish
2
+
3
+ # A BK-tree is a datastructure used for
4
+ # nearest neighbor lookup in a metric space.
5
+ # A metric space is any space with the
6
+ # following properties:
7
+ #
8
+ # * d(x,y) = 0 <=> x = y
9
+ # * d(x,y) = d(y,x)
10
+ # * d(x,z) <= d(x,y) + d(y,z)
11
+ #
12
+ # To use this datastructure, the objects added
13
+ # to it must have a distance_to(other) class method
14
+ # defined.
15
+ class BKTree
16
+ attr_reader :root
17
+
18
+ # Initialize a BKTree.
19
+ def initialize
20
+ @root = nil
21
+ end
22
+
23
+ # Add an object to the tree.
24
+ # Returns the tree itself so several
25
+ # adds may be chained together.
26
+ def add(obj)
27
+ if @root
28
+ @root.add(obj)
29
+ else
30
+ @root = Node.new(obj)
31
+ end
32
+ return self
33
+ end
34
+ alias_method :<<, :add
35
+
36
+ # Returns true if the tree is empty.
37
+ def empty?
38
+ return @root.nil? ? true : false
39
+ end
40
+
41
+ # Returns true if the given object is
42
+ # present in the tree.
43
+ def include?(obj)
44
+ self.include_near?(obj, 0)
45
+ end
46
+
47
+ # Return true if the given object is
48
+ # within threshold distance of an
49
+ # object in the tree.
50
+ def include_near?(obj, threshold)
51
+ self.fetch_near(obj, threshold) do
52
+ return true
53
+ end
54
+ return false
55
+ end
56
+
57
+ # Returns an array containing all
58
+ # object within threshold distance
59
+ # of the given object. If a block is
60
+ # given, it is called once for each
61
+ # objects within threshold distance
62
+ # of the given object.
63
+ def fetch_near(obj, threshold)
64
+ objs = []
65
+ @root.fetch_near(obj, threshold) do |item|
66
+ yield(item) if block_given?
67
+ objs << item
68
+ end
69
+ return objs
70
+ end
71
+
72
+ # Returns the nearest object to
73
+ # the given object.
74
+ def fetch_nearest(obj)
75
+ threshold = 0
76
+ nearests = []
77
+ while nearests.empty?
78
+ self.fetch_near(obj, threshold) do |item|
79
+ yield(item) if block_given?
80
+ nearests << item
81
+ end
82
+ threshold += 1
83
+ end
84
+ return nearests
85
+ end
86
+
87
+ # Returns the minimum distance
88
+ # between the given object and
89
+ # an object in the tree.
90
+ def min_dist(obj)
91
+ threshold = 0
92
+ while !self.include_near?(obj, threshold)
93
+ threshold += 1
94
+ end
95
+ return threshold
96
+ end
97
+
98
+ # This class represents a node
99
+ # in a BKTree.
100
+ class Node
101
+ attr_reader :value, :children
102
+
103
+ # Initialize a BKTree Node with
104
+ # the given object as its value
105
+ # or nil if no object is given.
106
+ def initialize(obj=nil)
107
+ @value = obj
108
+ @children = {}
109
+ end
110
+
111
+ # Add a node to a node in
112
+ # a recursive way.
113
+ def add(obj)
114
+ distance = obj.distance_to(@value)
115
+ if child = @children[distance]
116
+ child.add(obj)
117
+ else
118
+ @children[distance] = Node.new(obj)
119
+ end
120
+ end
121
+
122
+ # Calls block once for each
123
+ # object within threshold distance
124
+ # of the given object that are
125
+ # part of a child node of self.
126
+ def fetch_near(obj, threshold, &block)
127
+ distance = @value.distance_to(obj)
128
+ if distance <= threshold
129
+ yield @value
130
+ end
131
+ ((distance - threshold)..(distance + threshold)).each do |d|
132
+ if child = @children[d]
133
+ child.fetch_near(obj, threshold, &block)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
data/lib/data/trie.rb ADDED
@@ -0,0 +1,151 @@
1
+ module Batfish
2
+
3
+ # A Trie is a datastructure used to
4
+ # store key value pairs where the key
5
+ # is a string. It does so by through
6
+ # a tree-like structure where each edge
7
+ # as a character associated to it.
8
+ # To walk through the trie, you go down
9
+ # one character of the key at a time
10
+ # until you reach the end of the key.
11
+ class Trie
12
+ attr_reader :root
13
+
14
+ # Initialize a Trie
15
+ def initialize
16
+ @root = Node.new()
17
+ end
18
+
19
+ # Adds a key value pair to
20
+ # the trie. Returns the trie
21
+ # itself so calls to add may be
22
+ # chained together.
23
+ def add(key, value)
24
+ @root.add(key.to_s.upcase.split(""), value)
25
+ return self
26
+ end
27
+ alias_method :[]=, :add
28
+
29
+ # Deletes the given key value pair
30
+ # from the trie. Returns the trie
31
+ # itself so several delete may
32
+ # be chained.
33
+ def delete(key)
34
+ @root.delete(key.to_s.upcase.split(""))
35
+ return self
36
+ end
37
+
38
+ # Returns the value associated with
39
+ # the given key. Returns nil if it
40
+ # is not found.
41
+ def fetch(key)
42
+ @root.fetch(key.upcase.split(""))
43
+ end
44
+ alias_method :[], :fetch
45
+
46
+ # Returns true if the trie contains
47
+ # the given key.
48
+ def has_key?(key)
49
+ return self.fetch(key) ? true : false
50
+ end
51
+
52
+ # Returns true if the trie contains
53
+ # the given value.
54
+ def has_value?(value)
55
+ self.each do |k, v|
56
+ return true if v == value
57
+ end
58
+ return false
59
+ end
60
+
61
+ # Returns true if the trie is empty.
62
+ def empty?
63
+ return @root.children.empty? ? true : false
64
+ end
65
+
66
+ # Calls block once for each key value
67
+ # pair in the trie.
68
+ def each
69
+ @root.each("") do |k, v|
70
+ yield(k, v)
71
+ end
72
+ end
73
+
74
+ # This class represents a node in
75
+ # a Trie.
76
+ class Node
77
+ attr_reader :value, :children
78
+
79
+ # Initialize a Trie Node with
80
+ # the given object as its value
81
+ # or nil if no object is given.
82
+ def initialize(value=nil)
83
+ @value = value
84
+ @children = {}
85
+ end
86
+
87
+ # Add a node to a node in a
88
+ # recursive way.
89
+ def add(key, value)
90
+ if key.empty?
91
+ @value = value
92
+ else
93
+ letter = key.shift
94
+ if !@children[letter]
95
+ @children[letter] = Node.new()
96
+ end
97
+ @children[letter].add(key, value)
98
+ end
99
+ end
100
+
101
+ # Delete a key value pair from
102
+ # a node in a recursive way.
103
+ def delete(key)
104
+ if key.empty?
105
+ if !@value.nil? and @children.empty?
106
+ @value = nil
107
+ return true
108
+ else
109
+ @value = nil
110
+ return false
111
+ end
112
+ end
113
+ letter = key.shift
114
+ if @children[letter].delete(key)
115
+ @children.delete(letter)
116
+ if @children.empty?
117
+ return true
118
+ end
119
+ end
120
+ return false
121
+ end
122
+
123
+ # Returns the value associated
124
+ # with key. Returns nil if it
125
+ # is not found.
126
+ def fetch(key)
127
+ if key.empty?
128
+ return @value
129
+ end
130
+ child = @children[key.shift]
131
+ if child
132
+ child.fetch(key)
133
+ else
134
+ return nil
135
+ end
136
+ end
137
+
138
+ # Calls block once for each key
139
+ # value pairs under self.
140
+ def each(key, &block)
141
+ if value
142
+ yield(key, value)
143
+ end
144
+ @children.each do |letter, child|
145
+ child.each(key + letter, &block)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+
data/test/helper.rb ADDED
@@ -0,0 +1 @@
1
+ require 'lib/batfish'
@@ -0,0 +1,84 @@
1
+ require 'test/unit'
2
+ require 'test/helper'
3
+
4
+ include Batfish
5
+
6
+ class Numeric
7
+ def distance_to(other)
8
+ return (self - other).abs
9
+ end
10
+ end
11
+
12
+ class TestBKTree < Test::Unit::TestCase
13
+
14
+ def setup
15
+ @t = BKTree.new()
16
+ end
17
+
18
+ def test_empty?
19
+ assert(@t.empty?)
20
+ end
21
+
22
+ def test_include?
23
+ @t.add(1)
24
+ @t.add(4)
25
+ @t.add(5)
26
+ @t.add(9)
27
+ @t.add(12)
28
+ assert(@t.include?(5))
29
+ assert(!@t.include?(6))
30
+ end
31
+
32
+ def test_include_near?
33
+ @t.add(1)
34
+ @t.add(4)
35
+ @t.add(5)
36
+ @t.add(9)
37
+ @t.add(12)
38
+ assert(@t.include_near?(11, 1))
39
+ assert(!@t.include_near?(20, 5))
40
+ end
41
+
42
+ def test_fetch_near
43
+ @t.add(1)
44
+ @t.add(4)
45
+ @t.add(5)
46
+ @t.add(9)
47
+ @t.add(12)
48
+ assert_equal([1,4,5,9,12], @t.fetch_near(0, 20).sort)
49
+ assert_equal([1,4,5], @t.fetch_near(0, 5).sort)
50
+ result = []
51
+ @t.fetch_near(0, 5) do |v|
52
+ result << v
53
+ end
54
+ assert_equal([1,4,5], result.sort)
55
+ end
56
+
57
+ def test_min_dist
58
+ @t.add(1)
59
+ @t.add(4)
60
+ @t.add(5)
61
+ @t.add(9)
62
+ @t.add(12)
63
+ assert_equal(0, @t.min_dist(4))
64
+ assert_equal(0, @t.min_dist(12))
65
+ assert_equal(8, @t.min_dist(20))
66
+ assert_equal(2, @t.min_dist(-1))
67
+ end
68
+
69
+ def test_fetch_nearest
70
+ @t.add(1)
71
+ @t.add(4)
72
+ @t.add(5)
73
+ @t.add(9)
74
+ @t.add(11)
75
+ assert_equal([4], @t.fetch_nearest(4))
76
+ assert_equal([4], @t.fetch_nearest(3))
77
+ assert_equal([9,11], @t.fetch_nearest(10).sort)
78
+ result = []
79
+ @t.fetch_nearest(10) do |v|
80
+ result << v
81
+ end
82
+ assert_equal([9,11], result.sort)
83
+ end
84
+ end
data/test/test_trie.rb ADDED
@@ -0,0 +1,97 @@
1
+ require 'test/unit'
2
+ require 'test/helper'
3
+
4
+ include Batfish
5
+
6
+ class TestTrie < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @t = Trie.new()
10
+ end
11
+
12
+ def test_initialize
13
+ assert(@t.root.children.empty?)
14
+ end
15
+
16
+ def test_delete
17
+ @t["dinosaur"] = 0
18
+ @t["diplodocus"] = 1
19
+ @t["diplotomodon"] = 2
20
+ @t["raptor"] = 3
21
+ @t["tyrannosaurus"] = 4
22
+ @t["tyrannotitan"] = 5
23
+ @t["velociraptor"] = 5
24
+ @t.delete("tyrannosaurus")
25
+ assert(!@t.has_key?("tyrannosaurus"))
26
+ assert(@t.has_key?("tyrannotitan"))
27
+ @t.delete("diplo")
28
+ assert(@t.has_key?("diplodocus"))
29
+ assert(@t.has_key?("diplotomodon"))
30
+ @t.delete("raptor")
31
+ assert(!@t.has_key?("raptor"))
32
+ assert(@t.has_key?("velociraptor"))
33
+ end
34
+
35
+ def test_fetch
36
+ @t["dinosaur"] = 0
37
+ @t["diplodocus"] = 1
38
+ @t["diplotomodon"] = 2
39
+ @t["raptor"] = 3
40
+ @t["tyrannosaurus"] = 4
41
+ @t["tyrannotitan"] = 5
42
+ assert_equal(0, @t.fetch("dinosaur"))
43
+ assert_equal(1, @t.fetch("DiPlOdOcUs"))
44
+ assert_equal(nil, @t.fetch("velociraptor"))
45
+ assert_equal(nil, @t.fetch("tyranno"))
46
+ end
47
+
48
+ def test_has_key?
49
+ @t.add("dinosaur", 0)
50
+ @t.add("diplodocus", 1)
51
+ @t.add("diplotomodon", 2)
52
+ @t.add("raptor", 3)
53
+ @t.add("tyrannosaurus", 4)
54
+ @t.add("tyrannotitan", 5)
55
+ assert(@t.has_key?("dinosaur"))
56
+ assert(@t.has_key?("DiPlOdOcUs"))
57
+ assert(!@t.has_key?("velociraptor"))
58
+ assert(!@t.has_key?("tyranno"))
59
+ end
60
+
61
+ def test_has_value?
62
+ @t.add("dinosaur", 0)
63
+ @t.add("diplodocus", 1)
64
+ @t.add("diplotomodon", 2)
65
+ @t.add("raptor", 3)
66
+ @t.add("tyrannosaurus", 4)
67
+ @t.add("tyrannotitan", 5)
68
+ assert(@t.has_value?(0))
69
+ assert(@t.has_value?(5))
70
+ assert(!@t.has_value?(99))
71
+ assert(!@t.has_value?("dinosaur"))
72
+ end
73
+
74
+ def test_empty?
75
+ assert(@t.empty?)
76
+ @t.add("dinosaur", 0)
77
+ @t.add("diplodocus", 1)
78
+ assert(!@t.empty?)
79
+ @t.delete("dinosaur")
80
+ @t.delete("diplodocus")
81
+ assert(@t.empty?)
82
+ end
83
+
84
+ def test_each
85
+ @t.add("dinosaur", 0)
86
+ @t.add("tyrannosaurus", 4)
87
+ @t.add("diplotomodon", 2)
88
+ @t.add("tyrannotitan", 5)
89
+ @t.add("raptor", 3)
90
+ @t.add("diplodocus", 1)
91
+ i = 0
92
+ @t.each do |k, v|
93
+ assert_equal(i, v)
94
+ i += 1
95
+ end
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: renaudb-batfish
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Renaud Bourassa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-23 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: me@renaudbourassa.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - README.rdoc
26
+ - Rakefile
27
+ - VERSION
28
+ - batfish.gemspec
29
+ - lib/batfish.rb
30
+ - lib/data/bktree.rb
31
+ - lib/data/trie.rb
32
+ - test/helper.rb
33
+ - test/test_bktree.rb
34
+ - test/test_trie.rb
35
+ has_rdoc: false
36
+ homepage: http://github.com/renaudb/batfish
37
+ licenses:
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.5
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Just a bunch of functions.
62
+ test_files:
63
+ - test/helper.rb
64
+ - test/test_bktree.rb
65
+ - test/test_trie.rb