spliner 1.0.1 → 1.0.2
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/README.markdown +15 -1
- data/lib/spliner.rb +2 -151
- data/lib/spliner/spliner.rb +135 -0
- data/lib/spliner/spliner_section.rb +86 -0
- data/spec/spliner_spec.rb +41 -3
- metadata +5 -2
data/README.markdown
CHANGED
@@ -19,7 +19,7 @@ Spliner requires Ruby 1.9 or later. Install with rubygems:
|
|
19
19
|
Quick Start
|
20
20
|
-----------
|
21
21
|
|
22
|
-
require 'spliner'
|
22
|
+
require 'spliner'
|
23
23
|
|
24
24
|
# Initialize a spline interpolation with x range 0.0..2.0
|
25
25
|
my_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5})
|
@@ -37,6 +37,14 @@ require 'spliner'
|
|
37
37
|
ex_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5}, :extrapolate => '10%', :emethod => :hold)
|
38
38
|
xx = ex_spline[2.1] # returns 0.5
|
39
39
|
|
40
|
+
# Alternative intialization using X and Y arrays
|
41
|
+
ar_spline = Spliner::Spliner.new [1.0, 2.0, 3.0], [0.0, 3.0, 1.0]
|
42
|
+
|
43
|
+
# When duplicate X values are encountered, two or more discontinuous curves are used
|
44
|
+
two_spline = Spliner::Spliner.new [1.0, 2.0, 2.0, 3.0], [0.0, 3.0, 0.0, 1.0]
|
45
|
+
puts two_spline.sections # prints 2
|
46
|
+
|
47
|
+
|
40
48
|
Spliner is based on the interpolation described on this page
|
41
49
|
http://en.wikipedia.org/wiki/Spline_interpolation
|
42
50
|
|
@@ -48,6 +56,12 @@ Feel free to fork the project on GitHub and send fork requests. Please
|
|
48
56
|
try to have each feature separated in commits.
|
49
57
|
|
50
58
|
|
59
|
+
Home page
|
60
|
+
---------
|
61
|
+
|
62
|
+
http://www.github.com/tallakt/spliner
|
63
|
+
|
64
|
+
http://rubygems.org/gems/spliner
|
51
65
|
|
52
66
|
License
|
53
67
|
-------
|
data/lib/spliner.rb
CHANGED
@@ -1,154 +1,5 @@
|
|
1
|
-
|
2
|
-
# Spliner::Spliner
|
3
|
-
#
|
4
|
-
require 'matrix'
|
1
|
+
require 'spliner/spliner'
|
5
2
|
|
6
3
|
module Spliner
|
7
|
-
VERSION = '1.0.
|
8
|
-
|
9
|
-
# Spliner::Spliner provides cubic spline interpolation based on provided
|
10
|
-
# key points on a X-Y curve.
|
11
|
-
#
|
12
|
-
# == Example
|
13
|
-
# require 'spliner'
|
14
|
-
# # Initialize a spline interpolation with x range 0.0..2.0
|
15
|
-
# my_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5})
|
16
|
-
# # Perform interpolation on 31 values ranging from 0..2.0
|
17
|
-
# x_values = (0..30).map {|x| x / 30.0 * 2.0 }
|
18
|
-
# y_values = x_values.map {|x| my_spline[x] }
|
19
|
-
#
|
20
|
-
# http://en.wikipedia.org/wiki/Spline_interpolation
|
21
|
-
#
|
22
|
-
class Spliner
|
23
|
-
attr_reader :range
|
24
|
-
|
25
|
-
# Creates a new Spliner::Spliner object to interpolate between
|
26
|
-
# the supplied key points. The key points are provided in a hash where
|
27
|
-
# the key is the X value, and the value is the Y value. The X values
|
28
|
-
# mush be increasing and not duplicate. You must provide at least
|
29
|
-
# two values.
|
30
|
-
#
|
31
|
-
# options may take the following keys:
|
32
|
-
#
|
33
|
-
# :extrapolate
|
34
|
-
# Specify an area outside the given X values provided that should return
|
35
|
-
# a valid number. The value may be either a range (eg. -10..110) or a
|
36
|
-
# percentage value written as a string (eg '10%'). Default is no
|
37
|
-
# extrapolation.
|
38
|
-
#
|
39
|
-
# :emethod
|
40
|
-
# Specify a method of extrapolation, one of :linear (continue curve as
|
41
|
-
# a straigt line, default), or :hold (use Y values at the curve endpoints)
|
42
|
-
#
|
43
|
-
def initialize(key_points, options = {})
|
44
|
-
|
45
|
-
@points = key_points
|
46
|
-
@x = @points.keys
|
47
|
-
@y = @points.values
|
48
|
-
|
49
|
-
check_points_increasing
|
50
|
-
raise 'Interpolation needs at least two points' unless @points.size >= 2
|
51
|
-
|
52
|
-
@x_pairs = @points.keys.each_cons(2).map {|pair| pair.first..pair.last }
|
53
|
-
|
54
|
-
inv_diff = @x.each_cons(2).map {|x1, x2| 1 / (x2 - x1) }
|
55
|
-
a_diag = 2.0 * Matrix::diagonal(*vector_helper(inv_diff))
|
56
|
-
a_non_diag = Matrix::build(@points.size) do |row, col|
|
57
|
-
if row == col+ 1
|
58
|
-
inv_diff[col]
|
59
|
-
elsif col == row + 1
|
60
|
-
inv_diff[row]
|
61
|
-
else
|
62
|
-
0.0
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
a = a_diag + a_non_diag
|
67
|
-
|
68
|
-
tmp = @points.each_cons(2).map do |p1, p2|
|
69
|
-
x1, y1 = p1
|
70
|
-
x2, y2 = p2
|
71
|
-
3.0 * (y2 - y1) / (x2 - x1) ** 2.0
|
72
|
-
end
|
73
|
-
b = vector_helper(tmp)
|
74
|
-
|
75
|
-
@k = a.inv * b
|
76
|
-
|
77
|
-
options[:extrapolate].tap do |ex|
|
78
|
-
case ex
|
79
|
-
when /^\d+(\.\d+)?\s?%$/
|
80
|
-
percentage = ex[/\d+(\.\d+)?/].to_f
|
81
|
-
span = @x.last - @x.first
|
82
|
-
extra = span * percentage * 0.01
|
83
|
-
@range = (@x.first - extra)..(@x.last + extra)
|
84
|
-
when Range
|
85
|
-
@range = ex
|
86
|
-
when nil
|
87
|
-
@range = @x.first..@x.last
|
88
|
-
else
|
89
|
-
raise 'Unable to use extrapolation parameter'
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
@extrapolation_method = options[:emethod] || :linear
|
94
|
-
end
|
95
|
-
|
96
|
-
# returns an interpolated value
|
97
|
-
def get(v)
|
98
|
-
i = @x_pairs.find_index {|pair| pair.member? v }
|
99
|
-
if i
|
100
|
-
dx = @x[i + 1] - @x[i]
|
101
|
-
dy = @y[i + 1] - @y[i]
|
102
|
-
t = (v - @x[i]) / dx
|
103
|
-
a = @k[i] * dx - dy
|
104
|
-
b = -(@k[i + 1] * dx - dy)
|
105
|
-
(1 - t) * @y[i] + t * @y[i + 1] + t * (1 - t) * (a * (1 - t) + b * t)
|
106
|
-
elsif range.member? v
|
107
|
-
extrapolate(v)
|
108
|
-
else
|
109
|
-
nil
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
alias :'[]' :get
|
114
|
-
|
115
|
-
|
116
|
-
# for a vector [a, b, c] returns [a, a + b, b + c, c]
|
117
|
-
# :nodoc:
|
118
|
-
def vector_helper(a)
|
119
|
-
Vector[*([0.0] + a)] + Vector[*(a + [0.0])]
|
120
|
-
end
|
121
|
-
private :vector_helper
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
# :nodoc:
|
126
|
-
def check_points_increasing
|
127
|
-
@x.each_cons(2) do |x1, x2|
|
128
|
-
raise 'Points must form a series of x and y values where x is increasing' unless x2 > x1
|
129
|
-
end
|
130
|
-
end
|
131
|
-
private :check_points_increasing
|
132
|
-
|
133
|
-
# :nodoc:
|
134
|
-
def extrapolate(v)
|
135
|
-
case @extrapolation_method
|
136
|
-
when :hold
|
137
|
-
if v < @x.first
|
138
|
-
@y.first
|
139
|
-
else
|
140
|
-
@y.last
|
141
|
-
end
|
142
|
-
else
|
143
|
-
x, y, k = if v < @x.first
|
144
|
-
[@x.first, @y.first, @k.first]
|
145
|
-
else
|
146
|
-
[@x.last, @y.last, @k[-1]]
|
147
|
-
end
|
148
|
-
y + k * (v - x)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
private :extrapolate
|
152
|
-
|
153
|
-
end
|
4
|
+
VERSION = '1.0.2'
|
154
5
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
require 'spliner/spliner_section'
|
3
|
+
|
4
|
+
module Spliner
|
5
|
+
# Spliner::Spliner provides cubic spline interpolation based on provided
|
6
|
+
# key points on a X-Y curve.
|
7
|
+
#
|
8
|
+
# == Example
|
9
|
+
#
|
10
|
+
# require 'spliner'
|
11
|
+
# # Initialize a spline interpolation with x range 0.0..2.0
|
12
|
+
# my_spline = Spliner::Spliner.new [0.0, 1.0, 2.0], [0.0, 1.0, 0.5]
|
13
|
+
# # Perform interpolation on 31 values ranging from 0..2.0
|
14
|
+
# x_values = (0..30).map {|x| x / 30.0 * 2.0 }
|
15
|
+
# y_values = x_values.map {|x| my_spline[x] }
|
16
|
+
#
|
17
|
+
# Algorithm based on http://en.wikipedia.org/wiki/Spline_interpolation
|
18
|
+
#
|
19
|
+
class Spliner
|
20
|
+
attr_reader :range
|
21
|
+
|
22
|
+
# Creates a new Spliner::Spliner object to interpolate between
|
23
|
+
# the supplied key points.
|
24
|
+
#
|
25
|
+
# The key points shoul be in increaing X order. When duplicate X
|
26
|
+
# values are encountered, the spline is split into two or more
|
27
|
+
# discontinuous sections.
|
28
|
+
#
|
29
|
+
# The extrapolation method may be :linear by default, using a linear
|
30
|
+
# extrapolation at the curve ends using the curve derivative at the
|
31
|
+
# end points. The :hold method will use the Y value at the nearest end
|
32
|
+
# point of the curve.
|
33
|
+
#
|
34
|
+
# @overload initialize(key_points, options)
|
35
|
+
# @param key_points [Hash{Float => Float}] keys are X values in increasing order, values Y
|
36
|
+
# @param options [Hash]
|
37
|
+
# @option options [Range,String] :extrapolate ('0%') either a range or percentage, eg '10.0%'
|
38
|
+
# @option options [Symbol] :emethod (:linear) extrapolation method
|
39
|
+
#
|
40
|
+
# @overload initialize(x, y, options)
|
41
|
+
# @param x [Array(Float),Vector] the X values of the key points
|
42
|
+
# @param y [Array(Float),Vector] the Y values of the key points
|
43
|
+
# @param options [Hash]
|
44
|
+
# @option options [Range,String] :extrapolate ('0%') either a range or percentage, eg '10.0%'
|
45
|
+
# @option options [Symbol] :emethod (:linear) extrapolation method
|
46
|
+
#
|
47
|
+
def initialize(*param)
|
48
|
+
# sort parameters from two alternative initializer signatures
|
49
|
+
x, y = nil
|
50
|
+
case param.first
|
51
|
+
when Array, Vector
|
52
|
+
xx,yy, options = param
|
53
|
+
x = xx.to_a
|
54
|
+
y = yy.to_a
|
55
|
+
else
|
56
|
+
points, options = param
|
57
|
+
x = points.keys
|
58
|
+
y = points.values
|
59
|
+
end
|
60
|
+
options ||= {}
|
61
|
+
|
62
|
+
@sections = split_at_duplicates(x).map {|slice| SplinerSection.new x[slice], y[slice] }
|
63
|
+
|
64
|
+
# Handle extrapolation option parameter
|
65
|
+
options[:extrapolate].tap do |ex|
|
66
|
+
case ex
|
67
|
+
when /^\d+(\.\d+)?\s?%$/
|
68
|
+
percentage = ex[/\d+(\.\d+)?/].to_f
|
69
|
+
span = x.last - x.first
|
70
|
+
extra = span * percentage * 0.01
|
71
|
+
@range = (x.first - extra)..(x.last + extra)
|
72
|
+
when Range
|
73
|
+
@range = ex
|
74
|
+
when nil
|
75
|
+
@range = x.first..x.last
|
76
|
+
else
|
77
|
+
raise 'Unable to use extrapolation parameter'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@extrapolation_method = options[:emethod] || :linear
|
81
|
+
end
|
82
|
+
|
83
|
+
# returns the ranges at each slice between duplicate X values
|
84
|
+
def split_at_duplicates(x)
|
85
|
+
# find all indices with duplicate x values
|
86
|
+
dups = x.each_cons(2).map{|a,b| a== b}.each_with_index.select {|b,i| b }.map {|b,i| i}
|
87
|
+
([-1] + dups + [x.size - 1]).each_cons(2).map {|end0, end1| (end0 + 1)..end1 }
|
88
|
+
end
|
89
|
+
private :split_at_duplicates
|
90
|
+
|
91
|
+
|
92
|
+
# returns an interpolated value
|
93
|
+
def get(v)
|
94
|
+
i = @sections.find_index {|section| section.range.member? v }
|
95
|
+
if i
|
96
|
+
@sections[i].get v
|
97
|
+
elsif range.member? v
|
98
|
+
extrapolate(v)
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
alias :'[]' :get
|
105
|
+
|
106
|
+
# The number of non-continuous sections used
|
107
|
+
def sections
|
108
|
+
@sections.size
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
def extrapolate(v)
|
114
|
+
x, y, k = if v < first_x
|
115
|
+
[@sections.first.x.first, @sections.first.y.first, @sections.first.k.first]
|
116
|
+
else
|
117
|
+
[@sections.last.x.last, @sections.last.y.last, @sections.last.k[-1]]
|
118
|
+
end
|
119
|
+
|
120
|
+
case @extrapolation_method
|
121
|
+
when :hold
|
122
|
+
y
|
123
|
+
else
|
124
|
+
y + k * (v - x)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
private :extrapolate
|
128
|
+
|
129
|
+
def first_x
|
130
|
+
@sections.first.x.first
|
131
|
+
end
|
132
|
+
private :first_x
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
require 'spliner/spliner_section'
|
3
|
+
|
4
|
+
module Spliner
|
5
|
+
|
6
|
+
# Spliner::SplinerSection is only used via Spliner::Spliner
|
7
|
+
#
|
8
|
+
# As the spline algorithm does not handle duplicate X values well, the
|
9
|
+
# curve is split into two non continuous parts where duplicate X values
|
10
|
+
# appear. Each such part is represented by a SplinerSection
|
11
|
+
class SplinerSection
|
12
|
+
attr_reader :k, :x, :y
|
13
|
+
|
14
|
+
def initialize(x, y)
|
15
|
+
@x, @y = x, y
|
16
|
+
@x_pairs = @x.each_cons(2).map {|pair| pair.first..pair.last }
|
17
|
+
check_points_increasing
|
18
|
+
calculate_a_k
|
19
|
+
end
|
20
|
+
|
21
|
+
def range
|
22
|
+
@x.first..@x.last
|
23
|
+
end
|
24
|
+
|
25
|
+
def calculate_a_k
|
26
|
+
if @x.size > 1
|
27
|
+
inv_diff = @x.each_cons(2).map {|x1, x2| 1 / (x2 - x1) }
|
28
|
+
a_diag = 2.0 * Matrix::diagonal(*vector_helper(inv_diff))
|
29
|
+
a_non_diag = Matrix::build(@x.size) do |row, col|
|
30
|
+
if row == col+ 1
|
31
|
+
inv_diff[col]
|
32
|
+
elsif col == row + 1
|
33
|
+
inv_diff[row]
|
34
|
+
else
|
35
|
+
0.0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
a = a_diag + a_non_diag
|
40
|
+
|
41
|
+
tmp = @x.zip(@y).each_cons(2).map do |p1, p2|
|
42
|
+
x1, y1 = p1
|
43
|
+
x2, y2 = p2
|
44
|
+
3.0 * (y2 - y1) / (x2 - x1) ** 2.0
|
45
|
+
end
|
46
|
+
b = vector_helper(tmp)
|
47
|
+
|
48
|
+
@k = a.inv * b
|
49
|
+
else
|
50
|
+
@k = Vector[0.0]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
private :calculate_a_k
|
54
|
+
|
55
|
+
# returns an interpolated value
|
56
|
+
def get(v)
|
57
|
+
i = @x_pairs.find_index {|pair| pair.member? v }
|
58
|
+
if i
|
59
|
+
dx = @x[i + 1] - @x[i]
|
60
|
+
dy = @y[i + 1] - @y[i]
|
61
|
+
t = (v - @x[i]) / dx
|
62
|
+
a = @k[i] * dx - dy
|
63
|
+
b = -(@k[i + 1] * dx - dy)
|
64
|
+
(1 - t) * @y[i] + t * @y[i + 1] + t * (1 - t) * (a * (1 - t) + b * t)
|
65
|
+
elsif @x.size == 1 && @x.first == v
|
66
|
+
@y.first
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# for a vector [a, b, c] returns [a, a + b, b + c, c]
|
73
|
+
def vector_helper(a)
|
74
|
+
Vector[*([0.0] + a)] + Vector[*(a + [0.0])]
|
75
|
+
end
|
76
|
+
private :vector_helper
|
77
|
+
|
78
|
+
def check_points_increasing
|
79
|
+
@x.each_cons(2) do |x1, x2|
|
80
|
+
raise 'Key point\'s X values should be in increasing order' unless x2 > x1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
private :check_points_increasing
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
data/spec/spliner_spec.rb
CHANGED
@@ -6,11 +6,38 @@ describe Spliner::Spliner do
|
|
6
6
|
|
7
7
|
|
8
8
|
it 'should not accept x values that are not increasing' do
|
9
|
-
expect(lambda { Spliner::Spliner.new
|
9
|
+
expect(lambda { Spliner::Spliner.new [0.0, -1.0], [0.0, 1.0] }).to raise_exception
|
10
10
|
end
|
11
11
|
|
12
|
-
it 'should
|
13
|
-
|
12
|
+
it 'should support key points with a single value' do
|
13
|
+
s1 = Spliner::Spliner.new Hash[0.0, 0.0]
|
14
|
+
expect(s1.get 0.0).to be_within(0.0001).of(0.0)
|
15
|
+
expect(s1.get 1.5).to be_nil
|
16
|
+
|
17
|
+
s2 = Spliner::Spliner.new Hash[0.0, 0.0], :extrapolate => -1..1
|
18
|
+
expect(s2.get 0.0).to be_within(0.0001).of(0.0)
|
19
|
+
expect(s2.get 0.5).to be_within(0.0001).of(0.0)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'supports the Hash initializer' do
|
23
|
+
s1 = Spliner::Spliner.new Hash[0.0, 0.0, 1.0, 1.0]
|
24
|
+
expect(s1[0.5]).to be_within(0.0001).of(0.5)
|
25
|
+
|
26
|
+
s2 = Spliner::Spliner.new Hash[0.0, 0.0, 1.0, 1.0], :extrapolate => '100%'
|
27
|
+
expect(s2[0.5]).to be_within(0.0001).of(0.5)
|
28
|
+
expect(s2.range.first).to be_within(0.0001).of(-1.0)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'supports the x-y array/vector initializer' do
|
32
|
+
s1 = Spliner::Spliner.new [0.0, 1.0], [0.0, 1.0]
|
33
|
+
expect(s1[0.5]).to be_within(0.0001).of(0.5)
|
34
|
+
|
35
|
+
s2= Spliner::Spliner.new [0.0, 1.0], [0.0, 1.0], :extrapolate => '100%'
|
36
|
+
expect(s2[0.5]).to be_within(0.0001).of(0.5)
|
37
|
+
expect(s2.range.first).to be_within(0.0001).of(-1.0)
|
38
|
+
|
39
|
+
s3 = Spliner::Spliner.new Vector[0.0, 1.0], Vector[0.0, 1.0]
|
40
|
+
expect(s3[0.5]).to be_within(0.0001).of(0.5)
|
14
41
|
end
|
15
42
|
|
16
43
|
it 'should return the data points themselves' do
|
@@ -71,4 +98,15 @@ describe Spliner::Spliner do
|
|
71
98
|
expect(s3.range.first).to be_within(0.0001).of(-10.0)
|
72
99
|
expect(s3.range.last).to be_within(0.0001).of(110.0)
|
73
100
|
end
|
101
|
+
|
102
|
+
it 'splits data points with duplicate X values into separate sections' do
|
103
|
+
s = Spliner::Spliner.new [0.0, 1.0, 1.0, 2.0, 2.0, 3.0], [0.0, 0.0, 1.0, 1.0, 2.0, 2.0], :extrapolate => 3.0..4.0
|
104
|
+
expect(s.sections).to eq(3)
|
105
|
+
expect(s[-1.0]).to be_nil
|
106
|
+
expect(s[0.5]).to be_within(0.0001).of(0.0)
|
107
|
+
expect(s[1.5]).to be_within(0.0001).of(1.0)
|
108
|
+
expect(s[2.5]).to be_within(0.0001).of(2.0)
|
109
|
+
expect(s[3.5]).to be_within(0.0001).of(2.0)
|
110
|
+
expect(s[5.0]).to be_nil
|
111
|
+
end
|
74
112
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spliner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -57,6 +57,8 @@ files:
|
|
57
57
|
- README.markdown
|
58
58
|
- Rakefile
|
59
59
|
- lib/spliner.rb
|
60
|
+
- lib/spliner/spliner.rb
|
61
|
+
- lib/spliner/spliner_section.rb
|
60
62
|
- spec/spliner_spec.rb
|
61
63
|
- spliner.gemspec
|
62
64
|
homepage: http://www.github.com/tallakt/spliner
|
@@ -84,3 +86,4 @@ signing_key:
|
|
84
86
|
specification_version: 3
|
85
87
|
summary: Cubic spline interpolation library
|
86
88
|
test_files: []
|
89
|
+
has_rdoc:
|