binary_decision_tree 0.0.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61ea2ac1f016a28ef9c96ce8e61f08231876764a
4
- data.tar.gz: a4bcc60c2d5df5ebdb587708fb659ab357fe3333
3
+ metadata.gz: cd430f35feb7cf78297db1208f4678ad0cb192f3
4
+ data.tar.gz: 4777835269c7eca78d13d3694e11070c2b8f5729
5
5
  SHA512:
6
- metadata.gz: b15ee21a0e80ef48c4864159959f3916f17816c3d9148c6c9fb4c7b815b16d8bc580808846f291d97e600fbbeaf86478437e31565641b8bbcccd28fad9479710
7
- data.tar.gz: 2fcec32b0ba97197c44db1446003f3dda074b164c4d68f3a1309f20527de0d25789c29e05bf7abb155217cb275ebc789c33bcfeef442533907a73b3132af6aa2
6
+ metadata.gz: 7998bc2ad37b1d2b0652ac4b4117e199679a64b2594cafa3a2594e2c3b92c71e8b4c2b1a8b330f60c21d60b40d75cf546517d27d3f9c89aacd2bc370773fd81e
7
+ data.tar.gz: 000651f02ad554ab4ab1350936e16bba13e54f47cb8a417301c70bd9fbfcfd209f2fbf6a326c97db9162e2e9cc4fb6bb775c80607fe77377d281ac2d0bb582db
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ #- 2.1.0 not working on travis ci
5
+ - 2.0.0
6
+ - jruby-19mode
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in binary_decision_tree.gemspec
4
4
  gemspec
5
+
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :minitest do
2
+ watch(%r{^test/(.*)_test\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
4
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
5
+ end
data/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # BinaryDecisionTree
2
2
 
3
+ [![Build Status](https://travis-ci.org/haruska/binary_decision_tree.svg?branch=master)](https://travis-ci.org/haruska/binary_decision_tree)
4
+ [![Coverage Status](https://coveralls.io/repos/haruska/binary_decision_tree/badge.png?branch=master)](https://coveralls.io/r/haruska/binary_decision_tree?branch=master)
5
+ [![Gem Version](https://badge.fury.io/rb/binary_decision_tree.svg)](http://badge.fury.io/rb/binary_decision_tree)
6
+ [![Dependency Status](https://gemnasium.com/haruska/binary_decision_tree.svg)](https://gemnasium.com/haruska/binary_decision_tree)
7
+ [![Code Climate](https://codeclimate.com/github/haruska/binary_decision_tree.png)](https://codeclimate.com/github/haruska/binary_decision_tree)
8
+
3
9
  A binary tree designed to record decisions based on child nodes. This data structure is useful
4
10
  in things like single elimination tournaments. They can be marshalled and unmarshalled to two
5
11
  numbers.
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'test'
8
+ t.pattern = "test/**/*_test.rb"
9
+ end
@@ -20,4 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.5"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "coveralls"
24
+ spec.add_development_dependency "guard"
25
+ spec.add_development_dependency "guard-minitest"
23
26
  end
@@ -1,45 +1,47 @@
1
- class BinaryDecisionTree::MarshalledTree
2
- attr_reader :depth
3
- attr_reader :decisions
4
- attr_reader :mask
5
-
6
- def initialize(depth, decisions, mask)
7
- @depth = depth
8
- @decisions = decisions
9
- @mask = mask
10
- end
1
+ module BinaryDecisionTree
2
+ class MarshalledTree
3
+ attr_reader :depth
4
+ attr_reader :decisions
5
+ attr_reader :mask
6
+
7
+ def initialize(depth, decisions, mask)
8
+ @depth = depth
9
+ @decisions = decisions
10
+ @mask = mask
11
+ end
12
+
13
+ def self.from_tree(tree)
14
+ depth = tree.depth
15
+ decisions = 0
16
+ mask = 0
11
17
 
12
- def self.from_tree(tree)
13
- depth = tree.depth
14
- decisions = 0
15
- mask = 0
16
-
17
- (2**tree.depth).times do |i|
18
- next if i == 0
19
- node = tree.at(i)
20
- if !node.decision.nil?
21
- mask |= 1 << i
22
- decisions |= node.decision << i
18
+ (2**tree.depth).times do |i|
19
+ next if i == 0
20
+ node = tree.at(i)
21
+ if !node.decision.nil?
22
+ mask |= 1 << i
23
+ decisions |= node.decision << i
24
+ end
23
25
  end
24
- end
25
26
 
26
- new(depth, decisions, mask)
27
- end
27
+ new(depth, decisions, mask)
28
+ end
28
29
 
29
- def to_tree
30
- tree = Tree.new(depth)
30
+ def to_tree
31
+ tree = Tree.new(depth)
31
32
 
32
- (2**tree.depth).times do |i|
33
- next if i == 0
33
+ (2**tree.depth).times do |i|
34
+ next if i == 0
34
35
 
35
- current_position = 1 << i
36
+ current_position = 1 << i
36
37
 
37
- if (mask & current_position) != 0
38
- node = tree.at(i)
39
- node.decision = (decisions & current_position) == 0 ? 0 : 1
38
+ if (mask & current_position) != 0
39
+ node = tree.at(i)
40
+ node.decision = (decisions & current_position) == 0 ? 0 : 1
41
+ end
40
42
  end
41
- end
42
43
 
43
- tree
44
+ tree
45
+ end
44
46
  end
45
- end
47
+ end
@@ -1,43 +1,60 @@
1
- class BinaryDecisionTree::Node
2
- attr_accessor :decision #nil, 0, or 1
1
+ module BinaryDecisionTree
2
+ class Node
3
+ LEFT = 0
4
+ RIGHT = 1
3
5
 
4
- attr_reader :slot #bit position
5
- attr_reader :tree
6
+ attr_accessor :decision #nil, 0, or 1
6
7
 
7
- def initialize(tree, slot, decision: nil)
8
- @tree = tree
9
- @slot = slot
10
- @decision = decision
11
- end
8
+ attr_reader :slot #bit position
9
+ attr_reader :tree
12
10
 
13
- def value
14
- case decision
15
- when 0
16
- left.nil? ? left_position : left.value
17
- when 1
18
- right.nil? ? right_position : right.value
19
- else
20
- nil
11
+ def initialize(tree, slot)
12
+ @tree = tree
13
+ @slot = slot
14
+ @decision = nil
21
15
  end
22
- end
23
16
 
24
- def leaf?
25
- left.nil? && right.nil?
26
- end
17
+ def value
18
+ case decision
19
+ when LEFT
20
+ left.nil? ? left_position : left.value
21
+ when RIGHT
22
+ right.nil? ? right_position : right.value
23
+ else
24
+ nil
25
+ end
26
+ end
27
27
 
28
- def left_position
29
- slot * 2
30
- end
28
+ def leaf?
29
+ left.nil? && right.nil?
30
+ end
31
31
 
32
- def right_position
33
- left_position + 1
34
- end
32
+ def current_depth
33
+ Math.log2(slot).floor + 1
34
+ end
35
35
 
36
- def left
37
- tree.at(left_position)
38
- end
36
+ def parent_position
37
+ (slot % 2 == 0 ? slot + 1 : slot) / 2
38
+ end
39
39
 
40
- def right
41
- tree.at(right_position)
40
+ def left_position
41
+ slot * 2
42
+ end
43
+
44
+ def right_position
45
+ left_position + 1
46
+ end
47
+
48
+ def parent
49
+ tree.at(parent_position)
50
+ end
51
+
52
+ def left
53
+ tree.at(left_position)
54
+ end
55
+
56
+ def right
57
+ tree.at(right_position)
58
+ end
42
59
  end
43
- end
60
+ end
@@ -1,16 +1,22 @@
1
- class BinaryDecisionTree::Tree
2
- attr_reader :depth
1
+ module BinaryDecisionTree
2
+ class Tree
3
+ attr_reader :depth
3
4
 
4
- def initialize(depth)
5
- @depth = depth
6
- @nodes = Array.new(2**depth) {|i| i == 0 ? nil : Node.new(self, i)}
7
- end
5
+ def initialize(depth)
6
+ @depth = depth
7
+ @nodes = Array.new(size) {|i| i == 0 ? nil : Node.new(self, i)}
8
+ end
8
9
 
9
- def root
10
- @nodes[1]
11
- end
10
+ def root
11
+ @nodes[1]
12
+ end
13
+
14
+ def at(position)
15
+ @nodes[position]
16
+ end
12
17
 
13
- def at(position)
14
- @nodes[position]
18
+ def size
19
+ 2**depth
20
+ end
15
21
  end
16
- end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module BinaryDecisionTree
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,24 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'binary_decision_tree'
5
+ require 'minitest/autorun'
6
+
7
+ class MiniTest::Spec
8
+ def assert_same_node(exp, act, msg = nil)
9
+ assert_equal exp.decision, act.decision, msg
10
+ assert_equal exp.slot, act.slot, msg
11
+ end
12
+
13
+ def assert_same_tree(exp, act, msg = nil)
14
+ assert_equal exp.depth, act.depth, msg
15
+ exp.size.times do |i|
16
+ next if i == 0
17
+ exp_node = exp.at(i)
18
+ act_node = act.at(i)
19
+ assert_same_node exp_node, act_node, msg
20
+ end
21
+ end
22
+ end
23
+
24
+
@@ -0,0 +1,28 @@
1
+ require_relative '../../../test/test_helper'
2
+
3
+ module BinaryDecisionTree
4
+ class MarshalledTreeTest < MiniTest::Spec
5
+ before do
6
+ @tree = Tree.new(6)
7
+ @tree.size.times do |i|
8
+ next if i == 0
9
+ @tree.at(i).decision = rand(2)
10
+ end
11
+ end
12
+
13
+ describe "marshalling a tree" do
14
+ before do
15
+ @marshalled_tree = MarshalledTree.from_tree(@tree)
16
+ end
17
+
18
+ it "should marshal and unmarshal to the same tree" do
19
+ assert_same_tree @tree, @marshalled_tree.to_tree
20
+ end
21
+
22
+ it "should be able to unmarshal from numeric values" do
23
+ another_tree = MarshalledTree.new(@tree.depth, @marshalled_tree.decisions, @marshalled_tree.mask).to_tree
24
+ assert_same_tree @tree, another_tree
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,169 @@
1
+ require_relative '../../../test/test_helper'
2
+
3
+ module BinaryDecisionTree
4
+ class NodeTest < MiniTest::Spec
5
+ describe "creating a new node" do
6
+ before do
7
+ @tree = Tree.new(1)
8
+ end
9
+
10
+ it "should take both a tree and slot number and expose them" do
11
+ node = Node.new(@tree, 1)
12
+
13
+ assert_equal @tree, node.tree
14
+ assert_equal 1, node.slot
15
+ assert_nil node.decision
16
+ end
17
+ end
18
+
19
+ describe "calculating a value" do
20
+ before do
21
+ @tree = Tree.new(2)
22
+ @nodes = @tree.size.times.collect {|i| @tree.at(i)}
23
+ @nodes.each {|n| n.decision = rand(2) unless n.nil?}
24
+ end
25
+
26
+ describe "when decision is left" do
27
+ before do
28
+ @nodes.each {|n| n.decision = Node::LEFT unless n.nil?}
29
+ end
30
+
31
+ it "should return the slot id of next left position on a leaf" do
32
+ node = @nodes.last
33
+ assert node.leaf?
34
+
35
+ assert_equal node.left_position, node.value
36
+ end
37
+ end
38
+
39
+ describe "when decision is right" do
40
+ before do
41
+ @nodes.each {|n| n.decision = Node::RIGHT unless n.nil?}
42
+ end
43
+
44
+ it "should return the slot id of next right position on a leaf" do
45
+ node = @nodes.last
46
+ assert node.leaf?
47
+
48
+ assert_equal node.right_position, node.value
49
+ end
50
+ end
51
+
52
+ it "should traverse the tree until at a leaf and return that value" do
53
+ node = @tree.root
54
+ node.decision = Node::LEFT
55
+
56
+ assert_equal node.left.value, node.value
57
+ end
58
+
59
+ it "should be nil if a decision is not set" do
60
+ node = Tree.new(1).root
61
+ assert_nil node.decision
62
+ assert_nil node.value
63
+ end
64
+ end
65
+
66
+ describe "a leaf node" do
67
+ before do
68
+ @node = Tree.new(2).at(2)
69
+ end
70
+
71
+ it "should know it is a leaf node" do
72
+ assert @node.leaf?
73
+ end
74
+
75
+ it "should not have a left or right child in the tree" do
76
+ assert_nil @node.left
77
+ assert_nil @node.right
78
+ end
79
+
80
+ it "should still have left and right positions" do
81
+ assert !@node.left_position.nil?
82
+ assert !@node.right_position.nil?
83
+ end
84
+
85
+ it "should have a parent" do
86
+ assert !@node.parent.nil?
87
+ end
88
+ end
89
+
90
+ describe "current depth calculation" do
91
+ before do
92
+ @depth_hash = {}
93
+
94
+ #bfs
95
+ tree = Tree.new(6) #63 nodes
96
+ tree.depth.times do |i|
97
+ current_depth = i + 1
98
+ @depth_hash[current_depth] = []
99
+ if current_depth == 1
100
+ @depth_hash[current_depth] << tree.root
101
+ else
102
+ @depth_hash[current_depth - 1].each do |parent_node|
103
+ @depth_hash[current_depth] << parent_node.left
104
+ @depth_hash[current_depth] << parent_node.right
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ it "should return the correct depth" do
111
+ @depth_hash.each do |depth, nodes|
112
+ nodes.each do |node|
113
+ assert_equal depth, node.current_depth
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "parent position calculation" do
120
+ before do
121
+ @tree = Tree.new(6)
122
+ end
123
+
124
+ it "should be half of the current slot value" do
125
+ i = 1
126
+ while !@tree.at(i).nil? && !@tree.at(i).left.nil?
127
+ node = @tree.at(i)
128
+ assert_same node, node.left.parent
129
+ assert_same node, node.right.parent
130
+ i += 1
131
+ end
132
+ end
133
+
134
+ it "should have a get node helper" do
135
+ node = @tree.at(3)
136
+ assert_equal 1, node.parent_position
137
+ assert_same @tree.at(1), node.parent
138
+ end
139
+ end
140
+
141
+ describe "child position calculations" do
142
+ before do
143
+ @tree = Tree.new(3)
144
+ @node = @tree.at(2)
145
+ end
146
+
147
+ describe "left position calculation" do
148
+ it "should be twice the current slot value" do
149
+ assert_equal 4, @node.left_position
150
+ end
151
+
152
+ it "should have a get node helper" do
153
+ assert_same @tree.at(4), @node.left
154
+ end
155
+ end
156
+
157
+ describe "right position calculation" do
158
+ it "should be next to the left child (one greater)" do
159
+ assert_equal 5, @node.right_position
160
+ end
161
+
162
+ it "should have a get node helper" do
163
+ assert_same @tree.at(5), @node.right
164
+ end
165
+ end
166
+
167
+ end
168
+ end
169
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binary_decision_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Haruska
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-29 00:00:00.000000000 Z
11
+ date: 2014-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,48 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: coveralls
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  description: A binary decision tree useful for brackets of single elimination tournaments.
42
84
  email:
43
85
  - jason@haruska.com
@@ -45,8 +87,11 @@ executables: []
45
87
  extensions: []
46
88
  extra_rdoc_files: []
47
89
  files:
90
+ - .coveralls.yml
48
91
  - .gitignore
92
+ - .travis.yml
49
93
  - Gemfile
94
+ - Guardfile
50
95
  - LICENSE.txt
51
96
  - README.md
52
97
  - Rakefile
@@ -56,6 +101,9 @@ files:
56
101
  - lib/binary_decision_tree/node.rb
57
102
  - lib/binary_decision_tree/tree.rb
58
103
  - lib/binary_decision_tree/version.rb
104
+ - test/test_helper.rb
105
+ - test/unit/binary_decision_tree/marshalled_tree_test.rb
106
+ - test/unit/binary_decision_tree/node_test.rb
59
107
  homepage: http://github.com/haruska/binary_decision_tree
60
108
  licenses:
61
109
  - MIT
@@ -80,4 +128,7 @@ rubygems_version: 2.0.14
80
128
  signing_key:
81
129
  specification_version: 4
82
130
  summary: A binary decision tree
83
- test_files: []
131
+ test_files:
132
+ - test/test_helper.rb
133
+ - test/unit/binary_decision_tree/marshalled_tree_test.rb
134
+ - test/unit/binary_decision_tree/node_test.rb