variation 0.2.0
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/.document +3 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.rdoc +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +38 -0
- data/lib/variation.rb +12 -0
- data/lib/variation/change.rb +23 -0
- data/lib/variation/changes/immediate_change.rb +12 -0
- data/lib/variation/changes/linear_change.rb +13 -0
- data/lib/variation/changes/sigmoid_change.rb +14 -0
- data/lib/variation/errors.rb +7 -0
- data/lib/variation/functions/constant_function.rb +7 -0
- data/lib/variation/functions/linear_function.rb +12 -0
- data/lib/variation/functions/sigmoid_function.rb +28 -0
- data/lib/variation/profile.rb +130 -0
- data/lib/variation/range.rb +66 -0
- data/lib/variation/version.rb +4 -0
- data/spec/changes/immediate_change_spec.rb +33 -0
- data/spec/changes/linear_change_spec.rb +53 -0
- data/spec/changes/sigmoid_change_spec.rb +51 -0
- data/spec/errors_spec.rb +13 -0
- data/spec/functions/constant_function_spec.rb +20 -0
- data/spec/functions/linear_function_spec.rb +21 -0
- data/spec/functions/sigmoid_function_spec.rb +39 -0
- data/spec/profile_spec.rb +186 -0
- data/spec/range_spec.rb +104 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/variation_spec.rb +8 -0
- data/variation.gemspec +25 -0
- metadata +156 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup rdoc --title "variation Documentation" --protected
|
data/ChangeLog.rdoc
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 James Tunnell
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
= variation
|
2
|
+
|
3
|
+
* {Homepage}[https://rubygems.org/gems/variation]
|
4
|
+
* {Documentation}[http://rubydoc.info/gems/variation/frames]
|
5
|
+
* {Email}[mailto:jamestunnell at gmail.com]
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
Compute values that change with time (or some independent variable), using various transitions (immediate, linear, sigmoid) between values.
|
10
|
+
|
11
|
+
== Features
|
12
|
+
|
13
|
+
A variety of transitions:
|
14
|
+
* immediate
|
15
|
+
* linear
|
16
|
+
* sigmoid (s-shaped)
|
17
|
+
|
18
|
+
== Examples
|
19
|
+
|
20
|
+
require 'variation'
|
21
|
+
include Variation
|
22
|
+
|
23
|
+
p = Profile.new(
|
24
|
+
:start_value => 1,
|
25
|
+
:changes => {
|
26
|
+
3 => ImmediateChange.new(:end_value => 3),
|
27
|
+
5 => SigmoidChange.new(:end_value => 4, :length => 2),
|
28
|
+
6 => LinearChange.new(:end_value => 1, :length => 1)
|
29
|
+
}
|
30
|
+
)
|
31
|
+
|
32
|
+
# generate a function to calculate profile values
|
33
|
+
f = p.function
|
34
|
+
f.call(0) # should return 1
|
35
|
+
f.call(3 - 1e-5) # should return 1
|
36
|
+
f.call(3) # should return 3
|
37
|
+
f.call(4) # should return 3.5
|
38
|
+
f.call(5) # should return 4
|
39
|
+
f.call(5.5) # should return 2.5
|
40
|
+
f.call(6) # should return 1
|
41
|
+
f.call(99) # should return 1
|
42
|
+
|
43
|
+
== Requirements
|
44
|
+
|
45
|
+
== Install
|
46
|
+
|
47
|
+
$ gem install variation
|
48
|
+
|
49
|
+
== Copyright
|
50
|
+
|
51
|
+
Copyright (c) 2013 James Tunnell
|
52
|
+
|
53
|
+
See LICENSE.txt for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
begin
|
7
|
+
gem 'rspec', '~> 2.4'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
|
10
|
+
RSpec::Core::RakeTask.new
|
11
|
+
rescue LoadError => e
|
12
|
+
task :spec do
|
13
|
+
abort "Please run `gem install rspec` to install RSpec."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
task :test => :spec
|
18
|
+
task :default => :spec
|
19
|
+
|
20
|
+
require "bundler/gem_tasks"
|
21
|
+
|
22
|
+
begin
|
23
|
+
gem 'yard', '~> 0.8'
|
24
|
+
require 'yard'
|
25
|
+
|
26
|
+
YARD::Rake::YardocTask.new
|
27
|
+
rescue LoadError => e
|
28
|
+
task :yard do
|
29
|
+
abort "Please run `gem install yard` to install YARD."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
task :doc => :yard
|
33
|
+
|
34
|
+
require 'rubygems/package_task'
|
35
|
+
Gem::PackageTask.new(Gem::Specification.load('variation.gemspec')) do |pkg|
|
36
|
+
pkg.need_tar = true
|
37
|
+
pkg.need_zip = false
|
38
|
+
end
|
data/lib/variation.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'variation/version'
|
2
|
+
|
3
|
+
require 'variation/errors'
|
4
|
+
require 'variation/range'
|
5
|
+
require 'variation/functions/constant_function'
|
6
|
+
require 'variation/functions/linear_function'
|
7
|
+
require 'variation/functions/sigmoid_function'
|
8
|
+
require 'variation/change'
|
9
|
+
require 'variation/changes/immediate_change'
|
10
|
+
require 'variation/changes/linear_change'
|
11
|
+
require 'variation/changes/sigmoid_change'
|
12
|
+
require 'variation/profile'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Variation
|
2
|
+
class Change
|
3
|
+
attr_reader :length, :end_value
|
4
|
+
|
5
|
+
# Pass :length and :end_value by hash. Length must be > 0.
|
6
|
+
def initialize hashed_args
|
7
|
+
raise HashedArgMissingError unless hashed_args.has_key?(:length)
|
8
|
+
raise HashedArgMissingError unless hashed_args.has_key?(:end_value)
|
9
|
+
|
10
|
+
self.length = hashed_args[:length]
|
11
|
+
self.end_value = hashed_args[:end_value]
|
12
|
+
end
|
13
|
+
|
14
|
+
def length= length
|
15
|
+
raise NegativeLengthError if length < 0
|
16
|
+
@length = length
|
17
|
+
end
|
18
|
+
|
19
|
+
def end_value= end_value
|
20
|
+
@end_value = end_value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Variation
|
2
|
+
class LinearChange < Change
|
3
|
+
# Pass :length and :end_value by hash. Length must be > 0.
|
4
|
+
def initialize hashed_args
|
5
|
+
super(hashed_args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def transition_function start_point
|
9
|
+
end_point = [start_point[0] + length, end_value]
|
10
|
+
LinearFunction.from_points start_point, end_point
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Variation
|
2
|
+
class SigmoidChange < Change
|
3
|
+
# Pass :length and :end_value by hash. Length must be > 0.
|
4
|
+
def initialize hashed_args
|
5
|
+
super(hashed_args)
|
6
|
+
@abruptness = hashed_args[:abruptness] || 0.5
|
7
|
+
end
|
8
|
+
|
9
|
+
def transition_function start_point
|
10
|
+
end_point = [start_point[0] + length, end_value]
|
11
|
+
SigmoidFunction.from_points start_point, end_point, @abruptness
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module Variation
|
2
|
+
class NegativeLengthError < StandardError; end
|
3
|
+
class OutsideOfDomainError < StandardError; end
|
4
|
+
class NotBetweenZeroAndOneError < StandardError; end
|
5
|
+
class RangeNotIncreasingError < StandardError; end
|
6
|
+
class HashedArgMissingError < StandardError; end
|
7
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Variation
|
2
|
+
class LinearFunction
|
3
|
+
def self.from_points pt_a, pt_b, abruptness = 0.5
|
4
|
+
slope = (pt_b[1] - pt_a[1]) / (pt_b[0] - pt_a[0]).to_f
|
5
|
+
intercept = pt_a[1] - (slope * pt_a[0])
|
6
|
+
|
7
|
+
lambda do |x|
|
8
|
+
(slope * x) + intercept
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Variation
|
2
|
+
class SigmoidFunction
|
3
|
+
def self.from_points pt_a, pt_b, abruptness = 0.5
|
4
|
+
raise NotBetweenZeroAndOneError unless abruptness.between?(0,1)
|
5
|
+
|
6
|
+
domain = pt_a[0]..pt_b[0]
|
7
|
+
codomain = pt_a[1]..pt_b[1]
|
8
|
+
|
9
|
+
magn = LinearFunction.from_points([0,3],[1,9]).call(abruptness)
|
10
|
+
tanh_domain = -magn..magn
|
11
|
+
tanh_codomain = Math::tanh(-magn)..Math::tanh(magn)
|
12
|
+
|
13
|
+
domain_transformer = LinearFunction.from_points(
|
14
|
+
[domain.first, tanh_domain.first],
|
15
|
+
[domain.last, tanh_domain.last]
|
16
|
+
)
|
17
|
+
|
18
|
+
codomain_transformer = LinearFunction.from_points(
|
19
|
+
[tanh_codomain.first, codomain.first],
|
20
|
+
[tanh_codomain.last, codomain.last]
|
21
|
+
)
|
22
|
+
|
23
|
+
lambda do |x|
|
24
|
+
codomain_transformer.call(Math::tanh(domain_transformer.call(x)))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Variation
|
2
|
+
|
3
|
+
# Represent a setting that can change over time.
|
4
|
+
class Profile
|
5
|
+
|
6
|
+
attr_reader :start_value, :changes
|
7
|
+
|
8
|
+
def initialize hashed_args
|
9
|
+
raise HashedArgMissingError unless hashed_args.has_key?(:start_value)
|
10
|
+
changes = hashed_args[:changes] || {}
|
11
|
+
|
12
|
+
@start_value = hashed_args[:start_value]
|
13
|
+
@changes = changes
|
14
|
+
|
15
|
+
trim_changes_if_needed @changes
|
16
|
+
end
|
17
|
+
|
18
|
+
def length
|
19
|
+
length = 0
|
20
|
+
if @changes.any?
|
21
|
+
first_offset = @changes.keys.min
|
22
|
+
last_offset = @changes.keys.max
|
23
|
+
length = (last_offset - first_offset) + @changes[first_offset].length
|
24
|
+
end
|
25
|
+
return length
|
26
|
+
end
|
27
|
+
|
28
|
+
# def select range
|
29
|
+
# raise OutsideOfDomainError unless domain.include?(x_range.first)
|
30
|
+
# raise OutsideOfDomainError unless domain.include?(x_range.last)
|
31
|
+
|
32
|
+
# changes = {}
|
33
|
+
# @changes.each do |offset, change|
|
34
|
+
# change_end = offset + change.length
|
35
|
+
# if range.include?(offset) && range.include?(change_end)
|
36
|
+
# changes[offset] = change
|
37
|
+
# elsif range.include?(offset)
|
38
|
+
# changes[offset] = change.truncate_end(range.last - offset)
|
39
|
+
# elsif range.include?(change_end)
|
40
|
+
# changes[range.first] = change.truncate_start(change_end - range.first)
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
|
44
|
+
# changes = @changes.select do |offset, change|
|
45
|
+
# range.include?(offset) || range.include?(offset + change.length)
|
46
|
+
# end
|
47
|
+
|
48
|
+
# if changes.keys.min < range.first
|
49
|
+
|
50
|
+
# if first_offset
|
51
|
+
# last_offset = changes.keys.max
|
52
|
+
|
53
|
+
# end
|
54
|
+
# Profile.new(@start_value, changes)
|
55
|
+
# end
|
56
|
+
|
57
|
+
def end_value
|
58
|
+
if @changes.any?
|
59
|
+
@changes[@changes.keys.max].end_value
|
60
|
+
else
|
61
|
+
start_value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def function
|
66
|
+
functions = {}
|
67
|
+
|
68
|
+
prev_val = start_value
|
69
|
+
prev_offset = -Float::INFINITY
|
70
|
+
|
71
|
+
if @changes.any?
|
72
|
+
sorted_offsets = @changes.keys.sort
|
73
|
+
|
74
|
+
sorted_offsets.each_index do |i|
|
75
|
+
offset = sorted_offsets[i]
|
76
|
+
change = @changes[offset]
|
77
|
+
start_of_transition = offset - change.length
|
78
|
+
|
79
|
+
unless prev_offset == start_of_transition
|
80
|
+
functions[prev_offset...start_of_transition] = ConstantFunction.from_value(prev_val)
|
81
|
+
end
|
82
|
+
functions[start_of_transition...offset] = change.transition_function([start_of_transition, prev_val])
|
83
|
+
|
84
|
+
prev_val = change.end_value
|
85
|
+
prev_offset = offset
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
functions[prev_offset...Float::INFINITY] = ConstantFunction.from_value(prev_val)
|
90
|
+
|
91
|
+
lambda do |x|
|
92
|
+
result = functions.find {|domain,func| domain.include?(x) }
|
93
|
+
f = result[1]
|
94
|
+
f.call(x)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def at(x)
|
99
|
+
function.call(x)
|
100
|
+
end
|
101
|
+
|
102
|
+
def data(step_size)
|
103
|
+
data = {}
|
104
|
+
if @changes.any?
|
105
|
+
f = function
|
106
|
+
domain = (@changes.keys.max - length)..length
|
107
|
+
domain.step(step_size) do |x|
|
108
|
+
data[x] = f.call(x)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return data
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def trim_changes_if_needed changes
|
117
|
+
offsets = changes.keys.sort
|
118
|
+
for i in 1...offsets.count
|
119
|
+
prev_offset = offsets[i-1]
|
120
|
+
offset = offsets[i]
|
121
|
+
change = changes[offset]
|
122
|
+
|
123
|
+
if (offset - change.length) < prev_offset
|
124
|
+
change.length = offset - prev_offset
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Range
|
2
|
+
class RangeDecreasingError < StandardError; end
|
3
|
+
|
4
|
+
def decreasing?
|
5
|
+
if exclude_end?
|
6
|
+
last < first
|
7
|
+
else
|
8
|
+
last <= first
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def intersect? other
|
13
|
+
raise RangeDecreasingError if other.decreasing?
|
14
|
+
|
15
|
+
if other.first > last
|
16
|
+
return false
|
17
|
+
elsif other.first == last && exclude_end?
|
18
|
+
return false
|
19
|
+
elsif other.last < first
|
20
|
+
return false
|
21
|
+
elsif other.last == first && other.exclude_end?
|
22
|
+
return false
|
23
|
+
else
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def intersection(other)
|
29
|
+
raise RangeDecreasingError if other.decreasing?
|
30
|
+
|
31
|
+
if intersect?(other)
|
32
|
+
if include? other.first
|
33
|
+
start = other.first
|
34
|
+
else # other.first < first
|
35
|
+
start = first
|
36
|
+
end
|
37
|
+
|
38
|
+
if exclude_end?
|
39
|
+
if other.exclude_end?
|
40
|
+
return start...(last < other.last ? last : other.last)
|
41
|
+
else # other is inclusive
|
42
|
+
if other.last <= last
|
43
|
+
return start..other.last
|
44
|
+
else
|
45
|
+
return start...last
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else # self is inclusize
|
49
|
+
if other.exclude_end?
|
50
|
+
if other.last >= last
|
51
|
+
return start..last
|
52
|
+
else other.last < last
|
53
|
+
return start...other.last
|
54
|
+
end
|
55
|
+
else # other is inclusive as well
|
56
|
+
return start..(last < other.last ? last : other.last)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
alias_method :&, :intersection
|
65
|
+
alias_method :intersect, :intersection
|
66
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ImmediateChange do
|
4
|
+
describe '.new' do
|
5
|
+
context 'empty hash given' do
|
6
|
+
it 'should raise HashedArgMissingError' do
|
7
|
+
expect { ImmediateChange.new({}) }.to raise_error(HashedArgMissingError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context ':end_value given' do
|
12
|
+
it 'should raise HashedArgMissingError' do
|
13
|
+
expect { ImmediateChange.new(:end_value => 1) }.to_not raise_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#transition_function' do
|
19
|
+
it 'should return a Proc with arity of 1' do
|
20
|
+
[ 2, 5.5 ].each do |value|
|
21
|
+
ImmediateChange.new(:end_value => value).transition_function([0,0]).arity.should eq(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return a Proc, that returns the given constant value regardless of the argument' do
|
26
|
+
[ 2, 5.5 ].each do |value|
|
27
|
+
f = ImmediateChange.new(:end_value => value).transition_function([0,0])
|
28
|
+
f.call(1).should eq(value)
|
29
|
+
f.call(1000).should eq(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe LinearChange do
|
4
|
+
describe '.new' do
|
5
|
+
context 'empty hash given' do
|
6
|
+
it 'should raise HashedArgMissingError' do
|
7
|
+
expect { LinearChange.new({}) }.to raise_error(HashedArgMissingError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context ':length only given' do
|
12
|
+
it 'should raise HashedArgMissingError' do
|
13
|
+
expect { LinearChange.new(:length => 2) }.to raise_error(HashedArgMissingError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context ':end_value only given' do
|
18
|
+
it 'should raise HashedArgMissingError' do
|
19
|
+
expect { LinearChange.new(:end_value => 1) }.to raise_error(HashedArgMissingError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context ':length and :end_value given' do
|
24
|
+
it 'should raise HashedArgMissingError' do
|
25
|
+
expect { LinearChange.new(:end_value => 1, :length => 2) }.to_not raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#transition_function' do
|
31
|
+
it 'should return a Proc with arity of 1' do
|
32
|
+
LinearChange.new(:end_value => 1, :length => 1).transition_function([0,0]).arity.should eq(1)
|
33
|
+
LinearChange.new(:end_value => 3, :length => 2).transition_function([1,2]).arity.should eq(1)
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'end value 1.2 and length 2' do
|
37
|
+
before :all do
|
38
|
+
@change = LinearChange.new(:end_value => 1.2, :length => 2)
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'given start point [0,1.1]' do
|
42
|
+
it 'should produce a function that follows the equation y = 0.05x + 1.1' do
|
43
|
+
f = @change.transition_function([0,1.1])
|
44
|
+
f.call(0).should eq(1.1)
|
45
|
+
f.call(0.5).should eq(1.125)
|
46
|
+
f.call(1).should eq(1.15)
|
47
|
+
f.call(1.5).should eq(1.175)
|
48
|
+
f.call(2).should eq(1.2)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe SigmoidChange do
|
4
|
+
describe '.new' do
|
5
|
+
context 'empty hash given' do
|
6
|
+
it 'should raise HashedArgMissingError' do
|
7
|
+
expect { SigmoidChange.new({}) }.to raise_error(HashedArgMissingError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context ':length only given' do
|
12
|
+
it 'should raise HashedArgMissingError' do
|
13
|
+
expect { SigmoidChange.new(:length => 2) }.to raise_error(HashedArgMissingError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context ':end_value only given' do
|
18
|
+
it 'should raise HashedArgMissingError' do
|
19
|
+
expect { SigmoidChange.new(:end_value => 1) }.to raise_error(HashedArgMissingError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context ':length and :end_value given' do
|
24
|
+
it 'should raise HashedArgMissingError' do
|
25
|
+
expect { SigmoidChange.new(:end_value => 1, :length => 2) }.to_not raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#transition_function' do
|
31
|
+
it 'should return a Proc with arity of 1' do
|
32
|
+
SigmoidChange.new(:end_value => 1, :length => 1).transition_function([0,0]).arity.should eq(1)
|
33
|
+
SigmoidChange.new(:end_value => 3, :length => 2).transition_function([1,2]).arity.should eq(1)
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'end value 1.2 and length 2' do
|
37
|
+
before :all do
|
38
|
+
@change = SigmoidChange.new(:end_value => 1.25, :length => 2)
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'given start point [0,1.1]' do
|
42
|
+
it 'should produce a function that...' do
|
43
|
+
f = @change.transition_function([0,1.0])
|
44
|
+
f.call(0).should eq(1.0)
|
45
|
+
f.call(1).should eq(1.125)
|
46
|
+
f.call(2).should eq(1.25)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/spec/errors_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ConstantFunction do
|
4
|
+
describe '.from_value' do
|
5
|
+
it 'should return a Proc with arity of 1' do
|
6
|
+
ConstantFunction.from_value(0).arity.should eq(1)
|
7
|
+
ConstantFunction.from_value(125).arity.should eq(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return a Proc, that returns the given constant value regardless of the argument' do
|
11
|
+
f = ConstantFunction.from_value(0)
|
12
|
+
f.call(1).should eq(0)
|
13
|
+
f.call(1000).should eq(0)
|
14
|
+
|
15
|
+
f = ConstantFunction.from_value(-1)
|
16
|
+
f.call(1).should eq(-1)
|
17
|
+
f.call(1000).should eq(-1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe LinearFunction do
|
4
|
+
describe '.from_points' do
|
5
|
+
it 'should return a Proc with arity of 1' do
|
6
|
+
LinearFunction.from_points([0,0],[1,1]).arity.should eq(1)
|
7
|
+
LinearFunction.from_points([1,2],[2,3]).arity.should eq(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'given points [0,1.1] and [2,1.2]' do
|
11
|
+
it 'should produce a function that follows the equation y = 0.05x + 1.1' do
|
12
|
+
f = LinearFunction.from_points [0,1.1], [2,1.2]
|
13
|
+
f.call(0).should eq(1.1)
|
14
|
+
f.call(0.5).should eq(1.125)
|
15
|
+
f.call(1).should eq(1.15)
|
16
|
+
f.call(1.5).should eq(1.175)
|
17
|
+
f.call(2).should eq(1.2)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe SigmoidFunction do
|
4
|
+
describe '.from_points' do
|
5
|
+
context 'given abruptness less than zero' do
|
6
|
+
it 'should raise NotBetweenZeroAndOneError' do
|
7
|
+
expect { SigmoidFunction.from_points([0,0],[1,1],-0.01) }.to raise_error(NotBetweenZeroAndOneError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'given abruptness greater than 1' do
|
12
|
+
it 'should raise NotBetweenZeroAndOneError' do
|
13
|
+
expect { SigmoidFunction.from_points([0,0],[1,1],1.01) }.to raise_error(NotBetweenZeroAndOneError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'given abruptness between 0 and 1' do
|
18
|
+
it 'should not raise any error' do
|
19
|
+
(0..1).step(0.1) do |abruptness|
|
20
|
+
expect { SigmoidFunction.from_points([0,0],[1,1],abruptness) }.to_not raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return a Proc with arity of 1' do
|
26
|
+
SigmoidFunction.from_points([0,0],[1,1]).arity.should eq(1)
|
27
|
+
SigmoidFunction.from_points([1,2],[2,3]).arity.should eq(1)
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'given points [0,1.1] and [2,1.2]' do
|
31
|
+
it 'should produce a function that...' do
|
32
|
+
f = SigmoidFunction.from_points [0,1.0], [2,1.25]
|
33
|
+
f.call(0).should eq(1.0)
|
34
|
+
f.call(1).should eq(1.125)
|
35
|
+
f.call(2).should eq(1.25)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'spcore'
|
3
|
+
|
4
|
+
describe Profile do
|
5
|
+
describe '.new' do
|
6
|
+
context 'no :start_value given' do
|
7
|
+
it 'should raise HashedArgMissingError' do
|
8
|
+
expect { Profile.new({}) }.to raise_error(HashedArgMissingError)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'no :changes given' do
|
13
|
+
it 'should not raise error' do
|
14
|
+
expect { Profile.new(:start_value => 2) }.to_not raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should default changes to empty hash' do
|
18
|
+
Profile.new(:start_value => 2).changes.should be_empty
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#length' do
|
24
|
+
it 'should return difference from last change offset and (first change offset + first change length)' do
|
25
|
+
Profile.new(
|
26
|
+
:start_value => 1,
|
27
|
+
:changes => {
|
28
|
+
2 => LinearChange.new(:end_value => 2, :length => 2),
|
29
|
+
8 => SigmoidChange.new(:end_value => 0, :length => 3)
|
30
|
+
}
|
31
|
+
).length.should eq(8)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#function' do
|
36
|
+
before :all do
|
37
|
+
@profiles = [
|
38
|
+
Profile.new(
|
39
|
+
:start_value => 2.0,
|
40
|
+
:changes => {
|
41
|
+
1 => LinearChange.new(:end_value => 3.5, :length => 1),
|
42
|
+
4 => LinearChange.new(:end_value => 1.5, :length => 2)
|
43
|
+
}
|
44
|
+
),
|
45
|
+
Profile.new(
|
46
|
+
:start_value => 1,
|
47
|
+
:changes => {
|
48
|
+
2 => LinearChange.new(:end_value => 2, :length => 2),
|
49
|
+
8 => SigmoidChange.new(:end_value => 0, :length => 3)
|
50
|
+
}
|
51
|
+
),
|
52
|
+
Profile.new(
|
53
|
+
:start_value => -20,
|
54
|
+
:changes => {
|
55
|
+
-5 => ImmediateChange.new(:end_value => 3),
|
56
|
+
0 => ImmediateChange.new(:end_value => 3),
|
57
|
+
5 => SigmoidChange.new(:end_value => 2, :length => 1)
|
58
|
+
}
|
59
|
+
)
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#function#arity' do
|
64
|
+
it 'should be 1' do
|
65
|
+
@profiles.each do |profile|
|
66
|
+
profile.function.arity.should eq(1)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#function#call' do
|
72
|
+
context 'given first change offset' do
|
73
|
+
it 'should return first change value' do
|
74
|
+
@profiles.each do |profile|
|
75
|
+
first_offset = profile.changes.keys.min
|
76
|
+
first_change = profile.changes[first_offset]
|
77
|
+
profile.function.call(first_offset).should eq(first_change.end_value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'changes with length == 0 (i.e. immediate changes)' do
|
83
|
+
before :all do
|
84
|
+
@profile = Profile.new(
|
85
|
+
:start_value => -20,
|
86
|
+
:changes => {
|
87
|
+
-5 => ImmediateChange.new(:end_value => 3),
|
88
|
+
0 => ImmediateChange.new(:end_value => 5),
|
89
|
+
5 => ImmediateChange.new(:end_value => 2)
|
90
|
+
}
|
91
|
+
)
|
92
|
+
@function = @profile.function
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'given change offset' do
|
96
|
+
it 'should return change end value' do
|
97
|
+
@profile.changes.each do |offset, change|
|
98
|
+
@function.call(offset).should eq(change.end_value)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'given just before change offset' do
|
104
|
+
it 'should return change end value' do
|
105
|
+
@profile.changes.each do |offset, change|
|
106
|
+
@function.call(offset - 1e-5).should_not eq(change.end_value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'changes with length > 0' do
|
113
|
+
before :all do
|
114
|
+
@profile = Profile.new(
|
115
|
+
:start_value => -20,
|
116
|
+
:changes => {
|
117
|
+
-5 => LinearChange.new(:end_value => 3, :length => 2),
|
118
|
+
0 => LinearChange.new(:end_value => 1, :length => 1),
|
119
|
+
5 => SigmoidChange.new(:end_value => -10, :length => 2.5),
|
120
|
+
}
|
121
|
+
)
|
122
|
+
@function = @profile.function
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'given change offset' do
|
126
|
+
it 'should return change end value' do
|
127
|
+
@profile.changes.each do |offset, change|
|
128
|
+
@function.call(offset).should eq(change.end_value)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'given (change offset - change length)' do
|
134
|
+
context 'first change' do
|
135
|
+
it 'should return the start value' do
|
136
|
+
first_offset = @profile.changes.keys.min
|
137
|
+
first_change = @profile.changes[first_offset]
|
138
|
+
@function.call(first_offset - first_change.length).should eq(@profile.start_value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'second and later changes' do
|
143
|
+
it 'should return end value of previous change' do
|
144
|
+
sorted_offsets = @profile.changes.keys.sort
|
145
|
+
for i in 1...sorted_offsets.count
|
146
|
+
offset = sorted_offsets[i]
|
147
|
+
change = @profile.changes[offset]
|
148
|
+
prev_change = @profile.changes[sorted_offsets[i-1]]
|
149
|
+
|
150
|
+
@function.call(offset - change.length).should eq(prev_change.end_value)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'given (change offset - 1/2 change length)' do
|
157
|
+
context 'first change' do
|
158
|
+
it 'should return 1/2 between start value and change end value' do
|
159
|
+
first_offset = @profile.changes.keys.min
|
160
|
+
first_change = @profile.changes[first_offset]
|
161
|
+
|
162
|
+
halfway = first_offset - (first_change.length / 2.0)
|
163
|
+
expected = @profile.start_value + (first_change.end_value - @profile.start_value) / 2.0
|
164
|
+
@function.call(halfway).should eq(expected)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'second and later changes' do
|
169
|
+
it 'should return 1/2 between end value of previous change and end value of current change' do
|
170
|
+
sorted_offsets = @profile.changes.keys.sort
|
171
|
+
for i in 1...sorted_offsets.count
|
172
|
+
offset = sorted_offsets[i]
|
173
|
+
change = @profile.changes[offset]
|
174
|
+
prev_change = @profile.changes[sorted_offsets[i-1]]
|
175
|
+
|
176
|
+
halfway = offset - (change.length / 2.0)
|
177
|
+
expected = prev_change.end_value + (change.end_value - prev_change.end_value) / 2.0
|
178
|
+
@function.call(halfway).should eq(expected)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/spec/range_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Range#intersection' do
|
4
|
+
it 'should raise error if the other given range is decreasing' do
|
5
|
+
expect { (0..2).intersection(1..0) }.to raise_error
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'range includes end' do
|
9
|
+
context 'second range ends before the first begins' do
|
10
|
+
it 'should return nil' do
|
11
|
+
(5..10).intersection(1..4).should be_nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'second range starts before the first range starts' do
|
16
|
+
context 'second range ends where the first begins' do
|
17
|
+
context 'second range excludes end' do
|
18
|
+
it 'should return nil' do
|
19
|
+
(5..10).intersection(1...5).should be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'second range includes end' do
|
24
|
+
it 'should return a range with the same start/end' do
|
25
|
+
(5..10).intersection(1..5).should eq(5..5)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'second range ends inside first range' do
|
31
|
+
it 'should return a range that starts where the first range starts and ends where the second range ends' do
|
32
|
+
(5..10).intersection(4..6).should eq(5..6)
|
33
|
+
(5..10).intersection(4...6).should eq(5...6)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'second range starts where the first range starts' do
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'second range starts inside the first range' do
|
43
|
+
context 'second range ends inside the first range' do
|
44
|
+
it 'should return the second range' do
|
45
|
+
(5..10).intersection(6..9).should eq(6..9)
|
46
|
+
(5..10).intersection(6...8).should eq(6...8)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'second range ends where the first range ends' do
|
51
|
+
context 'second range is inclusive' do
|
52
|
+
it 'should return the second range' do
|
53
|
+
(5..10).intersection(6..10).should eq(6..10)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'second range is exclusize' do
|
58
|
+
it 'should return an inclusize version of the second range' do
|
59
|
+
(5..10).intersection(6...10).should eq(6..10)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'second range ends after the first range ends' do
|
65
|
+
it 'should return a range starting with the second range and ending with the first' do
|
66
|
+
(5..10).intersection(6..11).should eq(6..10)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'second range starts where the first ends' do
|
72
|
+
it 'should return a range with the same start/end' do
|
73
|
+
(5..10).intersection(10..15).should eq(10..10)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
context 'second range starts after the first ends' do
|
79
|
+
it 'should return nil' do
|
80
|
+
(5..10).intersection(11..15).should be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'range excludes end' do
|
86
|
+
before :all do
|
87
|
+
@range = 5...10
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'second range starts where the first ends' do
|
91
|
+
it 'should return nil' do
|
92
|
+
@range.intersection(10..15).should be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'second range starts inside the first range' do
|
97
|
+
context 'second range ends outside the first range' do
|
98
|
+
it 'should start where second range starts and end where first range ends (but exclude end)' do
|
99
|
+
@range.intersection(5..15).should eq(5...10)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/variation.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/variation/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "variation"
|
7
|
+
gem.version = Variation::VERSION
|
8
|
+
gem.summary = %q{Compute values that change with time, with various transitions (immediate, linear, sigmoid)}
|
9
|
+
gem.description = %q{Compute values that change with time (or some independent variable), using various transitions (immediate, linear, sigmoid) between values.}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["James Tunnell"]
|
12
|
+
gem.email = "jamestunnell@gmail.com"
|
13
|
+
gem.homepage = "https://rubygems.org/gems/variation"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'gnuplot'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rspec', '~> 2.4'
|
23
|
+
gem.add_development_dependency 'yard', '~> 0.8'
|
24
|
+
gem.add_development_dependency 'pry'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: variation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- James Tunnell
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: gnuplot
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.4'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.4'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.8'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Compute values that change with time (or some independent variable),
|
79
|
+
using various transitions (immediate, linear, sigmoid) between values.
|
80
|
+
email: jamestunnell@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .document
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- .yardopts
|
89
|
+
- ChangeLog.rdoc
|
90
|
+
- Gemfile
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.rdoc
|
93
|
+
- Rakefile
|
94
|
+
- lib/variation.rb
|
95
|
+
- lib/variation/change.rb
|
96
|
+
- lib/variation/changes/immediate_change.rb
|
97
|
+
- lib/variation/changes/linear_change.rb
|
98
|
+
- lib/variation/changes/sigmoid_change.rb
|
99
|
+
- lib/variation/errors.rb
|
100
|
+
- lib/variation/functions/constant_function.rb
|
101
|
+
- lib/variation/functions/linear_function.rb
|
102
|
+
- lib/variation/functions/sigmoid_function.rb
|
103
|
+
- lib/variation/profile.rb
|
104
|
+
- lib/variation/range.rb
|
105
|
+
- lib/variation/version.rb
|
106
|
+
- spec/changes/immediate_change_spec.rb
|
107
|
+
- spec/changes/linear_change_spec.rb
|
108
|
+
- spec/changes/sigmoid_change_spec.rb
|
109
|
+
- spec/errors_spec.rb
|
110
|
+
- spec/functions/constant_function_spec.rb
|
111
|
+
- spec/functions/linear_function_spec.rb
|
112
|
+
- spec/functions/sigmoid_function_spec.rb
|
113
|
+
- spec/profile_spec.rb
|
114
|
+
- spec/range_spec.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- spec/variation_spec.rb
|
117
|
+
- variation.gemspec
|
118
|
+
homepage: https://rubygems.org/gems/variation
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 1.8.23
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: Compute values that change with time, with various transitions (immediate,
|
143
|
+
linear, sigmoid)
|
144
|
+
test_files:
|
145
|
+
- spec/changes/immediate_change_spec.rb
|
146
|
+
- spec/changes/linear_change_spec.rb
|
147
|
+
- spec/changes/sigmoid_change_spec.rb
|
148
|
+
- spec/errors_spec.rb
|
149
|
+
- spec/functions/constant_function_spec.rb
|
150
|
+
- spec/functions/linear_function_spec.rb
|
151
|
+
- spec/functions/sigmoid_function_spec.rb
|
152
|
+
- spec/profile_spec.rb
|
153
|
+
- spec/range_spec.rb
|
154
|
+
- spec/spec_helper.rb
|
155
|
+
- spec/variation_spec.rb
|
156
|
+
has_rdoc:
|