polynomials 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,8 +8,10 @@ source "http://rubygems.org"
8
8
  group :development do
9
9
  gem "bundler", "~> 1.0.0"
10
10
  gem "jeweler", "~> 1.6.0"
11
- gem "rcov", ">= 0"
12
- end
13
- group :development, :tests do
11
+ gem "simplecov", ">= 0"
14
12
  gem 'awesome_print'
15
13
  end
14
+ group :production, :development, :tests do
15
+ gem "activesupport"
16
+ gem 'i18n'
17
+ end
data/Gemfile.lock CHANGED
@@ -1,20 +1,26 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ activesupport (3.0.7)
4
5
  awesome_print (0.3.2)
5
6
  git (1.2.5)
7
+ i18n (0.5.0)
6
8
  jeweler (1.6.0)
7
9
  bundler (~> 1.0.0)
8
10
  git (>= 1.2.5)
9
11
  rake
10
12
  rake (0.8.7)
11
- rcov (0.9.9)
13
+ simplecov (0.4.2)
14
+ simplecov-html (~> 0.4.4)
15
+ simplecov-html (0.4.5)
12
16
 
13
17
  PLATFORMS
14
18
  ruby
15
19
 
16
20
  DEPENDENCIES
21
+ activesupport
17
22
  awesome_print
18
23
  bundler (~> 1.0.0)
24
+ i18n
19
25
  jeweler (~> 1.6.0)
20
- rcov
26
+ simplecov
data/README.markdown ADDED
@@ -0,0 +1,73 @@
1
+ # Polynomials
2
+
3
+ ## Usage
4
+
5
+ <pre><code>
6
+ require 'polynomials'
7
+ require 'gnuplot'
8
+ include Polynomials
9
+
10
+
11
+ polynomial = Polynomial.parse(ARGV[0])
12
+
13
+ points = polynomial.roots | polynomial.local_extrema | polynomial.inflection_points
14
+
15
+ max_x = points.max_by(&amp;:x).x
16
+ min_x = points.min_by(&amp;:x).x
17
+
18
+ start = (min_x - 0.1)
19
+ stop = (max_x + 0.1)
20
+
21
+ x = start
22
+ data_x,data_y = [],[]
23
+ while x &lt; stop
24
+ data_x &lt;&lt; x
25
+ data_y &lt;&lt; polynomial.(x)
26
+ x += 0.005
27
+ end
28
+
29
+ Gnuplot.open do |gp|
30
+ Gnuplot::Plot.new(gp) do |plot|
31
+ plot.xrange &quot;[#{start}:#{stop}]&quot;
32
+ plot.title &quot;Polynomial&quot;
33
+ plot.ylabel &quot;f(x)&quot;
34
+ plot.xlabel &quot;x&quot;
35
+ plot.grid
36
+ plot.data &lt;&lt; Gnuplot::DataSet.new( [data_x,data_y] ) do |ds|
37
+ ds.with = &quot;lines&quot;
38
+ ds.linewidth = 0.1
39
+ ds.title = &quot;f(x) = #{polynomial}&quot;
40
+ end
41
+
42
+ [:inflection_point,:root, :maximum, :minimum].each do |kind_of_point|
43
+ selected_points = points.select(&amp;:&quot;#{kind_of_point}?&quot;)
44
+ plot.data &lt;&lt; Gnuplot::DataSet.new([selected_points.map(&amp;:x), selected_points.map(&amp;:y)]) do |ds|
45
+ ds.with = &quot;points&quot;
46
+ ds.linewidth = 2
47
+ ds.title = kind_of_point.to_s.pluralize.titleize
48
+ end
49
+ end
50
+ end
51
+ end
52
+ </code></pre>
53
+
54
+ ### Bash Command
55
+ <pre><code> % ruby examples/plot_only_mutual_data.rb '20.432 x^4 - 17.75 x^3 - 20x^2 + 5 x -50'</code></pre>
56
+
57
+ ### Output:
58
+ <img src="https://img.skitch.com/20110515-cwgqkt2mnbeit3c9y5djsp1cjh.jpg">
59
+
60
+ ## Contributing to polynomials
61
+
62
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
63
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
64
+ * Fork the project
65
+ * Commit and push until you are happy with your contribution
66
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
67
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
68
+
69
+ ## Copyright
70
+
71
+ Copyright (c) 2011 Manuel Korfmann. See LICENSE.txt for
72
+ further details.
73
+
data/Rakefile CHANGED
@@ -35,14 +35,14 @@ Rake::TestTask.new(:test) do |test|
35
35
  test.verbose = true
36
36
  end
37
37
 
38
- require 'rcov/rcovtask'
39
- Rcov::RcovTask.new do |test|
40
- test.libs << 'test'
41
- test.pattern = 'test/**/*_test.rb'
42
- test.verbose = true
43
- test.rcov_opts << '--exclude "gems/*"'
44
- end
45
-
38
+ #require 'rcov/rcovtask'
39
+ #Rcov::RcovTask.new do |test|
40
+ # test.libs << 'test'
41
+ # test.pattern = 'test/**/*_test.rb'
42
+ # test.verbose = true
43
+ # test.rcov_opts << '--exclude "gems/*"'
44
+ #end
45
+ #
46
46
  task :default => :test
47
47
 
48
48
  require 'rake/rdoctask'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -0,0 +1,47 @@
1
+ require 'polynomials'
2
+ require 'gnuplot'
3
+ include Polynomials
4
+
5
+
6
+ polynomial = Polynomial.parse(ARGV[0])
7
+
8
+ points = polynomial.roots | polynomial.local_extrema | polynomial.inflection_points
9
+
10
+ max_x = points.max_by(&:x).x
11
+ min_x = points.min_by(&:x).x
12
+
13
+ start = (min_x - 0.1)
14
+ stop = (max_x + 0.1)
15
+
16
+ x = start
17
+ data_x,data_y = [],[]
18
+ while x < stop
19
+ data_x << x
20
+ data_y << polynomial.(x)
21
+ x += 0.005
22
+ end
23
+
24
+ Gnuplot.open do |gp|
25
+ Gnuplot::Plot.new(gp) do |plot|
26
+ plot.xrange "[#{start}:#{stop}]"
27
+ plot.title "Polynomial"
28
+ plot.ylabel "f(x)"
29
+ plot.xlabel "x"
30
+ plot.grid
31
+ plot.data << Gnuplot::DataSet.new( [data_x,data_y] ) do |ds|
32
+ ds.with = "lines"
33
+ ds.linewidth = 0.1
34
+ ds.title = "f(x) = #{polynomial}"
35
+ end
36
+
37
+ [:inflection_point,:root, :maximum, :minimum].each do |kind_of_point|
38
+ selected_points = points.select(&:"#{kind_of_point}?")
39
+ plot.data << Gnuplot::DataSet.new([selected_points.map(&:x), selected_points.map(&:y)]) do |ds|
40
+ ds.with = "points"
41
+ ds.linewidth = 2
42
+ ds.title = kind_of_point.to_s.pluralize.titleize
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,3 @@
1
+ module Polynomials
2
+ class NotParsableError < ArgumentError;end
3
+ end
@@ -0,0 +1,64 @@
1
+ require 'mathn'
2
+ module Polynomials
3
+ module Formulas
4
+ extend self
5
+
6
+ def roots_of_cubic_function(a,b,c,d,with_complex = false)
7
+ f = ((3*c/a) - (b**2/a**2))/3
8
+ g = (((2*b**3)/a**3) - (9*b*c/a**2) + (27*d/a))/27
9
+ h = (g**2/4) + (f**3/27)
10
+ if f == 0 && g == 0 && h == 0
11
+ f = (3*c/a) - (b**2/a**2)
12
+ g = (2*b**3/a**3) - (9*b*c/a**2) + (27*d/a)
13
+ h = (g**2/4) + (f**3/27)
14
+ [-Math.cbrt(d/a)]
15
+ elsif h <= 0
16
+ i = Math.sqrt((g**2/4) - h)
17
+ j = Math.cbrt(i)
18
+ k = Math.acos(-(g / (2*i)))
19
+ l = j * -1
20
+ m = Math.cos(k/3)
21
+ n = Math.sqrt(3) * Math.sin(k/3)
22
+ p = (b/(3*a))*-1
23
+ [2*j*Math.cos(k/3) - (b/(3*a)), l*(m+n)+p, l*(m-n)+p]
24
+ else
25
+ f = (((3*c)/a) - (b**2/a**2))/3
26
+ g = ((2*b**3/a**3) - (9*b*c/a**2) + ((27*d)/a))/27
27
+ h = (g**2/4) + (f**3/27)
28
+ r = -(g/2) + Math.sqrt(h)
29
+ s = Math.cbrt(r)
30
+ t = -(g/2) - Math.sqrt(h)
31
+ u = Math.cbrt(t)
32
+ roots = [(s + u) - (b/(3*a))]
33
+ roots |= [1,-1].map do |algebraic_sign|
34
+ Complex((-(s + u)/2) - (b/(3*a)),algebraic_sign * (s-u)*Math.sqrt(3)/2)
35
+ end if with_complex
36
+ roots
37
+ end.map { |n| n.respond_to?(:round) ? n.round(10) : n }.to_set
38
+ end
39
+
40
+ def roots_of_quadratic_function(a,b,c)
41
+ p = b/a
42
+ q = c/a
43
+ denom = (p/2.0)**2 - q
44
+ return Set[] if denom < 0
45
+ root = Math.sqrt(denom)
46
+ Set.new([:+,:-].map{ |operator| (-(p/2.0)).send(operator, root)}.map { |n| n.round(10) })
47
+ end
48
+
49
+ def roots_of_quartic_function(*args)
50
+ a,b,c,d,e = args.map { |n| n/args.first }
51
+ f = c - (3*b**2/8)
52
+ g = d + (b**3/8) - (b*c/2)
53
+ h = e - (3*b**4/256) + (b**2 * c/16) - ( b*d/4)
54
+ roots = self.roots_of_cubic_function(1,(f/2),(((f**2)-(4*h))/16),-(g**2)/64,true)
55
+ combs = roots.reject{ |n| n == 0}.combination(2)
56
+ only_complex = combs.select { |comb| comb.all? { |n| n.is_a? Complex }}
57
+ p,q = (only_complex.empty? ? combs.first : only_complex.first).map { |n| Math.sqrt(n) }
58
+ r = (-g/(8*p*q))
59
+ s = b/(4*a)
60
+ roots = Set.new([p + q + r -s, p - q - r -s, -p + q - r -s, -p - q + r -s].map { |n| n.is_a?(Complex) ? (n.real if n.imaginary == 0) : n }.compact )
61
+ roots.map { |n| n.respond_to?(:round) ? n.round(10) : n }.to_set
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ module Polynomials
2
+ class Point
3
+ include Comparable
4
+ attr_accessor :x, :y
5
+
6
+ def self.inherited(subclass)
7
+ self.class_eval do
8
+ define_method :"#{subclass.name.demodulize.underscore}?" do
9
+ self.is_a? subclass
10
+ end
11
+ end
12
+ end
13
+ def initialize(x,y)
14
+ @x,@y = x,y
15
+ end
16
+
17
+ def to_s
18
+ "(#{self.x.inspect},#{self.y.inspect})"
19
+ end
20
+
21
+ def <=>(other)
22
+ if self.x < other.x
23
+ -1
24
+ elsif self.x > other.x
25
+ 1
26
+ elsif self.x == other.x && self.y == other.y
27
+ 0
28
+ end
29
+ end
30
+
31
+ def eql?(other)
32
+ self.x == other.x &&
33
+ self.y == other.y &&
34
+ other.class == self.class
35
+ end
36
+ end
37
+
38
+ class Extremum < Point
39
+ attr_accessor :kind_of_extremum
40
+
41
+ [:maximum,:minimum].each do |extremum|
42
+ self.superclass.class_eval do
43
+ define_method :"#{extremum}?" do
44
+ self.extremum? && self.kind_of_extremum == extremum
45
+ end
46
+ end
47
+ end
48
+
49
+ def initialize(*args,kind_of_extremum)
50
+ @kind_of_extremum = kind_of_extremum
51
+ super(*args)
52
+ end
53
+
54
+ def to_s
55
+ super + kind_of_extremum.to_s
56
+ end
57
+
58
+ def eql?(other)
59
+ super && self.kind_of_extremum == other.kind_of_extremum
60
+ end
61
+ end
62
+
63
+ class Root < Point
64
+ def initialize(x)
65
+ super(x,0)
66
+ end
67
+ end
68
+
69
+ class InflectionPoint < Point
70
+ end
71
+ end
@@ -0,0 +1,144 @@
1
+ module Polynomials
2
+ class Polynomial
3
+
4
+ MinMaxMapping = { 1.0 => :maximum, -1.0 => :minimum }
5
+ AfterExtremaCurvatureMapping = { maximum: :right, minimum: :left }
6
+ NegPosMinMaxExtremumMapping = {[1.0,-1.0] => :maximum,[-1.0,1.0] => :minimum}
7
+
8
+ attr_accessor :terms
9
+
10
+ def self.parse(string)
11
+ polynomial = self.new
12
+ string.split(/(?=[-+])/).each do |term|
13
+ parsed = Term.parse(term)
14
+ polynomial.terms[parsed.exponent].coefficient += parsed.coefficient
15
+ end
16
+ return polynomial
17
+ end
18
+
19
+ def initialize(*args)
20
+ self.terms = Hash.new { |hash, key| hash[key] = Term.new(key) }
21
+ unless args.empty?
22
+ args.reverse.each.with_index do |coefficient,exponent|
23
+ self.terms[exponent].coefficient += coefficient
24
+ end
25
+ end
26
+ end
27
+
28
+ def calculate(x)
29
+ self.terms.values.inject(0.0) do |acc,t|
30
+ acc + (t.coefficient.to_f * (x**t.exponent))
31
+ end
32
+ end
33
+ alias call calculate
34
+
35
+ def derivative
36
+ new_function = self.alter do |nf, term|
37
+ nf.terms[term.exponent - 1].coefficient += term.exponent * term.coefficient
38
+ end
39
+ new_function.terms.reject! { |_,t| t.coefficient == 0 }
40
+ return new_function
41
+ end
42
+
43
+ def roots
44
+ if !terms.empty? and terms.keys.none?(&:zero?)
45
+ self.alter { |nf, term| nf.terms[term.exponent-self.lt.exponent].coefficient = term.coefficient }.roots << Root.new(0.0)
46
+ else
47
+ case self.degree
48
+ when 1
49
+ Set[-self.terms[0].coefficient / self.terms[1].coefficient]
50
+ when 2
51
+ Formulas.roots_of_quadratic_function(*coefficients_till(2))
52
+ when 3
53
+ Formulas.roots_of_cubic_function(*coefficients_till(3))
54
+ when 4
55
+ Formulas.roots_of_quartic_function(*coefficients_till(4))
56
+ else
57
+ Set[]
58
+ end.map { |x| Root.new(x) }.to_set
59
+ end
60
+ end
61
+
62
+ def local_extrema
63
+ derivative = self.derivative
64
+ possible_extrema = derivative.roots.sort.map(&:x)
65
+ return Set[] if possible_extrema.empty?
66
+ samples = ([possible_extrema.first - 1] + possible_extrema.sort + [possible_extrema.last + 1]).each_cons(2).map do |before,after|
67
+ (before + after)/2
68
+ end
69
+ possible_extrema.zip(samples.each_cons(2)).inject(Set[]) do |set,(pe,(after,before))|
70
+ yafter = derivative.calculate(after)
71
+ ybefore = derivative.calculate(before)
72
+ kind_of_extremum = NegPosMinMaxExtremumMapping[[yafter/yafter.abs,ybefore/ybefore.abs]]
73
+ set << Extremum.new(pe,self.calculate(pe), kind_of_extremum) if kind_of_extremum
74
+ set
75
+ end
76
+ end
77
+
78
+ def curvature_behaviour
79
+ hash = Hash.new {|h,k|h[k]=Set.new}
80
+ return hash if self.degree > 5
81
+ self.derivative.extrema.sort.each_cons(2).group_by do |start,_|
82
+ kind_of_curvature = AfterExtremaCurvatureMapping[start.kind_of_extremum]
83
+ end.tap { |h| h.values.each { |v| v.map! { |s,e| Range.new(s.x,e.x) }}}
84
+ end
85
+
86
+ def degree
87
+ self.terms.keys.max || 0
88
+ end
89
+
90
+ def to_s
91
+ terms.sort_by { |_,t| -t.exponent }.inject("") do |string,(_,term)|
92
+ string << term.to_s unless term.coefficient.zero? && !term.exponent.zero?
93
+ string
94
+ end.strip.sub(/\A\+\s/, '')
95
+ end
96
+
97
+ def ==(other)
98
+ delete_zeros = proc{ |_,t| t.coefficient.zero? }
99
+ self.terms.delete_if(&delete_zeros) == other.terms.delete_if(&delete_zeros)
100
+ end
101
+
102
+ def extrema
103
+ extrema = local_extrema
104
+ unless self.degree == 0
105
+ a = self.gt.coefficient
106
+ max_or_min = (self.degree.even? ? [1.0,1.0] : [-1.0,1.0]).map { |n| (n * a)/a.abs }
107
+ max_or_min.zip([-Infinity,Infinity]).each do |kind_of_extremum, x|
108
+ extrema << Extremum.new(x,nil,MinMaxMapping[kind_of_extremum])
109
+ end
110
+ end
111
+ return extrema
112
+ end
113
+
114
+ def inflection_points
115
+ self.derivative.local_extrema.map { |p| InflectionPoint.new(p.x,self.calculate(p.x)) }.to_set
116
+ end
117
+
118
+ def gt
119
+ self.terms[self.degree]
120
+ end
121
+
122
+ def lt
123
+ self.terms[self.terms.min_by{ |_,t| t.exponent}.first]
124
+ end
125
+
126
+ def coefficients_till(n)
127
+ coefficients = []
128
+ (0..n).each do |e|
129
+ coefficients << self.terms[e].coefficient
130
+ end
131
+ return coefficients.reverse
132
+ end
133
+ private :coefficients_till
134
+
135
+ def alter
136
+ new_function = self.class.new
137
+ self.terms.values.each do |term|
138
+ yield new_function, term
139
+ end
140
+ return new_function
141
+ end
142
+ protected :alter
143
+ end
144
+ end
@@ -0,0 +1,48 @@
1
+ module Polynomials
2
+ class Term
3
+ attr_accessor :coefficient, :exponent
4
+
5
+ def initialize(exponent = 0, coefficient = 0)
6
+ @coefficient, @exponent = coefficient, exponent
7
+ end
8
+
9
+ def self.parse(string)
10
+ term = self.new
11
+ float = /\d+(?:\.\d+)?/
12
+ integer = /\d+/
13
+ polynomial_regex = /
14
+ \A\s*
15
+ (?<algebraic_sign>[-+]?)
16
+ \s*
17
+ (?<coefficient>#{float})?
18
+ \s*
19
+ (?<power>x(?:\^(?<exponent>#{integer}))?)?
20
+ \s*\Z
21
+ /x
22
+
23
+ raw_data = string.match(polynomial_regex)
24
+ raise NotParsableError unless raw_data
25
+ term.coefficient = (raw_data[:algebraic_sign] + ( raw_data[:coefficient] || "1")).to_f
26
+ term.exponent = raw_data[:power] ? (raw_data[:exponent] || 1).to_i : 0
27
+ term
28
+ end
29
+
30
+ def to_s
31
+ pretty_coeffiecent = coefficient.denominator == 1 ? coefficient.abs.to_i : coefficient.abs
32
+ algebraic_sign = coefficient < 0 ? '-' : '+'
33
+ power = "x#{"^#{exponent}" unless exponent == 1}"
34
+ "#{algebraic_sign} #{pretty_coeffiecent}#{ power unless exponent.zero?} "
35
+ end
36
+
37
+ def dup
38
+ duplicate = self.class.new
39
+ duplicate.coefficient = self.coefficient
40
+ duplicate.exponent = self.exponent
41
+ duplicate
42
+ end
43
+
44
+ def ==(other)
45
+ self.coefficient == other.coefficient && self.exponent == other.exponent
46
+ end
47
+ end
48
+ end
data/lib/polynomials.rb CHANGED
@@ -1,145 +1,19 @@
1
- require 'set'
2
- require_relative 'term'
3
- require_relative 'core_ext/math'
4
-
5
- class Polynomial
6
-
7
- MinMaxMapping = { 1.0 => :max, -1.0 => :min }
8
- AfterextremaCurvatureMapping = { max: :right, min: :left }
9
- NegPosMinMaxExtremumMapping = {[1.0,-1.0] => :max,[-1.0,1.0] => :min}
10
-
11
- attr_accessor :terms
12
-
13
- def self.parse(string)
14
- polynomial = self.new
15
- string.split(/(?=[-+])/).each do |term|
16
- parsed = Term.parse(term)
17
- polynomial.terms[parsed.exponent].coefficient += parsed.coefficient
18
- end
19
- return polynomial
20
- end
21
-
22
- def initialize(*args)
23
- self.terms = Hash.new { |hash, key| hash[key] = Term.new(key) }
24
- unless args.empty?
25
- args.reverse.each.with_index do |coefficient,exponent|
26
- self.terms[exponent].coefficient += coefficient
27
- end
28
- end
29
- end
30
-
31
- def calculate(x)
32
- self.terms.values.inject(0.0) do |acc,t|
33
- acc + (t.coefficient.to_f * (x**t.exponent))
34
- end
35
- end
36
-
37
- def derivative
38
- new_function = self.alter do |nf, term|
39
- nf.terms[term.exponent - 1].coefficient += term.exponent * term.coefficient
40
- end
41
- new_function.terms.reject! { |_,t| t.coefficient == 0 }
42
- return new_function
43
- end
44
-
45
- def roots
46
- if !terms.empty? and terms.keys.none?(&:zero?)
47
- self.alter { |nf, term| nf.terms[term.exponent-self.lt.exponent].coefficient = term.coefficient }.roots << 0.0
48
- else
49
- case self.degree
50
- when 1
51
- Set[-self.terms[0].coefficient / self.terms[1].coefficient]
52
- when 2
53
- Math.roots_of_quadratic_function(*coefficients_till(2))
54
- when 3
55
- Math.roots_of_cubic_function(*coefficients_till(3))
56
- when 4
57
- Math.roots_of_quartic_function(*coefficients_till(4))
58
- else
59
- Set[]
60
- end
61
- end
62
- end
1
+ $LOAD_PATH << File.join(File.dirname(File.expand_path(__FILE__)), 'polynomials')
63
2
 
64
- def local_extrema
65
- derivative = self.derivative
66
- max_min_extremum = Hash.new { |hash,key| hash[key] = Set.new }
67
- possible_extrema = derivative.roots.sort
68
- unless possible_extrema.empty?
69
- samples = ([possible_extrema.first - 1] + possible_extrema.sort + [possible_extrema.last + 1]).each_cons(2).map do |before,after|
70
- (before + after)/2
71
- end
3
+ require 'active_support/inflector'
72
4
 
73
- possible_extrema.zip(samples.each_cons(2)).each do |pe,(after,before)|
74
- yafter = derivative.calculate(after)
75
- ybefore = derivative.calculate(before)
76
- kind_of_extremum = NegPosMinMaxExtremumMapping[[yafter/yafter.abs,ybefore/ybefore.abs]]
77
- max_min_extremum[kind_of_extremum] << pe if kind_of_extremum
78
- end
79
- end
80
- return max_min_extremum
81
- end
82
-
83
- def curvature_behaviour
84
- hash = Hash.new {|h,k|h[k]=Set.new}
85
- return hash if self.degree > 5
86
- extrema = self.derivative.extrema
87
- all_extremas = extrema.values.inject(Set[],&:|).sort
88
- all_extremas.each_cons(2).map { |s,e| Range.new(s,e) }.group_by do |range|
89
- kind_of_curvature = AfterextremaCurvatureMapping[extrema.find { |k,v| v.include?(range.begin) }.first]
90
- end
91
- end
92
-
93
- def degree
94
- self.terms.keys.max || 0
95
- end
96
-
97
- def to_s
98
- terms.sort_by { |_,t| -t.exponent }.inject("") do |string,(_,term)|
99
- string << term.to_s unless term.coefficient.zero? && !term.exponent.zero?
100
- string
101
- end.strip.sub(/\A\+\s/, '')
102
- end
103
-
104
- def ==(other)
105
- delete_zeros = proc{ |_,t| t.coefficient.zero? }
106
- self.terms.delete_if(&delete_zeros) == other.terms.delete_if(&delete_zeros)
107
- end
108
-
109
- def extrema
110
- extrema = local_extrema
111
- unless self.degree == 0
112
- a = self.gt.coefficient
113
- max_or_min = (self.degree.even? ? [1.0,1.0] : [-1.0,1.0]).map { |n| (n * a)/a.abs }
114
- extrema[MinMaxMapping[max_or_min.first]] << -1.0/0
115
- extrema[MinMaxMapping[max_or_min.last]] << 1.0/0
116
- end
117
- return extrema
118
- end
119
-
120
- def gt
121
- self.terms[self.degree]
122
- end
5
+ ActiveSupport::Inflector.inflections do |inflect|
6
+ inflect.plural /mum$/, '\1ma'
7
+ inflect.singular /ma$/, '\1mum'
8
+ end
123
9
 
124
- def lt
125
- self.terms[self.terms.min_by{ |_,t| t.exponent}.first]
126
- end
10
+ require 'polynomial'
11
+ require 'set'
12
+ require 'term'
13
+ require 'point'
14
+ require 'formulas'
15
+ require 'exceptions'
127
16
 
128
- def coefficients_till(n)
129
- coefficients = []
130
- (0..n).each do |e|
131
- coefficients << self.terms[e].coefficient
132
- end
133
- return coefficients.reverse
134
- end
135
- private :coefficients_till
136
17
 
137
- def alter
138
- new_function = self.class.new
139
- self.terms.values.each do |term|
140
- yield new_function, term
141
- end
142
- return new_function
143
- end
144
- protected :alter
18
+ module Polynomials
145
19
  end
data/polynomials.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{polynomials}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Manuel Korfmann"]
12
- s.date = %q{2011-05-14}
12
+ s.date = %q{2011-05-15}
13
13
  s.description = %q{
14
14
  This is a gem dedicated to parsing, manipulating and finding roots,extrema and inflection points of polynomials.
15
15
  It can solve polynomial equations with a degree up to 4 by implementing formulas for solving quadratic, cubic and quartic functions.
@@ -17,22 +17,26 @@ It can solve polynomial equations with a degree up to 4 by implementing formulas
17
17
  s.email = %q{manu@korfmann.info}
18
18
  s.extra_rdoc_files = [
19
19
  "LICENSE.txt",
20
- "README.rdoc"
20
+ "README.markdown"
21
21
  ]
22
22
  s.files = [
23
23
  ".document",
24
24
  "Gemfile",
25
25
  "Gemfile.lock",
26
26
  "LICENSE.txt",
27
- "README.rdoc",
27
+ "README.markdown",
28
28
  "Rakefile",
29
29
  "VERSION",
30
- "lib/core_ext/math.rb",
30
+ "examples/plot_only_mutual_data.rb",
31
31
  "lib/polynomials.rb",
32
- "lib/term.rb",
32
+ "lib/polynomials/exceptions.rb",
33
+ "lib/polynomials/formulas.rb",
34
+ "lib/polynomials/point.rb",
35
+ "lib/polynomials/polynomial.rb",
36
+ "lib/polynomials/term.rb",
33
37
  "polynomials.gemspec",
34
- "test/helper.rb",
35
- "test/math_test.rb",
38
+ "test/formulas_test.rb",
39
+ "test/point_test.rb",
36
40
  "test/polynomial_test.rb",
37
41
  "test/term_test.rb",
38
42
  "test/test_helper.rb"
@@ -50,19 +54,25 @@ It can solve polynomial equations with a degree up to 4 by implementing formulas
50
54
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
55
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
52
56
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
53
- s.add_development_dependency(%q<rcov>, [">= 0"])
57
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
54
58
  s.add_development_dependency(%q<awesome_print>, [">= 0"])
59
+ s.add_development_dependency(%q<activesupport>, [">= 0"])
60
+ s.add_development_dependency(%q<i18n>, [">= 0"])
55
61
  else
56
62
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
57
63
  s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
58
- s.add_dependency(%q<rcov>, [">= 0"])
64
+ s.add_dependency(%q<simplecov>, [">= 0"])
59
65
  s.add_dependency(%q<awesome_print>, [">= 0"])
66
+ s.add_dependency(%q<activesupport>, [">= 0"])
67
+ s.add_dependency(%q<i18n>, [">= 0"])
60
68
  end
61
69
  else
62
70
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
71
  s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
64
- s.add_dependency(%q<rcov>, [">= 0"])
72
+ s.add_dependency(%q<simplecov>, [">= 0"])
65
73
  s.add_dependency(%q<awesome_print>, [">= 0"])
74
+ s.add_dependency(%q<activesupport>, [">= 0"])
75
+ s.add_dependency(%q<i18n>, [">= 0"])
66
76
  end
67
77
  end
68
78
 
@@ -1,13 +1,15 @@
1
- require_relative 'test_helper'
1
+ require "#{File.dirname(File.expand_path(__FILE__))}/test_helper"
2
+
2
3
  class TestMath < MiniTest::Unit::TestCase
4
+ include Polynomials
3
5
  def test_roots_for_constant_functions
4
6
  polynomial = Polynomial.parse('5')
5
- assert_equal Set[],polynomial.roots
7
+ assert_set_eql Set[],polynomial.roots
6
8
  end
7
9
 
8
10
  def test_roots_for_linear_functions
9
11
  polynomial = Polynomial.parse('5x + 2')
10
- assert_equal Set[-2.0/5.0],polynomial.roots
12
+ assert_set_eql Set[Root.new(-2.0/5.0)],polynomial.roots
11
13
  end
12
14
 
13
15
  def test_roots_quadratic_functions
@@ -16,50 +18,50 @@ class TestMath < MiniTest::Unit::TestCase
16
18
  q = -40.0/3.0
17
19
  root = Math.sqrt((p/2)**2 - q)
18
20
  fraction = -(p/2)
19
- assert_equal Set[(fraction - root).round(10), (fraction + root).round(10)],polynomial.roots
21
+ assert_set_eql Set[Root.new((fraction - root).round(10)), Root.new((fraction + root).round(10))],polynomial.roots
20
22
 
21
23
  polynomial = Polynomial.parse('3x^2 - 40')
22
- assert_equal Set[Math.sqrt(40.0/3).round(10), -Math.sqrt(40.0/3).round(10)], polynomial.roots
24
+ assert_set_eql Set[Root.new(Math.sqrt(40.0/3).round(10)), Root.new(-Math.sqrt(40.0/3).round(10))], polynomial.roots
23
25
  end
24
26
 
25
27
  def test_roots_for_cubic_functions
26
28
  polynomial = Polynomial.parse('1x^3 + 1x^2 + 2x - 1')
27
29
  assert_equal(1, polynomial.roots.size)
28
- assert_equal Set[0.3926467817], polynomial.roots
30
+ assert_set_eql Set[Root.new(0.3926467817)], polynomial.roots
29
31
 
30
32
  polynomial = Polynomial.parse('2x^3 - 4x^2 - 22x + 24')
31
- assert_equal Set[4.0,-3.0,1.0], polynomial.roots
33
+ assert_set_eql Set[Root.new(4.0),Root.new(-3.0),Root.new(1.0)], polynomial.roots
32
34
  end
33
35
 
34
36
  def test_roots_for_cubic_functions_with_complex
35
37
  coefficients = Polynomial.parse('3x^3 - 10x^2 + 14x + 27').terms.first(4).map{ |t| t.last.coefficient }
36
- assert_equal Set[-1.0, Complex(2.1666666666666634,2.0749832663314605), Complex(2.1666666666666634,-2.0749832663314605)],Math.roots_of_cubic_function(*coefficients, true)
38
+ assert_equal Set[-1.0, Complex(2.1666666666666634,2.0749832663314605), Complex(2.1666666666666634,-2.0749832663314605)],Formulas.roots_of_cubic_function(*coefficients, true)
37
39
  end
38
40
 
39
41
  def test_roots_for_cubic_functions_one_real_all_equal
40
42
  polynomial = Polynomial.parse('1x^3 + 6x^2 + 12x + 8')
41
43
  assert_equal(1, polynomial.roots.size)
42
- assert_equal(-2, polynomial.roots.first)
44
+ assert_equal(Root.new(-2), polynomial.roots.first)
43
45
  end
44
46
 
45
47
  def test_roots_for_quartic_functions
46
48
  polynomial = Polynomial.parse('3x^4 + 6x^3 - 123x^2 - 126x + 1080')
47
- assert_equal Set[5.0, 3.0, -4.0, -6.0], polynomial.roots
49
+ assert_set_eql Set[Root.new(5.0), Root.new(3.0), Root.new(-4.0), Root.new(-6.0)], polynomial.roots
48
50
 
49
51
  polynomial = Polynomial.parse('-20x^4 + 5x^3 + 17x^2 - 29x + 87')
50
- assert_equal Set[-1.6820039266,1.4875831103], polynomial.roots
52
+ assert_set_eql Set[Root.new(-1.6820039266),Root.new(1.4875831103)], polynomial.roots
51
53
  end
52
54
 
53
55
  def test_roots_without_term_with_exponent_of_zero
54
56
  polynomial = Polynomial.parse('3x^3 + 3x^2')
55
- assert_equal Set[0.0,-1.0], polynomial.roots
57
+ assert_set_eql Set[Root.new(0.0),Root.new(-1.0)], polynomial.roots
56
58
 
57
59
  polynomial = Polynomial.parse('1 x^4')
58
- assert_equal Set[0.0], polynomial.roots
60
+ assert_set_eql Set[Root.new(0.0)], polynomial.roots
59
61
  end
60
62
 
61
63
  def test_roots_biquadratic_equation
62
64
  polynomial = Polynomial.parse('4x^4 + 2x^2 - 1')
63
- assert_equal Set[0.5558929703, -0.5558929703], polynomial.roots
65
+ assert_set_eql Set[Root.new(0.5558929703), Root.new(-0.5558929703)], polynomial.roots
64
66
  end
65
67
  end
@@ -0,0 +1,51 @@
1
+ require "#{File.dirname(File.expand_path(__FILE__))}/test_helper"
2
+ class PointTest < MiniTest::Unit::TestCase
3
+ def test_initalizer
4
+ point = Point.new(2,Polynomial.new(7).calculate(2))
5
+ assert_equal 2, point.x
6
+ assert_equal 7, point.y
7
+ end
8
+
9
+ def test_to_s
10
+ point = Point.new(1,Polynomial.new(1).calculate(1))
11
+ assert_equal "(1,1.0)", point.to_s
12
+ end
13
+
14
+ def test_equality
15
+ point1 = Extremum.new(5,Polynomial.new(0.0).calculate(0), :maximum)
16
+ point2 = Extremum.new(5,Polynomial.new(0).calculate(0), :maximum)
17
+ point3 = Point.new(5,Polynomial.new(0).calculate(0))
18
+ assert point1.eql?(point2)
19
+ refute point3.eql?(point2)
20
+ end
21
+
22
+ def test_comparison
23
+ point1 = Point.new(1,Polynomial.new(5).calculate(1))
24
+ point2 = Point.new(3,Polynomial.new(5).calculate(3))
25
+ point3 = Point.new(0.5,Polynomial.new(5).calculate(0.5))
26
+ point4 = Point.new(0.5,Polynomial.new(5).calculate(0.5))
27
+ assert point1 < point2
28
+ assert point1 > point3
29
+ assert point4 <= point3
30
+ assert point4 <= point2
31
+ end
32
+
33
+ def test_root
34
+ assert_equal '(50,0)', Root.new(50).to_s
35
+ end
36
+
37
+ def test_boolean_max_min_meths
38
+ assert_equal true,Extremum.new(2,3,:maximum).maximum?
39
+ assert_equal true,Extremum.new(2,3,:minimum).minimum?
40
+ assert_equal false,Extremum.new(2,3,:maximum).minimum?
41
+ assert_equal false, Root.new(2).minimum?
42
+ end
43
+
44
+ def test_bolean_check_methods
45
+ assert_equal true, InflectionPoint.new(2,3).inflection_point?
46
+ assert_equal true, Extremum.new(2,3, :maximum).extremum?
47
+ assert_equal true, Root.new(2).root?
48
+ assert_equal false, Root.new(3).extremum?
49
+ end
50
+ end
51
+
@@ -1,7 +1,8 @@
1
- require_relative 'test_helper'
1
+ require "#{File.dirname(File.expand_path(__FILE__))}/test_helper"
2
2
 
3
3
  Infinity = 1.0/0
4
4
  class TestPolynomial < MiniTest::Unit::TestCase
5
+ include Polynomials
5
6
  def test_to_s
6
7
  polynomial = Polynomial.parse('5x + 2x^2 + 20')
7
8
  assert_equal '2x^2 + 5x + 20', polynomial.to_s
@@ -45,23 +46,23 @@ class TestPolynomial < MiniTest::Unit::TestCase
45
46
 
46
47
  def test_extrema
47
48
  polynomial = Polynomial.parse('3x^2 + 2x + 1')
48
- assert_equal({ min: Set[-2.0/6.0] }, polynomial.local_extrema)
49
+ assert_set_eql(Set[ Extremum.new(-2.0/6.0,polynomial.calculate(-2.0/6.0), :minimum) ], polynomial.local_extrema)
49
50
  polynomial = Polynomial.parse('5x^3 - 5x^2 + 2x - 2')
50
51
  end
51
52
 
52
53
  def test_extrema_with_slope_of_derivative_equal_to_zero
53
54
  polynomial = Polynomial.parse('1x^4')
54
- assert_equal({ min: Set[0.0] }, polynomial.local_extrema)
55
- assert_equal({ max: Set[1.0/0, -1.0/0], min: Set[0.0]}, polynomial.extrema)
55
+ assert_set_eql(Set[ Extremum.new(0.0,polynomial.calculate(0.0), :minimum) ], polynomial.local_extrema)
56
+ assert_set_eql(Set[*[Infinity,-Infinity].map { |x| Extremum.new(x,nil, :maximum)}, Extremum.new(0.0,polynomial.calculate(0.0), :minimum) ], polynomial.extrema)
56
57
  end
57
58
 
58
59
  def test_no_local_extrema
59
60
  polynomial = Polynomial.parse('6x^6 - 5x + 50')
60
- assert_equal({}, polynomial.local_extrema)
61
- assert_equal({max: Set[ -1.0/0, 1.0/0 ]}, polynomial.extrema)
61
+ assert_equal(Set[], polynomial.local_extrema)
62
+ assert_set_eql(Set[*[ -1.0/0, 1.0/0 ].map { |x| Extremum.new(x, nil, :maximum)}], polynomial.extrema)
62
63
  end
63
64
 
64
- def test_no_inflection_points
65
+ def test_curvature_behaviour_no_inflection_points
65
66
  polynomial = Polynomial.parse('1 x^4')
66
67
  assert_equal({ left: [-1.0/0..+1.0/0] }, polynomial.curvature_behaviour)
67
68
 
@@ -74,7 +75,7 @@ class TestPolynomial < MiniTest::Unit::TestCase
74
75
 
75
76
  def test_no_extremums
76
77
  polynomial = Polynomial.parse('5')
77
- assert_equal({}, polynomial.extrema)
78
+ assert_equal(Set[], polynomial.extrema)
78
79
  end
79
80
 
80
81
  def test_no_curvature
@@ -91,12 +92,12 @@ class TestPolynomial < MiniTest::Unit::TestCase
91
92
  assert_equal({}, polynomial.curvature_behaviour)
92
93
  end
93
94
 
94
- def test_two_inflection_points
95
+ def test_curvature_behaviour_two_inflection_points
95
96
  polynomial = Polynomial.parse('+ 1.0 x^4 + 5.0 x^3 - 1.0 x^2 + 3.0 x + 5.0')
96
97
  assert_equal({left:[-1.0/0..-2.5649778198, 0.0649778198..1.0/0], right: [-2.5649778198..0.0649778198] } , polynomial.curvature_behaviour)
97
98
  end
98
99
 
99
- def test_three_inflection_points
100
+ def test_curvature_behaviour_three_inflection_points
100
101
  polynomial = Polynomial.new(20,4,0,-1,-200)
101
102
  assert_equal( {:right=>[(-1/10)..0.0], :left=>[-Infinity..(-1/10), 0.0..Infinity]}, polynomial.curvature_behaviour)
102
103
  end
@@ -104,7 +105,7 @@ class TestPolynomial < MiniTest::Unit::TestCase
104
105
 
105
106
  def test_efficient_roots_calculation
106
107
  polynomial = Polynomial.parse('200x^2342435 + 6x^20')
107
- assert_equal(Set[0.0], polynomial.roots)
108
+ assert_set_eql(Set[Root.new(0.0)], polynomial.roots)
108
109
  end
109
110
 
110
111
  def test_lt
@@ -121,4 +122,9 @@ class TestPolynomial < MiniTest::Unit::TestCase
121
122
  polynomial = Polynomial.new(5,0,2,-1)
122
123
  assert_equal '5x^3 + 2x - 1', polynomial.to_s
123
124
  end
125
+
126
+ def test_inflection_points
127
+ polynomial = Polynomial.new(20,4,0,-1,-200)
128
+ assert_set_eql Set[InflectionPoint.new(-1/10,polynomial.(-1/10)),InflectionPoint.new(0,polynomial.(0))], polynomial.inflection_points
129
+ end
124
130
  end
data/test/term_test.rb CHANGED
@@ -1,4 +1,5 @@
1
- require_relative 'test_helper'
1
+ require "#{File.dirname(File.expand_path(__FILE__))}/test_helper"
2
+
2
3
  class TestTerm < MiniTest::Unit::TestCase
3
4
  def test_initializer
4
5
  assert_equal 0, Term.new.coefficient
@@ -25,6 +26,12 @@ class TestTerm < MiniTest::Unit::TestCase
25
26
  assert_equal 0, Term.parse('6').exponent
26
27
  end
27
28
 
29
+ def test_dup
30
+ term = Term.new(2,10)
31
+ assert_equal term.coefficient, term.dup.coefficient
32
+ assert_equal term.exponent, term.dup.exponent
33
+ end
34
+
28
35
  def test_invalid_string_raises_not_parsable_error
29
36
  ['6x^ -5', '6^20', '2 2', 'xx', '2 ^ x'].each do |not_parsable_string|
30
37
  assert_raises NotParsableError do
data/test/test_helper.rb CHANGED
@@ -1,3 +1,21 @@
1
1
  require 'awesome_print'
2
+ require 'simplecov'
3
+ SimpleCov.start
2
4
  require 'minitest/autorun'
3
- require_relative '../lib/polynomials.rb'
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'polynomials'
8
+
9
+ class MiniTest::Unit::TestCase
10
+ include Polynomials
11
+ def assert_set_eql(actual,computed)
12
+ test = lambda do
13
+ actual.all? do |a|
14
+ computed.any? do |c|
15
+ a.eql?(c) && c.eql?(a)
16
+ end
17
+ end
18
+ end
19
+ assert test.call, "Expected #{actual.inspect}, not #{computed.inspect}"
20
+ end
21
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
7
+ - 3
8
8
  - 0
9
- version: 0.2.0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Manuel Korfmann
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-05-14 00:00:00 +02:00
17
+ date: 2011-05-15 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -48,7 +48,7 @@ dependencies:
48
48
  prerelease: false
49
49
  version_requirements: *id002
50
50
  - !ruby/object:Gem::Dependency
51
- name: rcov
51
+ name: simplecov
52
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
@@ -73,6 +73,32 @@ dependencies:
73
73
  type: :development
74
74
  prerelease: false
75
75
  version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: activesupport
78
+ requirement: &id005 !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: *id005
89
+ - !ruby/object:Gem::Dependency
90
+ name: i18n
91
+ requirement: &id006 !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *id006
76
102
  description: "\n\
77
103
  This is a gem dedicated to parsing, manipulating and finding roots,extrema and inflection points of polynomials.\n\
78
104
  It can solve polynomial equations with a degree up to 4 by implementing formulas for solving quadratic, cubic and quartic functions.\n "
@@ -83,21 +109,25 @@ extensions: []
83
109
 
84
110
  extra_rdoc_files:
85
111
  - LICENSE.txt
86
- - README.rdoc
112
+ - README.markdown
87
113
  files:
88
114
  - .document
89
115
  - Gemfile
90
116
  - Gemfile.lock
91
117
  - LICENSE.txt
92
- - README.rdoc
118
+ - README.markdown
93
119
  - Rakefile
94
120
  - VERSION
95
- - lib/core_ext/math.rb
121
+ - examples/plot_only_mutual_data.rb
96
122
  - lib/polynomials.rb
97
- - lib/term.rb
123
+ - lib/polynomials/exceptions.rb
124
+ - lib/polynomials/formulas.rb
125
+ - lib/polynomials/point.rb
126
+ - lib/polynomials/polynomial.rb
127
+ - lib/polynomials/term.rb
98
128
  - polynomials.gemspec
99
- - test/helper.rb
100
- - test/math_test.rb
129
+ - test/formulas_test.rb
130
+ - test/point_test.rb
101
131
  - test/polynomial_test.rb
102
132
  - test/term_test.rb
103
133
  - test/test_helper.rb
@@ -115,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
145
  requirements:
116
146
  - - ">="
117
147
  - !ruby/object:Gem::Version
118
- hash: -2290630464224354984
148
+ hash: -1053346155644578569
119
149
  segments:
120
150
  - 0
121
151
  version: "0"
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = polynomials
2
-
3
- Description goes here.
4
-
5
- == Contributing to polynomials
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
- * Fork the project
10
- * Start a feature/bugfix branch
11
- * Commit and push until you are happy with your contribution
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2011 Manuel Korfmann. See LICENSE.txt for
18
- further details.
19
-
data/lib/core_ext/math.rb DELETED
@@ -1,60 +0,0 @@
1
- require 'mathn'
2
- module Math
3
- def self.roots_of_cubic_function(a,b,c,d,with_complex = false)
4
- f = ((3*c/a) - (b**2/a**2))/3
5
- g = (((2*b**3)/a**3) - (9*b*c/a**2) + (27*d/a))/27
6
- h = (g**2/4) + (f**3/27)
7
- if f == 0 && g == 0 && h == 0
8
- f = (3*c/a) - (b**2/a**2)
9
- g = (2*b**3/a**3) - (9*b*c/a**2) + (27*d/a)
10
- h = (g**2/4) + (f**3/27)
11
- [-Math.cbrt(d/a)]
12
- elsif h <= 0
13
- i = Math.sqrt((g**2/4) - h)
14
- j = Math.cbrt(i)
15
- k = Math.acos(-(g / (2*i)))
16
- l = j * -1
17
- m = Math.cos(k/3)
18
- n = Math.sqrt(3) * Math.sin(k/3)
19
- p = (b/(3*a))*-1
20
- [2*j*Math.cos(k/3) - (b/(3*a)), l*(m+n)+p, l*(m-n)+p]
21
- else
22
- f = (((3*c)/a) - (b**2/a**2))/3
23
- g = ((2*b**3/a**3) - (9*b*c/a**2) + ((27*d)/a))/27
24
- h = (g**2/4) + (f**3/27)
25
- r = -(g/2) + Math.sqrt(h)
26
- s = Math.cbrt(r)
27
- t = -(g/2) - Math.sqrt(h)
28
- u = Math.cbrt(t)
29
- roots = [(s + u) - (b/(3*a))]
30
- roots |= [1,-1].map do |algebraic_sign|
31
- Complex((-(s + u)/2) - (b/(3*a)),algebraic_sign * (s-u)*Math.sqrt(3)/2)
32
- end if with_complex
33
- roots
34
- end.map { |n| n.respond_to?(:round) ? n.round(10) : n }.to_set
35
- end
36
-
37
- def self.roots_of_quadratic_function(a,b,c)
38
- p = b/a
39
- q = c/a
40
- denom = (p/2.0)**2 - q
41
- return Set[] if denom < 0
42
- root = Math.sqrt(denom)
43
- Set.new([:+,:-].map{ |operator| (-(p/2.0)).send(operator, root)}.map { |n| n.round(10) })
44
- end
45
-
46
- def self.roots_of_quartic_function(*args)
47
- a,b,c,d,e = args.map { |n| n/args.first }
48
- f = c - (3*b**2/8)
49
- g = d + (b**3/8) - (b*c/2)
50
- h = e - (3*b**4/256) + (b**2 * c/16) - ( b*d/4)
51
- roots = self.roots_of_cubic_function(1,(f/2),(((f**2)-(4*h))/16),-(g**2)/64,true)
52
- combs = roots.reject{ |n| n == 0}.combination(2)
53
- only_complex = combs.select { |comb| comb.all? { |n| n.is_a? Complex }}
54
- p,q = (only_complex.empty? ? combs.first : only_complex.first).map { |n| Math.sqrt(n) }
55
- r = (-g/(8*p*q))
56
- s = b/(4*a)
57
- roots = Set.new([p + q + r -s, p - q - r -s, -p + q - r -s, -p - q + r -s].map { |n| n.is_a?(Complex) ? (n.real if n.imaginary == 0) : n }.compact )
58
- roots.map { |n| n.respond_to?(:round) ? n.round(10) : n }.to_set
59
- end
60
- end
data/lib/term.rb DELETED
@@ -1,47 +0,0 @@
1
- class NotParsableError < ArgumentError;end
2
- class Term
3
- attr_accessor :coefficient, :exponent
4
-
5
- def initialize(exponent = 0, coefficient = 0)
6
- @coefficient, @exponent = coefficient, exponent
7
- end
8
-
9
- def self.parse(string)
10
- term = self.new
11
- float = /\d+(?:\.\d+)?/
12
- integer = /\d+/
13
- polynomial_regex = /
14
- \A\s*
15
- (?<algebraic_sign>[-+]?)
16
- \s*
17
- (?<coefficient>#{float})?
18
- \s*
19
- (?<power>x(?:\^(?<exponent>#{integer}))?)?
20
- \s*\Z
21
- /x
22
-
23
- raw_data = string.match(polynomial_regex)
24
- raise NotParsableError unless raw_data
25
- term.coefficient = (raw_data[:algebraic_sign] + ( raw_data[:coefficient] || "1")).to_f
26
- term.exponent = raw_data[:power] ? (raw_data[:exponent] || 1).to_i : 0
27
- term
28
- end
29
-
30
- def to_s
31
- pretty_coeffiecent = coefficient.denominator == 1 ? coefficient.abs.to_i : coefficient.abs
32
- algebraic_sign = coefficient < 0 ? '-' : '+'
33
- power = "x#{"^#{exponent}" unless exponent == 1}"
34
- "#{algebraic_sign} #{pretty_coeffiecent}#{ power unless exponent.zero?} "
35
- end
36
-
37
- def dup
38
- duplicate = self.class.new
39
- duplicate.coefficient = self.coefficient
40
- duplicate.exponent = self.exponent
41
- duplicate
42
- end
43
-
44
- def ==(other)
45
- self.coefficient == other.coefficient && self.exponent == other.exponent
46
- end
47
- end
data/test/helper.rb DELETED
@@ -1,18 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'test/unit'
11
- require 'shoulda'
12
-
13
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
- $LOAD_PATH.unshift(File.dirname(__FILE__))
15
- require 'polynomials'
16
-
17
- class Test::Unit::TestCase
18
- end