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