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.
data/lib/compsci/timer.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  module CompSci
2
- module Timer
2
+ class Timer
3
+ SECS_PER_MIN = 60
4
+ MINS_PER_HOUR = 60
5
+ SECS_PER_HOUR = SECS_PER_MIN * MINS_PER_HOUR
6
+
3
7
  # lifted from seattlerb/minitest
4
8
  if defined? Process::CLOCK_MONOTONIC
5
9
  def self.now
@@ -32,5 +36,61 @@ module CompSci
32
36
  }
33
37
  return val, self.since(start) / i.to_f
34
38
  end
39
+
40
+ # YYYY-MM-DD HH::MM::SS.mmm
41
+ def self.timestamp(t)
42
+ t.strftime "%Y-%m-%d %H:%M:%S.%L"
43
+ end
44
+
45
+ # HH::MM::SS.mmm.uuuuuuuu
46
+ def self.elapsed_display(elapsed_ms, show_us: false)
47
+ elapsed_s, ms = elapsed_ms.divmod 1000
48
+ ms_only, ms_fraction = ms.round(8).divmod 1
49
+
50
+ h = elapsed_s / SECS_PER_HOUR
51
+ elapsed_s -= h * SECS_PER_HOUR
52
+ m, s = elapsed_s.divmod SECS_PER_MIN
53
+
54
+ hmsms = [[h, m, s].map { |i| i.to_s.rjust(2, '0') }.join(':'),
55
+ ms_only.to_s.rjust(3, '0')]
56
+ hmsms << (ms_fraction * 10 ** 8).round.to_s.ljust(8, '0') if show_us
57
+ hmsms.join('.')
58
+ end
59
+
60
+ def restart(t = Time.now)
61
+ @start = t
62
+ self
63
+ end
64
+ alias_method :initialize, :restart
65
+
66
+ def timestamp(t = Time.now)
67
+ self.class.timestamp t
68
+ end
69
+
70
+ def timestamp!(t = Time.now)
71
+ puts '-' * 70, timestamp(t), '-' * 70
72
+ end
73
+
74
+ def elapsed(t = Time.now)
75
+ t - @start
76
+ end
77
+
78
+ def elapsed_ms(t = Time.now)
79
+ elapsed(t) * 1000
80
+ end
81
+
82
+ def elapsed_display(t = Time.now)
83
+ self.class.elapsed_display(elapsed_ms(t))
84
+ end
85
+ alias_method :to_s, :elapsed_display
86
+ alias_method :inspect, :elapsed_display
87
+
88
+ def stamp(msg = '', t = Time.now)
89
+ format("%s %s", elapsed_display(t), msg)
90
+ end
91
+
92
+ def stamp!(msg = '', t = Time.now)
93
+ puts stamp(msg, t)
94
+ end
35
95
  end
36
96
  end
data/lib/compsci.rb CHANGED
@@ -1 +1,10 @@
1
- module CompSci; end
1
+ module CompSci
2
+ # thanks apeiros
3
+ # https://gist.github.com/rickhull/d0b579aa08c85430b0dc82a791ff12d6
4
+ def self.power_of?(num, base)
5
+ return false if base <= 1
6
+ mod = 0
7
+ num, mod = num.divmod(base) until num == 1 || mod > 0
8
+ mod == 0
9
+ end
10
+ end
@@ -1,143 +1,44 @@
1
1
  require 'compsci/fibonacci'
2
- require 'minitest/autorun'
3
- require 'minitest/benchmark'
2
+ require 'benchmark/ips'
4
3
 
5
4
  include CompSci
6
5
 
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]
6
+ # recursive benchmarks with low N; iterative for comparison
7
+ Benchmark.ips do |b|
8
+ b.config time: 3, warmup: 0.5
9
+ num = 25
13
10
 
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
- MATRIX_RANGE = [100, 1000, 10000, 100000]
11
+ b.report("Fibonacci.classic(#{num})") {
12
+ Fibonacci.classic(num)
13
+ }
18
14
 
19
- #SPEC_BENCHMARK = true
20
- #CLASS_BENCHMARK = true
21
- BENCHMARK_IPS = true
22
- SPEC_BENCHMARK = false
23
- CLASS_BENCHMARK = false
24
- #BENCHMARK_IPS = false
15
+ b.report("Fibonacci.cache_recursive(#{num})") {
16
+ Fibonacci.cache_recursive(num)
17
+ }
25
18
 
19
+ b.report("Fibonacci.cache_iterative(#{num})") {
20
+ Fibonacci.cache_iterative(num)
21
+ }
26
22
 
27
- if SPEC_BENCHMARK
28
- describe "Fibonacci.classic Benchmark" do
29
- bench_range do
30
- CLASSIC_RANGE
31
- end
32
-
33
- fc = ["Fibonacci.classic (exponential, 0.95)", 0.95]
34
- bench_performance_exponential(*fc) do |n|
35
- Fibonacci.classic(n)
36
- end
37
- end
38
-
39
- describe "Fibonacci.cache_recursive Benchmark" do
40
- bench_range do
41
- RECURSIVE_RANGE
42
- end
43
-
44
- fcr = ["Fibonacci.cache_recursive (linear, 0.95)", 0.95]
45
- bench_performance_linear(*fcr) do |n|
46
- Fibonacci.cache_recursive(n)
47
- end
48
- end
49
-
50
- describe "Fibonacci.cache_iterative Benchmark" do
51
- bench_range do
52
- CACHE_RANGE
53
- end
54
-
55
- fci = ["Fibonacci.cache_iterative (linear, 0.99)", 0.99]
56
- bench_performance_linear(*fci) do |n|
57
- Fibonacci.cache_iterative(n)
58
- end
59
- end
60
-
61
- describe "Fibonacci.dynamic Benchmark" do
62
- bench_range do
63
- DYNAMIC_RANGE
64
- end
65
-
66
- fd = ["Fibonacci.dynamic (linear, 0.99)", 0.99]
67
- bench_performance_linear(*fd) do |n|
68
- Fibonacci.dynamic(n)
69
- end
70
- end
71
-
72
- describe "Fibonacci.matrix Benchmark" do
73
- bench_range do
74
- MATRIX_RANGE
75
- end
76
-
77
- fd = ["Fibonacci.matrix (linear, 0.99)", 0.99]
78
- bench_performance_linear(*fd) do |n|
79
- Fibonacci.matrix(n)
80
- end
81
- end
23
+ b.compare!
82
24
  end
83
25
 
84
- if CLASS_BENCHMARK
85
- require 'compsci/timer'
86
-
87
- class BenchFib < Minitest::Benchmark
88
- def bench_fib
89
- times = CLASSIC_RANGE.map { |n|
90
- _answer, elapsed = Timer.elapsed { Fibonacci.classic(n) }
91
- elapsed
92
- }
93
- _a, _b, r2 = self.fit_exponential(CLASSIC_RANGE, times)
94
- puts
95
- puts "self-timed Fibonacci.classic(n) exponential fit: %0.3f" % r2
96
- puts
97
- end
98
- end
99
- end
100
-
101
- if BENCHMARK_IPS
102
- require 'benchmark/ips'
103
-
104
- # recursive benchmarks with low N; iterative for comparison
105
- Benchmark.ips do |b|
106
- b.config time: 3, warmup: 0.5
107
- num = 25
108
-
109
- b.report("Fibonacci.classic(#{num})") {
110
- Fibonacci.classic(num)
111
- }
112
-
113
- b.report("Fibonacci.cache_recursive(#{num})") {
114
- Fibonacci.cache_recursive(num)
115
- }
116
-
117
- b.report("Fibonacci.cache_iterative(#{num})") {
118
- Fibonacci.cache_iterative(num)
119
- }
120
-
121
- b.compare!
122
- end
123
-
124
- # nonrecursive benchmarks with high N
125
- Benchmark.ips do |b|
126
- b.config time: 3, warmup: 0.5
127
- num = 500
26
+ # nonrecursive benchmarks with high N
27
+ Benchmark.ips do |b|
28
+ b.config time: 3, warmup: 0.5
29
+ num = 500
128
30
 
129
- b.report("Fibonacci.cache_iterative(#{num})") {
130
- Fibonacci.cache_iterative(num)
131
- }
31
+ b.report("Fibonacci.cache_iterative(#{num})") {
32
+ Fibonacci.cache_iterative(num)
33
+ }
132
34
 
133
- b.report("Fibonacci.dynamic(#{num})") {
134
- Fibonacci.dynamic(num)
135
- }
35
+ b.report("Fibonacci.dynamic(#{num})") {
36
+ Fibonacci.dynamic(num)
37
+ }
136
38
 
137
- b.report("Fibonacci.matrix(#{num})") {
138
- Fibonacci.matrix(num)
139
- }
39
+ b.report("Fibonacci.matrix(#{num})") {
40
+ Fibonacci.matrix(num)
41
+ }
140
42
 
141
- b.compare!
142
- end
43
+ b.compare!
143
44
  end
@@ -0,0 +1,30 @@
1
+ require 'compsci/flex_node'
2
+ require 'benchmark/ips'
3
+
4
+ include CompSci
5
+
6
+ Benchmark.ips do |b|
7
+ b.config time: 3, warmup: 0.5
8
+
9
+ b.report("99x Binary ChildFlexNode#push") do
10
+ @root = ChildFlexNode.new 42
11
+ 99.times { @root.push rand(99), 2 }
12
+ end
13
+
14
+ b.report("99x Binary FlexNode#push") do
15
+ @root = FlexNode.new 42
16
+ 99.times { @root.push rand(99), 2 }
17
+ end
18
+
19
+ b.report("99x Ternary ChildFlexNode#push") do
20
+ @root = ChildFlexNode.new 42
21
+ 99.times { @root.push rand(99), 3 }
22
+ end
23
+
24
+ b.report("99x Ternary FlexNode#push") do
25
+ @root = FlexNode.new 42
26
+ 99.times { @root.push rand(99), 3 }
27
+ end
28
+
29
+ b.compare!
30
+ end
@@ -1,9 +1,11 @@
1
1
  require 'compsci/complete_tree'
2
2
  require 'minitest/autorun'
3
3
 
4
+ Minitest::Test.parallelize_me!
5
+
4
6
  include CompSci
5
7
 
6
- describe CompleteNaryTree do
8
+ describe CompleteTree do
7
9
  it "must calculate a parent index for N=2" do
8
10
  valid = {
9
11
  1 => 0,
@@ -24,10 +26,10 @@ describe CompleteNaryTree do
24
26
  -2 => -2,
25
27
  }
26
28
  valid.each { |idx, pidx|
27
- CompleteNaryTree.parent_idx(idx, 2).must_equal pidx
29
+ expect(CompleteTree.parent_idx(idx, 2)).must_equal pidx
28
30
  }
29
31
  invalid.each { |idx, pidx|
30
- CompleteNaryTree.parent_idx(idx, 2).must_equal pidx
32
+ expect(CompleteTree.parent_idx(idx, 2)).must_equal pidx
31
33
  }
32
34
  end
33
35
 
@@ -53,10 +55,10 @@ describe CompleteNaryTree do
53
55
  }
54
56
 
55
57
  valid.each { |idx, cidx|
56
- CompleteNaryTree.children_idx(idx, 2).must_equal cidx
58
+ expect(CompleteTree.children_idx(idx, 2)).must_equal cidx
57
59
  }
58
60
  invalid.each { |idx, cidx|
59
- CompleteNaryTree.children_idx(idx, 2).must_equal cidx
61
+ expect(CompleteTree.children_idx(idx, 2)).must_equal cidx
60
62
  }
61
63
  end
62
64
 
@@ -80,10 +82,10 @@ describe CompleteNaryTree do
80
82
  -2 => -1,
81
83
  }
82
84
  valid.each { |idx, pidx|
83
- CompleteNaryTree.parent_idx(idx, 3).must_equal pidx
85
+ expect(CompleteTree.parent_idx(idx, 3)).must_equal pidx
84
86
  }
85
87
  invalid.each { |idx, pidx|
86
- CompleteNaryTree.parent_idx(idx, 3).must_equal pidx
88
+ expect(CompleteTree.parent_idx(idx, 3)).must_equal pidx
87
89
  }
88
90
  end
89
91
 
@@ -102,28 +104,28 @@ describe CompleteNaryTree do
102
104
  }
103
105
 
104
106
  valid.each { |idx, cidx|
105
- CompleteNaryTree.children_idx(idx, 3).must_equal cidx
107
+ expect(CompleteTree.children_idx(idx, 3)).must_equal cidx
106
108
  }
107
109
  invalid.each { |idx, cidx|
108
- CompleteNaryTree.children_idx(idx, 3).must_equal cidx
110
+ expect(CompleteTree.children_idx(idx, 3)).must_equal cidx
109
111
  }
110
112
  end
111
113
 
112
114
  describe "instance" do
113
115
  before do
114
116
  @array = (0..99).sort_by { rand }
115
- @empty = CompleteNaryTree.new(child_slots: 5)
117
+ @empty = CompleteTree.new(child_slots: 5)
116
118
  @nonempty = CompleteQuaternaryTree.new(array: @array)
117
119
  end
118
120
 
119
121
  it "must have a size" do
120
- @empty.size.must_equal 0
121
- @nonempty.size.must_equal @array.size
122
+ expect(@empty.size).must_equal 0
123
+ expect(@nonempty.size).must_equal @array.size
122
124
  end
123
125
 
124
126
  it "must have a last_idx, nil when empty" do
125
- @empty.last_idx.nil?.must_equal true
126
- @nonempty.last_idx.must_equal 99
127
+ expect(@empty.last_idx.nil?).must_equal true
128
+ expect(@nonempty.last_idx).must_equal 99
127
129
  end
128
130
 
129
131
  # TODO: push, pop, display
data/test/compsci.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'compsci'
2
+ require 'minitest/autorun'
3
+
4
+ Minitest::Test.parallelize_me!
5
+
6
+ describe CompSci do
7
+ it "must determine if num is a power of base" do
8
+ powers = {}
9
+ basemax = 12
10
+ expmax = 10
11
+ 2.upto(basemax) { |base|
12
+ 0.upto(expmax) { |exp|
13
+ powers[base] ||= []
14
+ powers[base] << base**exp
15
+ }
16
+ }
17
+
18
+ # 12k assertions below!
19
+ 2.upto(basemax) { |base|
20
+ 1.upto(2**expmax) { |num|
21
+ expect(CompSci.power_of?(num, base)).must_equal powers[base].include?(num)
22
+ }
23
+ }
24
+ end
25
+ end
data/test/fibonacci.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'compsci/fibonacci'
2
2
  require 'minitest/autorun'
3
3
 
4
+ Minitest::Test.parallelize_me!
5
+
4
6
  include CompSci
5
7
 
6
8
  describe Fibonacci do
@@ -10,11 +12,10 @@ describe Fibonacci do
10
12
 
11
13
  it "must calculate fib(0..10)" do
12
14
  @answers.each_with_index { |ans, i|
13
- Fibonacci.classic(i).must_equal ans
14
- Fibonacci.cache_recursive(i).must_equal ans
15
- Fibonacci.cache_iterative(i).must_equal ans
16
- Fibonacci.dynamic(i).must_equal ans
17
- Fibonacci.matrix(i).must_equal ans
15
+ [:classic, :cache_recursive, :cache_iterative,
16
+ :dynamic, :matrix].each { |m|
17
+ expect(Fibonacci.send(m, i)).must_equal ans
18
+ }
18
19
  }
19
20
  end
20
21
  end
data/test/fit.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'compsci/fit'
2
2
  require 'minitest/autorun'
3
3
 
4
+ Minitest::Test.parallelize_me!
5
+
4
6
  include CompSci
5
7
 
6
8
  def noise # range: -0.5 to 0.5
@@ -13,16 +15,18 @@ describe Fit do
13
15
  end
14
16
 
15
17
  describe "Fit.sigma" do
16
- it "must answer correctly" do
17
- Fit.sigma([1, 2, 3]).must_equal 6
18
- Fit.sigma([1, 2, 3]) { |n| n ** 2 }.must_equal 14
18
+ it "answers correctly" do
19
+ expect(Fit.sigma([1, 2, 3])).must_equal 6
20
+ expect(Fit.sigma([1, 2, 3]) { |n| n ** 2 }).must_equal 14
19
21
  end
20
22
  end
21
23
 
22
24
  describe "Fit.error" do
23
- it "must calculate r^2" do
24
- Fit.error([[1, 1], [2, 2], [3, 3]]) { |x| x }.must_equal 1.0
25
- Fit.error([[1, 1], [2, 2], [3, 4]]) { |x| x }.must_be_close_to 0.785
25
+ it "calculates r^2" do
26
+ expect(Fit.error([[1, 1], [2, 2], [3, 3]]) { |x| x }).must_equal 1.0
27
+ expect(Fit.error([[1, 1], [2, 2], [3, 4]]) { |x|
28
+ x
29
+ }).must_be_close_to 0.785
26
30
  end
27
31
  end
28
32
 
@@ -32,24 +36,24 @@ describe Fit do
32
36
  # alternate measure. A low slope and r2 for linear fit, maybe.
33
37
  #
34
38
  describe "Fit.constant" do
35
- it "must stuff" do
39
+ it "returns zero variance with truly constant inputs" do
36
40
  [0, 1, 10, 100, 1000, 9999].each { |a|
37
41
  y_bar, variance = Fit.constant(@xs, @xs.map { |x| a })
38
- y_bar.must_equal a
39
- variance.must_equal 0
42
+ expect(y_bar).must_equal a
43
+ expect(variance).must_equal 0
40
44
  }
41
45
  end
42
46
  end
43
47
 
44
48
  # y = a + b*ln(x)
45
49
  describe "Fit.logarithmic" do
46
- it "must accept logarithmic data" do
50
+ it "accepts logarithmic data" do
47
51
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
48
52
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
49
53
  ary = Fit.logarithmic(@xs, @xs.map { |x| a + b * Math.log(x) })
50
- ary[0].must_be_close_to a
51
- ary[1].must_be_close_to b
52
- ary[2].must_equal 1.0
54
+ expect(ary[0]).must_be_close_to a
55
+ expect(ary[1]).must_be_close_to b
56
+ expect(ary[2]).must_equal 1.0
53
57
  }
54
58
  }
55
59
  end
@@ -57,65 +61,63 @@ describe Fit do
57
61
 
58
62
  # y = a + bx
59
63
  describe "Fit.linear" do
60
- it "must accept linear data" do
64
+ it "accepts linear data" do
61
65
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
62
66
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
63
67
  ary = Fit.linear(@xs, @xs.map { |x| a + b * x })
64
- ary[0].must_be_close_to a
65
- ary[1].must_be_close_to b
66
- ary[2].must_equal 1.0
68
+ expect(ary[0]).must_be_close_to a
69
+ expect(ary[1]).must_be_close_to b
70
+ expect(ary[2]).must_equal 1.0
67
71
  }
68
72
  }
69
73
  end
70
74
 
71
- it "must accept constant data" do
75
+ it "accepts constant data" do
72
76
  [0, 1, 10, 100, 1000, 9999].each { |a|
73
77
  ary = Fit.linear(@xs, @xs.map { |x| a })
74
- ary[0].must_equal a
75
- ary[1].must_equal 0
76
- ary[2].nan?.must_equal true
78
+ expect(ary[0]).must_equal a
79
+ expect(ary[1]).must_equal 0
80
+ expect(ary[2].nan?).must_equal true
77
81
  }
78
82
  end
79
83
 
80
84
  # note, this test can possibly fail depending on the uniformity of
81
85
  # rand's output for our sample
82
86
  #
83
- it "must accept noisy constant data" do
87
+ it "accepts noisy constant data" do
84
88
  r2s = []
85
89
  [0, 1, 10, 100, 1000, 9999].each { |a|
86
90
  ary = Fit.linear(@xs, @xs.map { |x| a + noise() })
87
- ary[0].must_be_close_to a, 0.4
88
- ary[1].must_be_close_to 0, 0.05
91
+ expect(ary[0]).must_be_close_to a, 0.4
92
+ expect(ary[1]).must_be_close_to 0, 0.05
89
93
  r2s << ary[2]
90
94
  }
91
95
  mean_r2 = Fit.sigma(r2s) / r2s.size
92
- mean_r2.must_be_close_to 0.15, 0.15
96
+ expect(mean_r2).must_be_close_to 0.15, 0.15
93
97
  end
94
98
 
95
- it "must reject x^2" do
96
- skip "it does not reject x^2 at r^2 < 0.99"
97
- xs = [1, 10, 100, 1000]
99
+ it "rejects x^2" do
100
+ xs = Array.new(99) { |i| i }
98
101
  _a, _b, r2 = Fit.linear(xs, xs.map { |x| x**2 })
99
- r2.must_be :<, 0.99
102
+ expect(r2).must_be :<, 0.99
100
103
  end
101
104
 
102
- it "must reject x^3" do
103
- skip "it does not reject x^3 at r^2 < 0.99"
104
- xs = [1, 10, 100, 1000]
105
+ it "rejects x^3" do
106
+ xs = Array.new(99) { |i| i }
105
107
  _a, _b, r2 = Fit.linear(xs, xs.map { |x| x**3 })
106
- r2.must_be :<, 0.99
108
+ expect(r2).must_be :<, 0.99
107
109
  end
108
110
  end
109
111
 
110
112
  # y = ae^(bx)
111
113
  describe "Fit.exponential" do
112
- it "must accept exponential data" do
114
+ it "accepts exponential data" do
113
115
  [0.001, 7.5, 500, 1000, 5000, 9999].each { |a|
114
116
  [-1.4, -1.1, -0.1, 0.01, 0.5, 0.75].each { |b|
115
117
  ary = Fit.exponential(@xs, @xs.map { |x| a * Math::E**(b * x) })
116
- ary[0].must_be_close_to a
117
- ary[1].must_be_close_to b
118
- ary[2].must_equal 1.0
118
+ expect(ary[0]).must_be_close_to a
119
+ expect(ary[1]).must_be_close_to b
120
+ expect(ary[2]).must_equal 1.0
119
121
  }
120
122
  }
121
123
  end
@@ -123,16 +125,56 @@ describe Fit do
123
125
 
124
126
  # y = ax^b
125
127
  describe "Fit.power" do
126
- it "must accept power data" do
128
+ it "accepts power data" do
127
129
  [0.01, 7.5, 500, 1000, 5000, 9999].each { |a|
128
130
  [-114, -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
129
- next if b == -114 # Fit.error warning: Bignum out of Float range
131
+ # [ -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
132
+ # note: on Ruby 2.4.x and older, b == -114 throws
133
+ # warning: Bignum out of Float range
134
+ # also: TruffleRuby as of Jan '22: ary[2] is NaN rather than 1.0
130
135
  ary = Fit.power(@xs, @xs.map { |x| a * x**b })
131
- ary[0].must_be_close_to a
132
- ary[1].must_be_close_to b
133
- ary[2].must_equal 1.0
136
+ expect(ary[0]).must_be_close_to a
137
+ expect(ary[1]).must_be_close_to b
138
+ expect(ary[2]).must_equal 1.0
134
139
  }
135
140
  }
136
141
  end
137
142
  end
143
+
144
+ describe "Fit.predict" do
145
+ before do
146
+ @a, @b, @x = 1, 2, 3
147
+ end
148
+
149
+ it "accepts a few different models" do
150
+ [:constant, :logarithmic, :linear, :exponential, :power].each { |model|
151
+ expect(Fit.predict(model, @a, @b, @x)).must_be_kind_of(Numeric)
152
+ }
153
+ expect { Fit.predict(:invalid, @a, @b, @x) }.must_raise RuntimeError
154
+ end
155
+
156
+ it "predicts constant relationships" do
157
+ expect(Fit.predict(:constant, @a, @b, @x)).must_equal @a
158
+ expect(Fit.predict(:constant, @a, @x, @b)).must_equal @a
159
+ expect(Fit.predict(:constant, @x, @a, @b)).must_equal @x
160
+ end
161
+
162
+ it "predicts logarithmic relationships" do
163
+ expect(Fit.predict(:logarithmic, @a, @b, @x)
164
+ ).must_equal @a + @b * Math.log(@x)
165
+ end
166
+
167
+ it "predicts linear relationships" do
168
+ expect(Fit.predict(:linear, @a, @b, @x)).must_equal @a + @b * @x
169
+ end
170
+
171
+ it "predicts exponential relationships" do
172
+ expect(Fit.predict(:exponential, @a, @b, @x)
173
+ ).must_equal @a * Math::E ** (@b * @x)
174
+ end
175
+
176
+ it "predicts power relationships" do
177
+ expect(Fit.predict(:power, @a, @b, @x)).must_equal @a * @x ** @b
178
+ end
179
+ end
138
180
  end