compsci 0.3.0.1 → 0.3.2.1

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