compsci 0.3.0.1 → 0.3.2.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.
@@ -1,86 +0,0 @@
1
- require 'compsci/node'
2
- require 'compsci/tree'
3
-
4
- module CompSci
5
- class BinarySearchTree < BinaryTree
6
- def self.display_level(nodes: [], width: 80)
7
- unless self.power_of?(nodes.size, 2)
8
- raise "unexpected node count: #{nodes.size}"
9
- end
10
- block_width = [width / nodes.size, 1].max
11
- nodes.map { |node|
12
- str = node ? node.to_s : '_'
13
- space = [(block_width + str.size) / 2, str.size + 1].max
14
- str.ljust(space, ' ').rjust(block_width, ' ')
15
- }.join
16
- end
17
-
18
- # helper method; any object which responds to key, value, and children
19
- # may be used
20
- def self.new_node(key, val)
21
- CompSci::KeyNode.new(val, key: key, children: 2)
22
- end
23
-
24
- def self.new_with_kv(key, val)
25
- self.new(self.new_node(key, val))
26
- end
27
-
28
- def initialize(root_node)
29
- @node_class = root_node.class
30
- @child_slots = 2
31
- if root_node.children.size == @child_slots
32
- @root = root_node
33
- else
34
- raise "bad root: #{root_node}; expected #{@child_slots} child slots"
35
- end
36
- end
37
-
38
- def search_recursive(key, node: @root)
39
- return node if node.nil? or node.key == key
40
- child = key < node.key ? node.children[0] : node.children[1]
41
- search_recursive(key, node: child)
42
- end
43
-
44
- def search_iterative(key, node: @root)
45
- while !node.nil?
46
- return node if node.key == key
47
- node = key < node.key ? node.children[0] : node.children[1]
48
- end
49
- node
50
- end
51
-
52
- def insert_recursive(key, val, node: @root)
53
- return nil if node.nil? or node.key == key
54
- if key < node.key
55
- if node.children[0]
56
- insert_recursive(key, val, node: node.children[0])
57
- else
58
- node.children[0] = @node_class.new(val, key: key, children: 2)
59
- end
60
- else
61
- if node.children[1]
62
- insert_recursive(key, val, node: node.children[1])
63
- else
64
- node.children[1] = @node_class.new(val, key: key, children: 2)
65
- end
66
- end
67
- end
68
- alias_method :insert, :insert_recursive
69
- alias_method :[]=, :insert
70
-
71
- def display(node: @root, width: 80)
72
- levels = [self.class.display_level(nodes: [node], width: width)]
73
- nodes = node.children
74
- while nodes.any? { |n| !n.nil? }
75
- levels << self.class.display_level(nodes: nodes, width: width)
76
- children = []
77
- nodes.each { |n|
78
- children += n ? n.children : Array.new(@child_slots)
79
- }
80
- nodes = children
81
- end
82
- levels.join("\n")
83
- end
84
- alias_method :to_s, :display
85
- end
86
- end
data/lib/compsci/tree.rb DELETED
@@ -1,142 +0,0 @@
1
- # require 'compsci/node'
2
-
3
- module CompSci
4
- # for now at least, this assumes children simply stack up
5
- # children like: [nil, child1, child2] are not supported
6
- class Tree
7
- attr_reader :root
8
-
9
- def initialize(node_class, val)
10
- @root = node_class.new val
11
- end
12
-
13
- # does not support children gaps
14
- def df_search(node: nil, &blk)
15
- node ||= @root
16
- return node if yield node
17
- node.children.each { |c|
18
- stop_node = self.df_search(node: c, &blk)
19
- return stop_node if stop_node
20
- }
21
- nil
22
- end
23
-
24
- # does not support children gaps
25
- def bf_search(node: nil, &blk)
26
- node ||= @root
27
- destinations = [node]
28
- while !destinations.empty?
29
- node = destinations.shift
30
- return node if yield node
31
- destinations += node.children
32
- end
33
- nil
34
- end
35
-
36
- def df_search_generic(node: nil, &blk)
37
- # Perform pre-order operation
38
- # children.each { Perform in-order operation }
39
- # Perform post-order operation
40
- puts "not defined yet"
41
- end
42
- end
43
-
44
- class NaryTree < Tree
45
- # thanks apeiros
46
- # https://gist.github.com/rickhull/d0b579aa08c85430b0dc82a791ff12d6
47
- def self.power_of?(num, base)
48
- return false if base <= 1
49
- mod = 0
50
- num, mod = num.divmod(base) until num == 1 || mod > 0
51
- mod == 0
52
- end
53
-
54
- attr_reader :child_slots
55
-
56
- def initialize(node_class, val, child_slots:)
57
- super(node_class, val)
58
- @child_slots = child_slots
59
- end
60
-
61
- def open_parent?(node)
62
- node.children.size < @child_slots
63
- end
64
-
65
- def open_sibling
66
- # try siblings first, only possible with Node#parent
67
- if @open_parent.respond_to?(:siblings)
68
- @open_parent.siblings.find { |s| self.open_parent?(s) }
69
- end
70
- end
71
-
72
- def open_parent
73
- @open_parent ||= @root
74
- return @open_parent if self.open_parent?(@open_parent)
75
- open_sibling = self.open_sibling
76
- return @open_parent = open_sibling if open_sibling
77
- @open_parent = self.bf_search { |n| self.open_parent?(n) }
78
- end
79
-
80
- def push(value)
81
- self.open_parent.new_child value
82
- end
83
-
84
- def display(width: nil)
85
- str = ''
86
- old_level = 0
87
- width ||= @child_slots * 40
88
- self.bf_search { |node|
89
- raise "#{node.class} not yet supported" unless node.respond_to? :gen
90
- level = node.gen
91
- if old_level != level
92
- str += "\n"
93
- old_level = level
94
- end
95
- # center in block_width
96
- slots = @child_slots**level
97
- block_width = width / slots
98
- val = node.to_s
99
- space = [(block_width + val.size) / 2, val.size + 1].max
100
- str += val.ljust(space, ' ').rjust(block_width, ' ')
101
- false
102
- }
103
- str
104
- end
105
- alias_method :to_s, :display
106
- end
107
-
108
- class BinaryTree < NaryTree
109
- def initialize(node_class, val)
110
- super(node_class, val, child_slots: 2)
111
- end
112
-
113
- def display(width: 80)
114
- count = 0
115
- str = ''
116
- self.bf_search { |node|
117
- count += 1
118
- level = Math.log(count, 2).floor
119
- block_width = width / (2**level)
120
- val = node.to_s
121
- str += "\n" if 2**level == count and count > 1
122
- space = [(block_width + val.size) / 2, val.size + 1].max
123
- str += val.ljust(space, ' ').rjust(block_width, ' ')
124
- false # keep searching to visit every node
125
- }
126
- str
127
- end
128
- alias_method :to_s, :display
129
- end
130
-
131
- class TernaryTree < NaryTree
132
- def initialize(node_class, val)
133
- super(node_class, val, child_slots: 3)
134
- end
135
- end
136
-
137
- class QuaternaryTree < NaryTree
138
- def initialize(node_class, val)
139
- super(node_class, val, child_slots: 4)
140
- end
141
- end
142
- end
data/test/bench/tree.rb DELETED
@@ -1,31 +0,0 @@
1
- require 'compsci/node'
2
- require 'compsci/tree'
3
- require 'benchmark/ips'
4
-
5
- include CompSci
6
-
7
- Benchmark.ips do |b|
8
- b.config time: 3, warmup: 0.5
9
-
10
- b.report("99x BinaryTree(ChildNode)#push") do
11
- tree = BinaryTree.new(ChildFlexNode, 42)
12
- 99.times { tree.push rand 99 }
13
- end
14
-
15
- b.report("99x BinaryTree(FlexNode)#push") do
16
- tree = BinaryTree.new(FlexNode, 42)
17
- 99.times { tree.push rand 99 }
18
- end
19
-
20
- b.report("99x TernaryTree(ChildFlexNode)#push") do
21
- tree = TernaryTree.new(ChildFlexNode, 42)
22
- 99.times { tree.push rand 99 }
23
- end
24
-
25
- b.report("99x TernaryTree(FlexNode)#push") do
26
- tree = TernaryTree.new(FlexNode, 42)
27
- 99.times { tree.push rand 99 }
28
- end
29
-
30
- b.compare!
31
- end
@@ -1,98 +0,0 @@
1
- require 'compsci/node'
2
- require 'compsci/binary_search_tree'
3
- require 'compsci/names'
4
- require 'minitest/autorun'
5
-
6
- include CompSci
7
-
8
- describe BinarySearchTree do
9
- before do
10
- @keys = Array.new(4) { |i| Names::WW2[i] }
11
- @values = Array.new(4) { Names::SOLAR.sample }
12
- @nodes = Array.new(4) { |i|
13
- KeyNode.new(@values[i], key: @keys[i], children: 2)
14
- }
15
- @tree = BinarySearchTree.new(@nodes.first)
16
-
17
- # tree will look like:
18
- # A:val1
19
- # B:val2
20
- # C:val3
21
- # D:val4
22
- end
23
-
24
- it "must display_level" do
25
- str = BinarySearchTree.display_level nodes: @nodes, width: 80
26
- str.size.must_be :>=, 80 # it can overflow
27
-
28
- str = BinarySearchTree.display_level nodes: @nodes, width: 200
29
- str.size.must_equal 200 # it won't overflow
30
-
31
- @keys.each { |k| str.must_include k.to_s }
32
- @values.each { |v| str.must_include v.to_s }
33
- end
34
-
35
- it "must provide a new_node" do
36
- node = BinarySearchTree.new_node('the key', 'the value')
37
- node.must_be_kind_of Node
38
- node.must_be_kind_of KeyNode
39
- node.key.must_equal 'the key'
40
- node.value.must_equal 'the value'
41
- end
42
-
43
- it "must instantiate with key and value" do
44
- tree = BinarySearchTree.new_with_kv('the key', 'the value')
45
- node = tree.root
46
- node.must_be_kind_of Node
47
- node.must_be_kind_of KeyNode
48
- node.key.must_equal 'the key'
49
- node.value.must_equal 'the value'
50
- end
51
-
52
- it "must decide what to do with duplicate nodes" do
53
- end
54
-
55
- it "must decide what to do with duplicate keys" do
56
- end
57
-
58
- it "must insert nodes" do
59
- 1.upto(@nodes.size - 1) { |i| @tree[@keys[i]] = @values[i] }
60
- @tree.root.children.wont_be_empty
61
- @tree.root.children[0].nil?.must_equal true
62
- @tree.root.children[1].key.must_equal @keys[1]
63
- @tree.root.children[1].children[0].nil?.must_equal true
64
- @tree.root.children[1].children[1].value.must_equal @values[2]
65
- end
66
-
67
- it "must search nodes" do
68
- tree = nil
69
- new_order = (0..9).to_a.shuffle
70
- new_order.each { |i|
71
- k, v = Names::NATO[i], Names::SOLAR.sample
72
- if tree.nil?
73
- tree = BinarySearchTree.new_with_kv(k, v)
74
- else
75
- tree[k] = v
76
- end
77
- }
78
-
79
- 2.times {
80
- i = new_order.sample
81
- key = Names::NATO[new_order.sample]
82
- node = tree.search_iterative key
83
- node.wont_be_nil
84
- node.key.must_equal key
85
- }
86
-
87
- 2.times {
88
- i = new_order.sample
89
- key = Names::NATO[new_order.sample]
90
- node = tree.search_recursive key
91
- node.wont_be_nil
92
- node.key.must_equal key
93
- }
94
-
95
- tree.search_iterative(Names::SOLAR.sample).must_be_nil
96
- tree.search_recursive(Names::SOLAR.sample).must_be_nil
97
- end
98
- end
data/test/tree.rb DELETED
@@ -1,200 +0,0 @@
1
- require 'compsci/node'
2
- require 'compsci/tree'
3
- require 'minitest/autorun'
4
-
5
- include CompSci
6
-
7
- describe Tree do
8
- before do
9
- @tree = Tree.new(FlexNode, 42)
10
- @vals = Array.new(99) { rand 99 }
11
- end
12
-
13
- it "is populated via the root node" do
14
- @vals.each { |v| @tree.root.new_child v }
15
- @tree.root.children.size.must_equal @vals.size
16
- end
17
-
18
- it "does depth_first search" do
19
- vals = (0..30).to_a
20
- tree = Tree.new(FlexNode, vals.shift)
21
- tree.root.new_child vals.shift
22
- tree.root.new_child vals.shift
23
- tree.root.children.each { |c|
24
- c.new_child vals.shift
25
- c.new_child vals.shift
26
-
27
- c.children.each { |cc|
28
- cc.new_child vals.shift
29
- cc.new_child vals.shift
30
- }
31
- }
32
-
33
- visited = []
34
- tree.df_search { |n|
35
- visited << n.value
36
- false
37
- }
38
- visited.wont_be_empty
39
- visited.must_equal [0, 1, 3, 5, 6, 4, 7, 8, 2, 9, 11, 12, 10, 13, 14]
40
- end
41
-
42
- it "does breadth_first search" do
43
- vals = (0..30).to_a
44
- tree = Tree.new(FlexNode, vals.shift)
45
- tree.root.new_child vals.shift
46
- tree.root.new_child vals.shift
47
- tree.root.children.each { |c|
48
- c.new_child vals.shift
49
- c.new_child vals.shift
50
-
51
- c.children.each { |cc|
52
- cc.new_child vals.shift
53
- cc.new_child vals.shift
54
- }
55
- }
56
-
57
- visited = []
58
- tree.bf_search { |n|
59
- visited << n.value
60
- false
61
- }
62
- visited.wont_be_empty
63
- visited.must_equal [0, 1, 2, 3, 4, 9, 10, 5, 6, 7, 8, 11, 12, 13, 14]
64
- end
65
- end
66
-
67
- describe NaryTree do
68
- it "must power_of?" do
69
- powers = {}
70
- basemax = 12
71
- expmax = 10
72
- 2.upto(basemax) { |base|
73
- 0.upto(expmax) { |exp|
74
- powers[base] ||= []
75
- powers[base] << base**exp
76
- }
77
- }
78
-
79
- # 12k assertions below!
80
- 2.upto(basemax) { |base|
81
- 1.upto(2**expmax) { |num|
82
- if powers[base].include?(num)
83
- NaryTree.power_of?(num, base).must_equal true
84
- else
85
- NaryTree.power_of?(num, base).must_equal false
86
- end
87
- }
88
- }
89
- end
90
-
91
- describe "with FlexNode" do
92
- before do
93
- @tree = NaryTree.new(FlexNode, 42, child_slots: 3)
94
- end
95
-
96
- it "must have an open parent" do
97
- @tree.open_parent?(@tree.root).must_equal true
98
- @tree.open_parent?(@tree.open_parent).must_equal true
99
- end
100
-
101
- it "must push a value onto an open parent" do
102
- op = @tree.open_parent
103
- opc = op.children.size
104
- @tree.push 5
105
- @tree.open_parent.children.size.must_equal opc + 1
106
- end
107
- end
108
-
109
- describe "with ChildFlexNode" do
110
- before do
111
- @tree = NaryTree.new(ChildFlexNode, 42, child_slots: 4)
112
- end
113
-
114
- it "must have an open parent" do
115
- @tree.open_parent?(@tree.root).must_equal true
116
- @tree.open_parent?(@tree.open_parent).must_equal true
117
- end
118
-
119
- it "must push a value onto an open parent" do
120
- op = @tree.open_parent
121
- opc = op.children.size
122
- @tree.push 5
123
- @tree.open_parent.children.size.must_equal opc + 1
124
- end
125
- end
126
- end
127
-
128
- describe "BinaryTree" do
129
- before do
130
- @tree = BinaryTree.new(FlexNode, 42)
131
- end
132
-
133
- it "must have 2 child_slots" do
134
- @tree.child_slots.must_equal 2
135
- end
136
-
137
- it "must to_s" do
138
- item_count = 31
139
- # tree already has a root node
140
- (item_count - 1).times { @tree.push rand 99 }
141
- str = @tree.to_s
142
- line_count = str.split("\n").size
143
- line_count.must_equal Math.log(item_count + 1, 2).ceil
144
- end
145
-
146
- describe "searching" do
147
- before do
148
- @tree = NaryTree.new(FlexNode, 42, child_slots: 2)
149
- 99.times { |i| @tree.push i }
150
- end
151
-
152
- it "must find 42 quickly" do
153
- count = 0
154
- @tree.df_search { |n|
155
- count += 1
156
- n.value == 42
157
- }.must_equal @tree.root
158
- count.must_equal 1
159
-
160
- count = 0
161
- @tree.bf_search { |n|
162
- count += 1
163
- n.value == 42
164
- }.must_equal @tree.root
165
- count.must_equal 1
166
- end
167
-
168
- it "must find 99 slowly" do
169
- count = 0
170
- @tree.df_search { |n|
171
- count += 1
172
- n.value == 99
173
- }
174
- count.must_equal 100
175
-
176
- count = 0
177
- @tree.bf_search { |n|
178
- count += 1
179
- n.value == 99
180
- }
181
- count.must_equal 100
182
- end
183
-
184
- it "must find 81 accordingly" do
185
- count = 0
186
- @tree.df_search { |n|
187
- count += 1
188
- n.value == 81
189
- }
190
- count.must_equal 42
191
-
192
- count = 0
193
- @tree.bf_search { |n|
194
- count += 1
195
- n.value == 81
196
- }
197
- count.must_equal 83
198
- end
199
- end
200
- end