compsci 0.3.0.1 → 0.3.1.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.
- checksums.yaml +5 -5
- data/README.md +137 -78
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/compsci.gemspec +2 -2
- data/examples/binary_search_tree.rb +43 -8
- data/examples/complete_tree.rb +21 -22
- data/examples/flex_node.rb +117 -0
- data/examples/heap.rb +40 -2
- data/examples/heap_push.rb +7 -1
- data/examples/ternary_search_tree.rb +30 -0
- data/examples/tree.rb +27 -25
- data/lib/compsci.rb +10 -1
- data/lib/compsci/complete_tree.rb +6 -7
- data/lib/compsci/flex_node.rb +90 -0
- data/lib/compsci/heap.rb +1 -2
- data/lib/compsci/names/pokemon.rb +62 -0
- data/lib/compsci/node.rb +109 -59
- data/lib/compsci/simplex.rb +2 -4
- data/lib/compsci/simplex/parse.rb +4 -4
- data/test/bench/fibonacci.rb +29 -128
- data/test/bench/flex_node.rb +30 -0
- data/test/complete_tree.rb +16 -14
- data/test/compsci.rb +25 -0
- data/test/fibonacci.rb +6 -5
- data/test/fit.rb +30 -26
- data/test/flex_node.rb +226 -0
- data/test/heap.rb +46 -46
- data/test/names.rb +95 -56
- data/test/node.rb +177 -85
- data/test/simplex_parse.rb +23 -15
- data/test/timer.rb +10 -10
- metadata +16 -16
- data/examples/tree_push.rb +0 -72
- data/lib/compsci/binary_search_tree.rb +0 -86
- data/lib/compsci/tree.rb +0 -142
- data/test/bench/tree.rb +0 -31
- data/test/binary_search_tree.rb +0 -98
- data/test/tree.rb +0 -200
data/test/node.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'compsci/node'
|
2
|
+
require 'compsci/names'
|
2
3
|
require 'minitest/autorun'
|
3
4
|
|
4
5
|
include CompSci
|
@@ -10,14 +11,32 @@ describe Node do
|
|
10
11
|
@emilio_estevez = Node.new 'emilio'
|
11
12
|
end
|
12
13
|
|
14
|
+
it "must display_line" do
|
15
|
+
nodes = [@martin_sheen, @charlie_sheen, @emilio_estevez]
|
16
|
+
str = Node.display_line nodes: nodes, width: 80
|
17
|
+
# TODO: it was 78 once. Why < 80?
|
18
|
+
expect(str.size).must_be :>=, 78 # it might overflow
|
19
|
+
|
20
|
+
str = Node.display_line nodes: nodes, width: 200
|
21
|
+
expect(str.size).must_be :>=, 198 # it won't overflow
|
22
|
+
end
|
23
|
+
|
24
|
+
|
13
25
|
it "must start with no children" do
|
14
26
|
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
15
|
-
n.children.must_be_empty
|
27
|
+
expect(n.children.compact).must_be_empty
|
16
28
|
}
|
17
29
|
end
|
18
30
|
|
19
31
|
it "must not respond to :parent" do
|
20
|
-
@martin_sheen.respond_to?(:parent).must_equal false
|
32
|
+
expect(@martin_sheen.respond_to?(:parent)).must_equal false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "must create a tree by adding children" do
|
36
|
+
@martin_sheen[0] = @charlie_sheen
|
37
|
+
@martin_sheen[1] = @emilio_estevez
|
38
|
+
expect(@martin_sheen.children).must_include @charlie_sheen
|
39
|
+
expect(@martin_sheen.children).must_include @emilio_estevez
|
21
40
|
end
|
22
41
|
end
|
23
42
|
|
@@ -30,42 +49,142 @@ describe KeyNode do
|
|
30
49
|
|
31
50
|
it "must start with no children" do
|
32
51
|
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
33
|
-
n.children.must_be_empty
|
52
|
+
expect(n.children.compact).must_be_empty
|
34
53
|
}
|
35
54
|
end
|
36
55
|
|
37
56
|
it "must not respond to :parent" do
|
38
|
-
@martin_sheen.respond_to?(:parent).must_equal false
|
57
|
+
expect(@martin_sheen.respond_to?(:parent)).must_equal false
|
39
58
|
end
|
40
59
|
|
41
60
|
it "must have a key" do
|
42
|
-
@martin_sheen.key.must_equal 'marty'
|
43
|
-
@charlie_sheen.key.must_equal 'charles'
|
44
|
-
@emilio_estevez.key.must_equal 'emile'
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
61
|
+
expect(@martin_sheen.key).must_equal 'marty'
|
62
|
+
expect(@charlie_sheen.key).must_equal 'charles'
|
63
|
+
expect(@emilio_estevez.key).must_equal 'emile'
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "KeyNode.key_cmp_idx" do
|
67
|
+
it "must handle 2 or 3 child_slots" do
|
68
|
+
c2 = {
|
69
|
+
[1,2] => 0,
|
70
|
+
[1,10] => 0,
|
71
|
+
[2,2] => :raise,
|
72
|
+
[3,2] => 1,
|
73
|
+
[10,2] => 1,
|
74
|
+
}
|
75
|
+
c3 = {
|
76
|
+
[1,2] => 0,
|
77
|
+
[1,10] => 0,
|
78
|
+
[2,2] => 1,
|
79
|
+
[3,2] => 2,
|
80
|
+
[10,2] => 2,
|
81
|
+
}
|
82
|
+
|
83
|
+
c2.each { |(new_key, key), expected|
|
84
|
+
if expected == :raise
|
85
|
+
expect(proc {
|
86
|
+
KeyNode.key_cmp_idx(new_key, key)
|
87
|
+
}).must_raise KeyNode::DuplicateKey
|
88
|
+
else
|
89
|
+
expect(KeyNode.key_cmp_idx(new_key, key)).must_equal expected
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
c3.each { |(new_key, key), expected|
|
94
|
+
expect(KeyNode.key_cmp_idx(new_key, key,
|
95
|
+
child_slots: 3)).must_equal expected
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "Binary Search Tree" do
|
101
|
+
before do
|
102
|
+
@keys = Array.new(4) { |i| Names::WW2[i] }
|
103
|
+
@values = Array.new(4) { Names::SOLAR.sample }
|
104
|
+
@root = KeyNode.new(@values[0], key: @keys[0], children: 2)
|
105
|
+
1.upto(@keys.size - 1) { |i| @root.insert(@keys[i], @values[i]) }
|
106
|
+
|
107
|
+
# tree looks like:
|
108
|
+
# A:val1
|
109
|
+
# B:val2
|
110
|
+
# C:val3
|
111
|
+
# D:val4
|
112
|
+
end
|
113
|
+
|
114
|
+
it "must link to inserted nodes" do
|
115
|
+
expect(@root.children).wont_be_empty
|
116
|
+
expect(@root.children[0]).must_be_nil
|
117
|
+
c1 = @root.children[1]
|
118
|
+
expect(c1).wont_be_nil
|
119
|
+
expect(c1.key).must_equal @keys[1]
|
120
|
+
expect(c1.children[0]).must_be_nil
|
121
|
+
cc1 = c1.children[1]
|
122
|
+
expect(cc1).wont_be_nil
|
123
|
+
expect(cc1.value).must_equal @values[2]
|
124
|
+
expect(cc1.children[0]).must_be_nil
|
125
|
+
ccc1 = cc1.children[1]
|
126
|
+
expect(ccc1).wont_be_nil
|
127
|
+
expect(ccc1.key).must_equal @keys[3]
|
128
|
+
expect(ccc1.value).must_equal @values[3]
|
129
|
+
end
|
130
|
+
|
131
|
+
it "must search nodes" do
|
132
|
+
node = nil
|
133
|
+
new_order = (0..9).to_a.shuffle
|
134
|
+
new_order.each { |i|
|
135
|
+
k, v = Names::NATO[i], Names::SOLAR.sample
|
136
|
+
if node.nil?
|
137
|
+
node = KeyNode.new(v, key: k, children: 2)
|
138
|
+
else
|
139
|
+
node.insert(k, v)
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
3.times {
|
144
|
+
key = Names::NATO[new_order.sample]
|
145
|
+
found = node.search key
|
146
|
+
expect(found).wont_be_nil
|
147
|
+
expect(found.key).must_equal key
|
148
|
+
}
|
149
|
+
|
150
|
+
expect(node.search(Names::SOLAR.sample)).must_be_nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it "must accept or reject duplicates" do
|
154
|
+
expect(proc {
|
155
|
+
@root.insert(@keys[0], @values.sample)
|
156
|
+
}).must_raise KeyNode::DuplicateKey
|
157
|
+
|
158
|
+
node = KeyNode.new(@values[0], key: @keys[0], duplicates: true)
|
159
|
+
child = node.insert(@keys[0], @values[0])
|
160
|
+
expect(child).must_be_kind_of KeyNode
|
161
|
+
expect(node.children[1]).must_equal child
|
162
|
+
expect(node.children[0]).must_be_nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "Ternary Search Tree" do
|
167
|
+
before do
|
168
|
+
@keys = Array.new(4) { |i| Names::NATO[i] }
|
169
|
+
@values = Array.new(4) { Names::SOLAR.sample }
|
170
|
+
@root = KeyNode.new(@values[0], key: @keys[0], children: 3)
|
171
|
+
1.upto(@keys.size - 1) { |i| @root.insert(@keys[i], @values[i]) }
|
172
|
+
|
173
|
+
# tree looks like:
|
174
|
+
# A:val1
|
175
|
+
# B:val2
|
176
|
+
# C:val3
|
177
|
+
# D:val4
|
178
|
+
end
|
179
|
+
|
180
|
+
it "must insert a duplicate as the middle child" do
|
181
|
+
node3 = KeyNode.new(@values[0], key: @keys[0], children: 3)
|
182
|
+
child = node3.insert(@keys[0], @values[0])
|
183
|
+
expect(child).must_be_kind_of KeyNode
|
184
|
+
expect(node3.children[1]).must_equal child
|
185
|
+
expect(node3.children[0]).must_be_nil
|
186
|
+
expect(node3.children[2]).must_be_nil
|
187
|
+
end
|
69
188
|
end
|
70
189
|
end
|
71
190
|
|
@@ -78,63 +197,36 @@ describe ChildNode do
|
|
78
197
|
|
79
198
|
it "must start with neither parent nor children" do
|
80
199
|
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
81
|
-
n.parent.
|
82
|
-
n.children.must_be_empty
|
200
|
+
expect(n.parent).must_be_nil
|
201
|
+
expect(n.children.compact).must_be_empty
|
83
202
|
}
|
84
203
|
end
|
85
204
|
|
86
205
|
it "must track parent and children" do
|
87
|
-
@
|
88
|
-
@charlie_sheen.parent.must_equal @martin_sheen
|
89
|
-
@martin_sheen.children.must_include @charlie_sheen
|
90
|
-
|
91
|
-
@martin_sheen.children.wont_include @emilio_estevez
|
92
|
-
@martin_sheen
|
93
|
-
@
|
94
|
-
@
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
@martin_sheen =
|
101
|
-
@charlie_sheen
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
@martin_sheen
|
109
|
-
|
110
|
-
@
|
111
|
-
@
|
112
|
-
@martin_sheen.children.must_include @emilio_estevez
|
113
|
-
@emilio_estevez.parent.must_equal @martin_sheen
|
114
|
-
end
|
115
|
-
|
116
|
-
it "must determine a node's generation" do
|
117
|
-
@emilio_estevez.gen.must_equal 0
|
118
|
-
@martin_sheen.add_child @emilio_estevez
|
119
|
-
@emilio_estevez.gen.must_equal 1
|
120
|
-
end
|
121
|
-
|
122
|
-
it "must create children from scalars" do
|
123
|
-
@martin_sheen.new_child 'fake_emilio'
|
124
|
-
@martin_sheen.children.size.must_equal 1
|
125
|
-
@martin_sheen.children.first.value.must_equal 'fake_emilio'
|
126
|
-
@martin_sheen.children.wont_include @emilio_estevez
|
127
|
-
end
|
128
|
-
|
129
|
-
it "must recognize siblings" do
|
130
|
-
@charlie_sheen.add_parent @martin_sheen
|
131
|
-
@emilio_estevez.add_parent @martin_sheen
|
132
|
-
@martin_sheen.new_child 'fake_emilio'
|
133
|
-
|
134
|
-
@charlie_sheen.siblings.must_include @emilio_estevez
|
135
|
-
@charlie_sheen.siblings.wont_include @martin_sheen
|
136
|
-
@emilio_estevez.siblings.must_include @charlie_sheen
|
137
|
-
@martin_sheen.siblings.must_be_empty
|
138
|
-
@emilio_estevez.siblings.find { |n| n.value == 'fake_emilio' }.wont_be_nil
|
206
|
+
@martin_sheen[0] = @charlie_sheen
|
207
|
+
expect(@charlie_sheen.parent).must_equal @martin_sheen
|
208
|
+
expect(@martin_sheen.children).must_include @charlie_sheen
|
209
|
+
|
210
|
+
expect(@martin_sheen.children).wont_include @emilio_estevez
|
211
|
+
@martin_sheen[1] = @emilio_estevez
|
212
|
+
expect(@emilio_estevez.parent).must_equal @martin_sheen
|
213
|
+
expect(@martin_sheen.children).must_include @emilio_estevez
|
214
|
+
expect(@martin_sheen.children).must_include @charlie_sheen
|
215
|
+
end
|
216
|
+
|
217
|
+
it "must track siblings" do
|
218
|
+
@martin_sheen[0] = @charlie_sheen
|
219
|
+
@martin_sheen[1] = @emilio_estevez
|
220
|
+
expect(@charlie_sheen.siblings).must_include @emilio_estevez
|
221
|
+
# TODO: should siblings not include self?
|
222
|
+
# expect(@charlie_sheen.siblings).wont_include @charlie_sheen
|
223
|
+
expect(@charlie_sheen.siblings).must_include @charlie_sheen
|
224
|
+
end
|
225
|
+
|
226
|
+
it "must track generation" do
|
227
|
+
@martin_sheen[0] = @charlie_sheen
|
228
|
+
expect(@charlie_sheen.gen).must_equal 1
|
229
|
+
@charlie_sheen[0] = @emilio_estevez # kinky!
|
230
|
+
expect(@emilio_estevez.gen).must_equal 2
|
139
231
|
end
|
140
232
|
end
|
data/test/simplex_parse.rb
CHANGED
@@ -12,13 +12,13 @@ describe Simplex::Parse do
|
|
12
12
|
"99x" => [99.0, :x],
|
13
13
|
"z" => [ 1.0, :z],
|
14
14
|
"-b" => [-1.0, :b] }.each { |valid, expected|
|
15
|
-
P.term(valid).must_equal expected
|
15
|
+
expect(P.term(valid)).must_equal expected
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
19
|
it "must reject invalid terms" do
|
20
20
|
["3xy", "24/7x", "x17", "2*x"].each { |invalid|
|
21
|
-
proc { P.term(invalid) }.must_raise RuntimeError
|
21
|
+
expect(proc { P.term(invalid) }).must_raise RuntimeError
|
22
22
|
}
|
23
23
|
end
|
24
24
|
end
|
@@ -32,7 +32,7 @@ describe Simplex::Parse do
|
|
32
32
|
"a - -b" => { a: 1.0, b: 1.0 },
|
33
33
|
"a A b" => { a: 1.0, :A => 1.0, b: 1.0 },
|
34
34
|
}.each { |valid, expected|
|
35
|
-
P.expression(valid).must_equal expected
|
35
|
+
expect(P.expression(valid)).must_equal expected
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
@@ -40,18 +40,18 @@ describe Simplex::Parse do
|
|
40
40
|
["a2 + b2 = c2",
|
41
41
|
"x + xy",
|
42
42
|
"x * 2"].each { |invalid|
|
43
|
-
proc { P.expression(invalid) }.must_raise P::Error
|
43
|
+
expect(proc { P.expression(invalid) }).must_raise P::Error
|
44
44
|
}
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
describe "Parse.tokenize" do
|
49
49
|
it "ignores leading or trailing whitespace" do
|
50
|
-
P.tokenize(" 5x + 2.9y ").must_equal ["5x", "+", "2.9y"]
|
50
|
+
expect(P.tokenize(" 5x + 2.9y ")).must_equal ["5x", "+", "2.9y"]
|
51
51
|
end
|
52
52
|
|
53
53
|
it "ignores multiple spaces" do
|
54
|
-
P.tokenize("5x + 2.9y").must_equal ["5x", "+", "2.9y"]
|
54
|
+
expect(P.tokenize("5x + 2.9y")).must_equal ["5x", "+", "2.9y"]
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -61,7 +61,7 @@ describe Simplex::Parse do
|
|
61
61
|
"0.94a - 22.1b <= -14.67" => [{ a: 0.94, b: -22.1 }, -14.67],
|
62
62
|
"x <= 0" => [{ x: 1.0 }, 0],
|
63
63
|
}.each { |valid, expected|
|
64
|
-
P.inequality(valid).must_equal expected
|
64
|
+
expect(P.inequality(valid)).must_equal expected
|
65
65
|
}
|
66
66
|
end
|
67
67
|
|
@@ -70,25 +70,33 @@ describe Simplex::Parse do
|
|
70
70
|
"0.94a - 22.1b <= -14.67c",
|
71
71
|
"x < 0",
|
72
72
|
].each { |invalid|
|
73
|
-
proc { P.inequality(invalid) }.must_raise P::Error
|
73
|
+
expect(proc { P.inequality(invalid) }).must_raise P::Error
|
74
74
|
}
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
79
|
describe "Simplex.maximize" do
|
80
|
-
it "must problem
|
80
|
+
it "must solve a problem" do
|
81
81
|
prob = Simplex.problem(maximize: 'x + y',
|
82
82
|
constraints: ['2x + y <= 4',
|
83
83
|
'x + 2y <= 3'])
|
84
84
|
sol = prob.solution
|
85
|
-
sol.must_equal [Rational(5, 3), Rational(2, 3)]
|
85
|
+
expect(sol).must_equal [Rational(5, 3), Rational(2, 3)]
|
86
86
|
end
|
87
87
|
|
88
|
-
it "must maximize
|
89
|
-
Simplex.maximize('x + y',
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
it "must maximize" do
|
89
|
+
expect(Simplex.maximize('x + y',
|
90
|
+
'2x + y <= 4',
|
91
|
+
'x + 2y <= 3')).must_equal [Rational(5, 3),
|
92
|
+
Rational(2, 3)]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "must reject bad expressions" do
|
96
|
+
expect(proc { Simplex.maximize(5) }).must_raise ArgumentError
|
97
|
+
expect(proc {
|
98
|
+
Simplex.maximize('5')
|
99
|
+
}).must_raise Simplex::Parse::InvalidTerm
|
100
|
+
# proc { Simplex.maximize('5x') }.must_raise Simplex::Parse::InvalidTerm
|
93
101
|
end
|
94
102
|
end
|
data/test/timer.rb
CHANGED
@@ -7,8 +7,8 @@ describe Timer do
|
|
7
7
|
describe "elapsed" do
|
8
8
|
it "must return the block value and positive number" do
|
9
9
|
answer, elapsed = Timer.elapsed { sleep 0.01; :foo }
|
10
|
-
answer.must_equal :foo
|
11
|
-
elapsed.must_be_close_to 0.015, 0.
|
10
|
+
expect(answer).must_equal :foo
|
11
|
+
expect(elapsed).must_be_close_to 0.015, 0.01
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -16,7 +16,7 @@ describe Timer do
|
|
16
16
|
it "must be positive" do
|
17
17
|
start = Timer.now
|
18
18
|
sleep 0.01
|
19
|
-
Timer.since(start).must_be_close_to 0.015, 0.
|
19
|
+
expect(Timer.since(start)).must_be_close_to 0.015, 0.01
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -27,9 +27,9 @@ describe Timer do
|
|
27
27
|
sleep 0.01
|
28
28
|
:foo
|
29
29
|
}
|
30
|
-
answer.must_equal :foo
|
31
|
-
avg_et.must_be_close_to 0.01, 0.005
|
32
|
-
Timer.since(start).must_be_close_to 0.15, 0.05
|
30
|
+
expect(answer).must_equal :foo
|
31
|
+
expect(avg_et).must_be_close_to 0.01, 0.005
|
32
|
+
expect(Timer.since(start)).must_be_close_to 0.15, 0.05
|
33
33
|
end
|
34
34
|
|
35
35
|
it "must repeat short loops and stop on time" do
|
@@ -42,8 +42,8 @@ describe Timer do
|
|
42
42
|
_answer, avg_et = Timer.loop_avg(count: 5) {
|
43
43
|
sleep 0.01
|
44
44
|
}
|
45
|
-
avg_et.must_be_close_to 0.015, 0.005
|
46
|
-
Timer.since(start).must_be_close_to 0.1, 0.05
|
45
|
+
expect(avg_et).must_be_close_to 0.015, 0.005
|
46
|
+
expect(Timer.since(start)).must_be_close_to 0.1, 0.05
|
47
47
|
end
|
48
48
|
|
49
49
|
it "must not interrupt long loops" do
|
@@ -51,8 +51,8 @@ describe Timer do
|
|
51
51
|
_answer, avg_et = Timer.loop_avg(seconds: 0.01) {
|
52
52
|
sleep 0.1
|
53
53
|
}
|
54
|
-
Timer.since(start).must_be_close_to avg_et, 0.05
|
55
|
-
avg_et.must_be_close_to 0.15, 0.05
|
54
|
+
expect(Timer.since(start)).must_be_close_to avg_et, 0.05
|
55
|
+
expect(avg_et).must_be_close_to 0.15, 0.05
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|