binary_decision_tree 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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