compsci 0.0.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 +7 -0
- data/README.md +48 -0
- data/Rakefile +108 -0
- data/VERSION +1 -0
- data/compsci.gemspec +38 -0
- data/examples/binary_tree.rb +21 -0
- data/examples/heap.rb +65 -0
- data/examples/timer.rb +42 -0
- data/lib/compsci/fib.rb +24 -0
- data/lib/compsci/fit.rb +137 -0
- data/lib/compsci/heap.rb +100 -0
- data/lib/compsci/timer.rb +36 -0
- data/lib/compsci/tree.rb +140 -0
- data/lib/compsci.rb +2 -0
- data/test/bench/fib.rb +114 -0
- data/test/bench/heap.rb +15 -0
- data/test/bench/tree.rb +19 -0
- data/test/fib.rb +13 -0
- data/test/fit.rb +162 -0
- data/test/heap.rb +46 -0
- data/test/timer.rb +48 -0
- data/test/tree.rb +209 -0
- metadata +78 -0
data/lib/compsci/tree.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'compsci'
|
2
|
+
|
3
|
+
module CompSci
|
4
|
+
class Tree
|
5
|
+
attr_reader :root, :child_slots
|
6
|
+
|
7
|
+
def initialize(root_node, child_slots: 2)
|
8
|
+
@root = root_node
|
9
|
+
@child_slots = child_slots
|
10
|
+
@open_parent = @root
|
11
|
+
end
|
12
|
+
|
13
|
+
def push(value)
|
14
|
+
self.open_parent.new_child value
|
15
|
+
end
|
16
|
+
|
17
|
+
def open_parent?(node)
|
18
|
+
node.children.size < @child_slots
|
19
|
+
end
|
20
|
+
|
21
|
+
def open_parent
|
22
|
+
return @open_parent if self.open_parent?(@open_parent)
|
23
|
+
@open_parent = self.bf_search { |n| self.open_parent?(n) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def df_search(node: nil, &blk)
|
27
|
+
node ||= @root
|
28
|
+
return node if yield node
|
29
|
+
node.children.each { |c|
|
30
|
+
stop_node = self.df_search(node: c, &blk)
|
31
|
+
return stop_node if stop_node
|
32
|
+
}
|
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
|
+
|
43
|
+
def bf_search(node: nil, &blk)
|
44
|
+
node ||= @root
|
45
|
+
destinations = [node]
|
46
|
+
while !destinations.empty?
|
47
|
+
node = destinations.shift
|
48
|
+
return node if yield node
|
49
|
+
destinations += node.children
|
50
|
+
end
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
class Node
|
55
|
+
attr_accessor :value, :parent
|
56
|
+
attr_reader :children
|
57
|
+
|
58
|
+
def initialize(value)
|
59
|
+
@value = value
|
60
|
+
@parent = nil
|
61
|
+
@children = []
|
62
|
+
# @metadata = {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_child(node)
|
66
|
+
node.parent ||= self
|
67
|
+
raise "node has a parent: #{node.parent}" if node.parent != self
|
68
|
+
@children << node
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_child(value)
|
72
|
+
self.add_child self.class.new(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_parent(node)
|
76
|
+
@parent = node
|
77
|
+
node.add_child(self)
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
@value.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def inspect
|
85
|
+
"#<%s:0x%0xi @value=%s @children=[%s]>" %
|
86
|
+
[self.class,
|
87
|
+
self.object_id,
|
88
|
+
self.to_s,
|
89
|
+
@children.map(&:to_s).join(', ')]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class BinaryTree < Tree
|
95
|
+
def initialize(root_node)
|
96
|
+
super(root_node, child_slots: 2)
|
97
|
+
end
|
98
|
+
|
99
|
+
def bf_print(node: nil, width: 80)
|
100
|
+
count = 0
|
101
|
+
self.bf_search(node: node) { |n|
|
102
|
+
count += 1
|
103
|
+
level = Math.log(count, 2).floor
|
104
|
+
block_width = width / (2**level)
|
105
|
+
puts if 2**level == count and count > 1
|
106
|
+
print n.to_s.ljust(block_width / 2, ' ').rjust(block_width, ' ')
|
107
|
+
}
|
108
|
+
puts
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# A CompleteBinaryTree can very efficiently use an array for storage using
|
113
|
+
# simple arithmetic to determine parent child relationships.
|
114
|
+
#
|
115
|
+
class CompleteBinaryTree
|
116
|
+
# integer math says idx 2 and idx 1 both have parent at idx 0
|
117
|
+
def self.parent_idx(idx)
|
118
|
+
(idx-1) / 2
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.children_idx(idx)
|
122
|
+
[2*idx + 1, 2*idx + 2]
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_reader :store
|
126
|
+
|
127
|
+
def initialize(store: [])
|
128
|
+
@store = store
|
129
|
+
# yield self if block_given?
|
130
|
+
end
|
131
|
+
|
132
|
+
def size
|
133
|
+
@store.size
|
134
|
+
end
|
135
|
+
|
136
|
+
def last_idx
|
137
|
+
@store.size - 1 unless @store.empty?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/compsci.rb
ADDED
data/test/bench/fib.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'compsci/fib'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/benchmark'
|
4
|
+
|
5
|
+
include CompSci
|
6
|
+
|
7
|
+
# CLASSIC_RANGE = [3, 5, 10, 15, 20, 25, 30, 31, 32, 33, 34, 35]
|
8
|
+
CLASSIC_RANGE = [10, 15, 20, 25, 30, 31, 32, 33, 34, 35]
|
9
|
+
# RECURSIVE_RANGE = [10, 100, 1000, 2500, 5000, 7500]
|
10
|
+
RECURSIVE_RANGE = [100, 1000, 2500, 5000, 7500]
|
11
|
+
# CACHE_RANGE = [100, 1000, 10000, 100000, 112500, 125000]
|
12
|
+
CACHE_RANGE = [100, 1000, 10000, 100000]
|
13
|
+
|
14
|
+
# this causes churn at the process level and impacts other benchmarks
|
15
|
+
# DYNAMIC_RANGE = [100, 1000, 10000, 100000, 200000, 500000]
|
16
|
+
DYNAMIC_RANGE = [100, 1000, 10000, 100000]
|
17
|
+
|
18
|
+
#SPEC_BENCHMARK = true
|
19
|
+
#CLASS_BENCHMARK = false
|
20
|
+
#BENCHMARK_IPS = true
|
21
|
+
SPEC_BENCHMARK = false
|
22
|
+
CLASS_BENCHMARK = true
|
23
|
+
BENCHMARK_IPS = false
|
24
|
+
|
25
|
+
|
26
|
+
if SPEC_BENCHMARK
|
27
|
+
describe "Fibonacci.classic Benchmark" do
|
28
|
+
bench_range do
|
29
|
+
CLASSIC_RANGE
|
30
|
+
end
|
31
|
+
|
32
|
+
fc = ["Fibonacci.classic (exponential, 0.95)", 0.95]
|
33
|
+
bench_performance_exponential(*fc) do |n|
|
34
|
+
Fibonacci.classic(n)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "Fibonacci.cache_recursive Benchmark" do
|
39
|
+
bench_range do
|
40
|
+
RECURSIVE_RANGE
|
41
|
+
end
|
42
|
+
|
43
|
+
fcr = ["Fibonacci.cache_recursive (linear, 0.95)", 0.95]
|
44
|
+
bench_performance_linear(*fcr) do |n|
|
45
|
+
Fibonacci.cache_recursive(n)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "Fibonacci.cache_iterative Benchmark" do
|
50
|
+
bench_range do
|
51
|
+
CACHE_RANGE
|
52
|
+
end
|
53
|
+
|
54
|
+
fci = ["Fibonacci.cache_iterative (linear, 0.99)", 0.99]
|
55
|
+
bench_performance_linear(*fci) do |n|
|
56
|
+
Fibonacci.cache_iterative(n)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "Fibonacci.dynamic Benchmark" do
|
61
|
+
bench_range do
|
62
|
+
DYNAMIC_RANGE
|
63
|
+
end
|
64
|
+
|
65
|
+
fd = ["Fibonacci.dynamic (linear, 0.99)", 0.99]
|
66
|
+
bench_performance_linear(*fd) do |n|
|
67
|
+
Fibonacci.dynamic(n)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if CLASS_BENCHMARK
|
73
|
+
require 'compsci/timer'
|
74
|
+
|
75
|
+
class BenchFib < Minitest::Benchmark
|
76
|
+
def bench_fib
|
77
|
+
times = CLASSIC_RANGE.map { |n|
|
78
|
+
_answer, elapsed = Timer.elapsed { Fibonacci.classic(n) }
|
79
|
+
elapsed
|
80
|
+
}
|
81
|
+
_a, _b, r2 = self.fit_exponential(CLASSIC_RANGE, times)
|
82
|
+
puts
|
83
|
+
puts "self-timed Fibonacci.classic(n) exponential fit: %0.3f" % r2
|
84
|
+
puts
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if BENCHMARK_IPS
|
90
|
+
require 'benchmark/ips'
|
91
|
+
|
92
|
+
Benchmark.ips do |b|
|
93
|
+
b.config time: 3, warmup: 0.5
|
94
|
+
num = 25
|
95
|
+
|
96
|
+
b.report("Fibonacci.classic(#{num})") {
|
97
|
+
Fibonacci.classic(num)
|
98
|
+
}
|
99
|
+
|
100
|
+
b.report("Fibonacci.cache_recursive(#{num})") {
|
101
|
+
Fibonacci.cache_recursive(num)
|
102
|
+
}
|
103
|
+
|
104
|
+
b.report("Fibonacci.cache_iterative(#{num})") {
|
105
|
+
Fibonacci.cache_iterative(num)
|
106
|
+
}
|
107
|
+
|
108
|
+
b.report("Fibonacci.dynamic(#{num})") {
|
109
|
+
Fibonacci.dynamic(num)
|
110
|
+
}
|
111
|
+
|
112
|
+
b.compare!
|
113
|
+
end
|
114
|
+
end
|
data/test/bench/heap.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'compsci/heap'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/benchmark'
|
4
|
+
|
5
|
+
include CompSci
|
6
|
+
|
7
|
+
describe "Heap#push Benchmark" do
|
8
|
+
before do
|
9
|
+
@heap = Heap.new
|
10
|
+
end
|
11
|
+
|
12
|
+
bench_performance_constant "Heap#push (constant, 0.9999)", 0.9999 do |n|
|
13
|
+
n.times { @heap.push rand 99999 }
|
14
|
+
end
|
15
|
+
end
|
data/test/bench/tree.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'compsci/tree'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/benchmark'
|
4
|
+
|
5
|
+
include CompSci
|
6
|
+
|
7
|
+
describe "Tree#push Benchmark" do
|
8
|
+
bench_range do
|
9
|
+
# note, 5000 takes way too long and is definitely not constant time
|
10
|
+
# TODO: BUG?
|
11
|
+
# [10, 100, 1000, 2000, 5000]
|
12
|
+
[10, 100, 1000, 2000]
|
13
|
+
end
|
14
|
+
|
15
|
+
bench_performance_constant "Tree#push (constant)" do |n|
|
16
|
+
tree = Tree.new Tree::Node.new 42
|
17
|
+
n.times { tree.push rand 99 }
|
18
|
+
end
|
19
|
+
end
|
data/test/fib.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'compsci/fib'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Fibonacci do
|
7
|
+
it "must calculate fib(10) == 55" do
|
8
|
+
Fibonacci.classic(10).must_equal 55
|
9
|
+
Fibonacci.cache_recursive(10).must_equal 55
|
10
|
+
Fibonacci.cache_iterative(10).must_equal 55
|
11
|
+
Fibonacci.dynamic(10).must_equal 55
|
12
|
+
end
|
13
|
+
end
|
data/test/fit.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'compsci/fit'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Fit do
|
7
|
+
before do
|
8
|
+
@xs = [1, 2, 5, 10, 20, 50, 100, 200, 500]
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "sigma" do
|
12
|
+
it "must answer correctly" do
|
13
|
+
Fit.sigma([1, 2, 3]).must_equal 6
|
14
|
+
Fit.sigma([1, 2, 3]) { |n| n ** 2 }.must_equal 14
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "error" do
|
19
|
+
it "must calculate r^2" do
|
20
|
+
Fit.error([[1, 1], [2, 2], [3, 3]]) { |x| x }.must_equal 1.0
|
21
|
+
Fit.error([[1, 1], [2, 2], [3, 4]]) { |x| x }.must_be_close_to 0.785
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# y = a
|
26
|
+
describe "constant" do
|
27
|
+
# note, this test can possibly fail depending on the uniformity of
|
28
|
+
# rand's output for our sample
|
29
|
+
it "must accept constant data" do
|
30
|
+
[0, 1, 10, 100, 1000, 9999].each { |a|
|
31
|
+
ys = @xs.map { |x| a + (rand - 0.5) }
|
32
|
+
y_bar, variance = Fit.constant(@xs, ys)
|
33
|
+
var_val = variance / ys.size
|
34
|
+
y_bar.must_be_close_to a, 0.3
|
35
|
+
var_val.must_be_close_to 0.1, 0.09
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# y = a + b*ln(x)
|
41
|
+
describe "logarithmic" do
|
42
|
+
it "must accept logarithmic data" do
|
43
|
+
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
|
44
|
+
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
|
45
|
+
ary = Fit.logarithmic(@xs, @xs.map { |x| a + b * Math.log(x) })
|
46
|
+
ary[0].must_be_close_to a
|
47
|
+
ary[1].must_be_close_to b
|
48
|
+
ary[2].must_equal 1.0
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# y = a + bx
|
55
|
+
describe "linear" do
|
56
|
+
it "must accept linear data" do
|
57
|
+
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
|
58
|
+
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
|
59
|
+
ary = Fit.linear(@xs, @xs.map { |x| a + b * x })
|
60
|
+
ary[0].must_be_close_to a
|
61
|
+
ary[1].must_be_close_to b
|
62
|
+
ary[2].must_equal 1.0
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
# test that b is near 0; (1 - b) is similar magnitude to r2 in terms of
|
68
|
+
# threshold
|
69
|
+
# here's the deal: r2 is usually pretty low, but sometimes it is up over
|
70
|
+
# 0.5, if rand() is being less than uniform in our sample
|
71
|
+
# so, accept a wide range for r2
|
72
|
+
# and let's check against 1 - b
|
73
|
+
#
|
74
|
+
# note, this test can possibly fail depending on the uniformity of
|
75
|
+
# rand's output for our sample
|
76
|
+
#
|
77
|
+
it "must accept constant data" do
|
78
|
+
r2s = []
|
79
|
+
[0, 1, 10, 100, 1000, 9999].each { |a|
|
80
|
+
ys = @xs.map { |x| a + (rand - 0.5) }
|
81
|
+
ary = Fit.linear(@xs, ys)
|
82
|
+
ary[0].must_be_close_to a, 0.4
|
83
|
+
ary[1].must_be_close_to 0, 0.05
|
84
|
+
r2s << ary[2]
|
85
|
+
}
|
86
|
+
mean_r2 = Fit.sigma(r2s) / r2s.size
|
87
|
+
mean_r2.must_be_close_to 0.15, 0.15
|
88
|
+
end
|
89
|
+
|
90
|
+
it "must reject nonlinear data" do
|
91
|
+
skip "investigate further"
|
92
|
+
# this should be quite un-linear; expect r2 below 0.8
|
93
|
+
#
|
94
|
+
# ACTUALLY
|
95
|
+
#
|
96
|
+
# the r2 for fit_linear is mostly about the relative fit of a sloped
|
97
|
+
# line compared to zero slope (i.e. y_bar)
|
98
|
+
#
|
99
|
+
# this is why a linear r2 close to 1.0 is the wrong test for fit_constant
|
100
|
+
# because the relative fit of the sloped line (slope near 0) doesn't
|
101
|
+
# "explain" much relative to y_bar
|
102
|
+
#
|
103
|
+
# in the case where y = x^3, a linear fit may still have a high r2,
|
104
|
+
# because the error for the y_bar predictor is astronomical. A super
|
105
|
+
# steep slope fits (relative to the zero slope mean) pretty well.
|
106
|
+
|
107
|
+
# this calls into question how useful r2 is, as we need it to be a
|
108
|
+
# threshold value due to noise, yet even a terrible fit like trying to
|
109
|
+
# match x^3 is hard to distinguish from noise
|
110
|
+
#
|
111
|
+
|
112
|
+
a = -50
|
113
|
+
b = 1.3
|
114
|
+
ys = @xs.map { |x| a + b * x**2 + x**3 }
|
115
|
+
ary = Fit.linear(@xs, ys)
|
116
|
+
if ary[2] > 0.85
|
117
|
+
puts
|
118
|
+
puts "fit_linear: #{ary.inspect}"
|
119
|
+
puts "y = %0.2f + %0.2f(x) (r2 = %0.3f)" % ary
|
120
|
+
puts
|
121
|
+
col1, col2 = 5, 15
|
122
|
+
puts "x".ljust(col1, ' ') + "y".ljust(col2, ' ') + "predicted"
|
123
|
+
puts '---'.ljust(col1, ' ') + '---'.ljust(col2, ' ') + '---'
|
124
|
+
@xs.zip(ys).each { |(x,y)|
|
125
|
+
puts x.to_s.ljust(col1, ' ') + y.to_s.ljust(col2, ' ') +
|
126
|
+
"%0.2f" % (ary[0] + ary[1] * x)
|
127
|
+
}
|
128
|
+
# ary[2].must_be :<, 0.8
|
129
|
+
ary[2].must_be :<, 0.9
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# y = ae^(bx)
|
135
|
+
describe "exponential" do
|
136
|
+
it "must accept exponential data" do
|
137
|
+
[0.001, 7.5, 500, 1000, 5000, 9999].each { |a|
|
138
|
+
[-1.4, -1.1, -0.1, 0.01, 0.5, 0.75].each { |b|
|
139
|
+
ary = Fit.exponential(@xs, @xs.map { |x| a * Math::E**(b * x) })
|
140
|
+
ary[0].must_be_close_to a
|
141
|
+
ary[1].must_be_close_to b
|
142
|
+
ary[2].must_equal 1.0
|
143
|
+
}
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# y = ax^b
|
149
|
+
describe "power" do
|
150
|
+
it "must accept power data" do
|
151
|
+
[0.01, 7.5, 500, 1000, 5000, 9999].each { |a|
|
152
|
+
[-114, -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
|
153
|
+
next if b == -114 # Fit.error warning: Bignum out of Float range
|
154
|
+
ary = Fit.power(@xs, @xs.map { |x| a * x**b })
|
155
|
+
ary[0].must_be_close_to a
|
156
|
+
ary[1].must_be_close_to b
|
157
|
+
ary[2].must_equal 1.0
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/test/heap.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'compsci/heap'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Heap do
|
7
|
+
before do
|
8
|
+
@maxheap = Heap.new
|
9
|
+
@minheap = Heap.new(minheap: true)
|
10
|
+
@inserts = (1..10).to_a
|
11
|
+
@inserts.each { |i|
|
12
|
+
@maxheap.push i
|
13
|
+
@minheap.push i
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "must satisfy the heap property" do
|
18
|
+
@maxheap.heap?.must_equal true
|
19
|
+
@minheap.heap?.must_equal true
|
20
|
+
@minheap.store.must_equal @inserts
|
21
|
+
@maxheap.store.wont_equal @inserts
|
22
|
+
@maxheap.store.wont_equal @inserts.reverse
|
23
|
+
end
|
24
|
+
|
25
|
+
it "must recognize heap violations" do
|
26
|
+
@minheap.store.push 0
|
27
|
+
@minheap.heap?.must_equal false
|
28
|
+
@minheap.sift_up @minheap.last_idx
|
29
|
+
@minheap.heap?.must_equal true
|
30
|
+
|
31
|
+
@minheap.store.unshift 10
|
32
|
+
@minheap.heap?.must_equal false
|
33
|
+
@minheap.sift_down 0
|
34
|
+
@minheap.heap?.must_equal true
|
35
|
+
|
36
|
+
@maxheap.store.push 10
|
37
|
+
@maxheap.heap?.must_equal false
|
38
|
+
@maxheap.sift_up @maxheap.last_idx
|
39
|
+
@maxheap.heap?.must_equal true
|
40
|
+
|
41
|
+
@maxheap.store.unshift 0
|
42
|
+
@maxheap.heap?.must_equal false
|
43
|
+
@maxheap.sift_down 0
|
44
|
+
@maxheap.heap?.must_equal true
|
45
|
+
end
|
46
|
+
end
|
data/test/timer.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'compsci/timer'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Timer do
|
7
|
+
describe "elapsed" do
|
8
|
+
it "must return the block value and positive number" do
|
9
|
+
answer, elapsed = Timer.elapsed { sleep 0.01; :foo }
|
10
|
+
answer.must_equal :foo
|
11
|
+
elapsed.must_be_close_to 0.015, 0.005
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "since" do
|
16
|
+
it "must be positive" do
|
17
|
+
start = Timer.now
|
18
|
+
sleep 0.01
|
19
|
+
Timer.since(start).must_be_close_to 0.015, 0.005
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "loop_average" do
|
24
|
+
it "return the block value and a positive number" do
|
25
|
+
start = Timer.now
|
26
|
+
answer, avg_et = Timer.loop_average(seconds: 0.25) {
|
27
|
+
sleep 0.01
|
28
|
+
:foo
|
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.3, 0.05
|
33
|
+
end
|
34
|
+
|
35
|
+
it "must repeat short loops and stop on time" do
|
36
|
+
true.must_equal true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "must not interrupt long loops" do
|
40
|
+
start = Timer.now
|
41
|
+
_answer, avg_et = Timer.loop_average(seconds: 0.1) {
|
42
|
+
sleep 0.25
|
43
|
+
}
|
44
|
+
Timer.since(start).must_be_close_to avg_et, 0.05
|
45
|
+
avg_et.must_be_close_to 0.3, 0.05
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|