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