polynomial 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,176 @@
1
+ == 0.7.0 2010-11-18
2
+
3
+ * 1 important bugfix:
4
+ * Basic operations no longer alter the operands
5
+
6
+ * Lots fo small improvements:
7
+ * new method #dup
8
+ * requires no longer alter $:
9
+ * README.txt finally written
10
+ * fixed typo in HandyHash LoadError message
11
+ * removed redundant #zero?
12
+ * improved test handling of differences between Ruby 1.8 and 1.9
13
+ * test coverage reached 100% (except for Multivariate which is currently at 93.71%)
14
+ * All tests run flawlessly (100% pass) with the following Ruby versions:
15
+ * MRI 1.8.7 (2008-06-20 patchlevel 22) [x86_64-linux]
16
+ * MRI 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux]
17
+ * MRI ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]
18
+ * jruby 1.5.3 (ruby 1.8.7 patchlevel 249) (2010-09-28 7ca06d7) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_17) [amd64-java]
19
+ * rubinius 1.1.0 (1.8.7 release 2010-09-23 JI) [x86_64-unknown-linux-gnu]
20
+ * But they fail with the following Ruby version:
21
+ * ruby 1.8.6 (2010-02-05 patchlevel 399) [x86_64-linux]
22
+
23
+ == 0.6.0 2009-07-10
24
+
25
+ * 1 new feature:
26
+ * Polynomial::Multivariate
27
+
28
+ == 0.5.3 2009-07-10
29
+
30
+ * 1 new feature:
31
+ * Polynomial::Unity and Polynomial::Zero
32
+
33
+ * additional tests for substitute with BigDecimal coefficients and x
34
+
35
+ == 0.5.2 2009-06-22
36
+
37
+ * 1 bugfix: Polynomial[0]#to_s evaluates to "0" instead of ""
38
+
39
+ == 0.5.1 2009-06-22
40
+
41
+ * 1 minor enhancement:
42
+ * comments rearranging for correct RDoc output
43
+ * fixing some comments content
44
+ * new comments added
45
+
46
+ == 0.5.0 2009-06-22
47
+
48
+ * 1 bugfix:
49
+ * division by degree 1 polynomial now works as expected
50
+
51
+ * 5 new features:
52
+ * Polynomial#%(d) gives the remainder of the division by d
53
+ * Polynomial#**(n) raises to non-negative integer power
54
+ * Polynomial#quodiv and #quo
55
+ * like #divmod and #div but using #quo instead of #div, therefore if
56
+ dividend and divisor are integers or rationals the quotient and rest
57
+ will also be rational
58
+ * Polynomial#new accepts degree + block, e.g. Polynomial.new(2) {|n| n+1 }
59
+ * new module: Polynomials::Legendre
60
+
61
+ * 4 minor enhancements:
62
+ * class variables changed to instance module variables
63
+ * Ultraspherical particular alphas values of 0, 1 and 0.5 delegates
64
+ respectively to Chebyshev first kind, second kind and Legendre.
65
+ * coefs attr_reader access changed from protected to public
66
+ * more tests: according to rcov, test coverage is 98.1% for polynomial class
67
+ and 100% for Chebyshev, Legendre and Ultraspherical
68
+
69
+ * removed feature:
70
+ * substitute(*xs) removed, and substitute_single(x) renamed to substitute(x)
71
+
72
+ == 0.4.3 2009-06-17
73
+
74
+ * 1 minor enhancement:
75
+ * Ultraspherical alpha values no longer restricted to some predefined range
76
+ - RangeError exception is only raised if degree of generated polynomial is not the required
77
+
78
+ == 0.4.2 2009-06-16
79
+
80
+ * 1 minor enhancement:
81
+ * Polynomial.from_string now accepts numbers in scientific notation (e.g. '1e-2*x')
82
+
83
+ == 0.4.1 2009-06-15
84
+
85
+ * bugfixes
86
+ * #to_s no longer spaces "1e-3" to "1e - 3"
87
+
88
+ * minor enhancements:
89
+ * ultrasph. alpha range extended down to -0.99999999999999 (Polynomial::Ultraspherical::MinAlpha)
90
+ * better tests for Chebyshev polynomials (concise, randomized)
91
+
92
+ == 0.4.0 2009-06-15
93
+
94
+ * 2 bugfixes:
95
+ * Polynomial#coerce now works as expected, returning two Polynomial objects
96
+ * Polynomial.from_string parsing is now faster and stricter
97
+
98
+ * 5 new methods:
99
+ * to_num, to_f, to_i
100
+ * divmod
101
+ * /
102
+
103
+ * 3 new features:
104
+ * Complex coefficients should work, though most tests are focused on Integer and Float
105
+ * if HandyHash gem is installed, abbreviations are enabled
106
+ * 2 new Polynomial#to_s parameters
107
+ - :spaced to allow spaces between terms (default to true)
108
+ - :decreasing, to allow greatest to smallest degree monomials
109
+ * to_s
110
+
111
+ * 1 change:
112
+ * default Polynomial#to_s parameters changed to match those of Polynomial.from_string
113
+
114
+ * 1 new module:
115
+ * Polynomial::Ultraspherical - generates ultraspherical (Gegenbauer) polynomials
116
+
117
+ == 0.3.0 2009-06-12
118
+
119
+ * 2 minor enhancements:
120
+ * Chebyshev module is now defined inside Polynomial class, thus
121
+ it is now necessary to prepend Polynomial:: to method calls.
122
+
123
+ * uncached interactive versions available:
124
+ - Polynomial::Chebyshev.uncached_first_kind and ...second_kind
125
+ - Default is cached.
126
+
127
+ Uncached versions have O(1) memory consumption versus O(n^2) of the cached
128
+ version, however its amortized (average of many calss) time consumption
129
+ is higher.
130
+
131
+ == 0.2.1 2009-06-09
132
+
133
+ * 1 minor enhancement:
134
+ * interactive versions of Chebyshev.first_kind and Chebyshev.second_kind (2x faster)
135
+
136
+ == 0.2.0 2009-06-09
137
+
138
+ * 2 new features:
139
+ * new initializer: Polynomial.new(pow_coef_hash)
140
+ * new constructor: Polynomial.from_string
141
+
142
+ == 0.1.3 2009-06-09
143
+
144
+ * 1 compatibility change:
145
+ * abandoned colons in favor of linebreaks in case statements, so that the gem is now Ruby 1.9 compatible
146
+
147
+ == 0.1.2 2009-02-14
148
+
149
+ * 1 minor enhancement:
150
+ * to_s has a parameter to change the multiplication symbol from the default '*', e.g. to ''
151
+
152
+ == 0.1.1 2009-02-10
153
+
154
+ * 1 bugfix:
155
+ * to_s no longer doubles plus signal when omitting a zero coefficient
156
+
157
+ * 1 minor enhancement:
158
+ * to_s has a verbose flag to disable omitting powers with zero coefficient
159
+ * to_s has a parameter to change the power symbol from the default '**', e.g. to '^'
160
+
161
+ == 0.1.0 2009-01-06
162
+
163
+ * 1 minor enhancement:
164
+ * Substitute now accepts an array of x-values
165
+ * New method #zero? (null polynomial)
166
+ * New method #derivatives
167
+
168
+ == 0.0.2 2008-06-17
169
+
170
+ * 1 minor enhancement:
171
+ * Simplifications due to Chebyshev not being a class anymore (it is now a module).
172
+
173
+ == 0.0.1 2008-05-06
174
+
175
+ * 1 major enhancement:
176
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 FIXME full name
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/Manifest.txt ADDED
@@ -0,0 +1,34 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/polynomial.rb
9
+ lib/polynomial/chebyshev.rb
10
+ lib/polynomial/legendre.rb
11
+ lib/polynomial/multivariate.rb
12
+ lib/polynomial/ultraspherical.rb
13
+ lib/polynomial/version.rb
14
+ script/console
15
+ script/destroy
16
+ script/generate
17
+ script/txt2html
18
+ setup.rb
19
+ tasks/deployment.rake
20
+ tasks/environment.rake
21
+ tasks/website.rake
22
+ test/bench_cheby_pol_vs_trig.rb
23
+ test/test_chebyshev.rb
24
+ test/test_helper.rb
25
+ test/test_legendre.rb
26
+ test/test_multivariate.rb
27
+ test/test_polynomial.rb
28
+ test/test_suite.rb*
29
+ test/test_ultraspherical.rb
30
+ website/index.html
31
+ website/index.txt
32
+ website/javascripts/rounded_corners_lite.inc.js
33
+ website/stylesheets/screen.css
34
+ website/template.html.erb
data/README.txt ADDED
@@ -0,0 +1,72 @@
1
+ = polynomial
2
+
3
+ Website:
4
+ * http://adrianomitre.github.com/Polynomial
5
+
6
+ Repository:
7
+ * http://github.com/adrianomitre/Polynomial
8
+
9
+ == DESCRIPTION:
10
+
11
+ Rich-featured single and multiple variables polynomials classes for Ruby.
12
+
13
+ == FEATURES:
14
+
15
+ * Implements all basic operations (+, -, *, /, quo), plus integral and derivatives.
16
+ * Rich, configurable and robust conversion from and to strings.
17
+ * Very flexible constructor interface.
18
+ * Integration with EasyPlot for easy plotting.
19
+ * Coefficients can be complex numbers.
20
+ * Avoid changing coefficients types as much as possible, i.e., does not convert integers or rationals to float unless needed
21
+ * Constructors for Legendre, Ultraspherical, and first and second kind Chebyshev polynomials.
22
+
23
+ == SYNOPSIS:
24
+
25
+ foo = Polynomial.new(1, 2) #=> #<Polynomial:0x7f25286cea68 @coefs=[1, 2]>
26
+ foo.to_s #=> "1 + 2*x"
27
+ foo.plot # open gnuplot window with 'foo' plotted (require EasyPlot gem)
28
+
29
+ bar = Polynomial.new(3) {|n| n+1 } #=> #<Polynomial:0x7f25287461a8 @coefs=[1, 2, 3, 4]>
30
+ bar.to_s(:power_symbol=>'^') #=> "1 + 2*x + 3*x^2 + 4*x^3"
31
+
32
+ require 'rational'
33
+ baz = bar.quo(foo) #=> #<Polynomial:0x7f00a1ff0570 @coefs=[Rational(3, 4), Rational(1, 2), Rational(2, 1)]>
34
+ baz.to_s #=> "(3/4) + (1/2)*x"
35
+
36
+ == REQUIREMENTS:
37
+
38
+ MRI, JRuby or Rubinius as long as RUBY_VERSION >= 1.8.7.
39
+
40
+ The following gems are soft requirements, i.e., Polynomial can be used without
41
+ them, though with slightly reduced functionality:
42
+ * HandyHash
43
+ * EasyPlot
44
+
45
+ == INSTALL:
46
+
47
+ * sudo gem install polynomial
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2008-2010 Adriano Mitre
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,95 @@
1
+ my_path = File.dirname(File.expand_path(__FILE__))
2
+ require File.join(my_path, '../polynomial')
3
+
4
+ class Polynomial
5
+
6
+ # Generate Chebyshev polynomials of first and second kinds.
7
+ # For the mathematics, see {Wikipedia entry}[http://en.wikipedia.org/wiki/Chebyshev_polynomials].
8
+ #
9
+ module Chebyshev
10
+
11
+ @fk = [Polynomial.new(1), Polynomial.new(0,1)]
12
+ @sk = [Polynomial.new(1), Polynomial.new(0,2)]
13
+ @fact = Polynomial.new(0,2)
14
+
15
+ # Generate the n-th Chebyshev polynomial of the first kind using
16
+ # a cached interactive implementation of the recurrence relation.
17
+ # Caching reduces amortized (average after many calls) time consumption
18
+ # to O(1) in the hypothetical case of infinite calls with uniform
19
+ # distribution in a bounded range.
20
+ #
21
+ # Warning: due to caching, memory usage is proportional to the square of
22
+ # the greatest degree computed. Use the uncached_first_kind method
23
+ # if no caching is wanted.
24
+ #
25
+ def self.first_kind(n)
26
+ n >= 0 or raise RangeError, 'degree should be non-negative'
27
+
28
+ (@fk.size).upto(n) do |m|
29
+ @fk[m] = (@fact * @fk[m-1] - @fk[m-2])
30
+ end
31
+ @fk[n]
32
+ end
33
+
34
+ # Same as second_kind, but uncached. It saves memory but the amortized time
35
+ # consumption is higher, namely O(n^2).
36
+ #
37
+ def self.uncached_first_kind(n)
38
+ n >= 0 or raise RangeError, 'degree should be non-negative'
39
+
40
+ case n
41
+ when 0 then Polynomial.new(1)
42
+ when 1 then Polynomial.new(0,1)
43
+ else
44
+ prev2 = self.first_kind(0)
45
+ prev1 = self.first_kind(1)
46
+ curr = nil # scope
47
+ (n-1).times do
48
+ curr = (@fact * prev1 - prev2)
49
+ prev1, prev2 = curr, prev1
50
+ end
51
+ curr
52
+ end
53
+ end
54
+
55
+ # Generate the n-th Chebyshev polynomial of the second kind using
56
+ # an cached-interactive implementation of the recurrence relation.
57
+ # Caching reduces amortized (average after many calls) time consumption.
58
+ #
59
+ # Warning: due to caching, memory usage is proportional to the square of
60
+ # the greatest degree computed. Use the uncached_second_kind method
61
+ # if no caching is wanted.
62
+ #
63
+ def self.second_kind(n)
64
+ n >= 0 or raise RangeError, 'degree should be non-negative'
65
+
66
+ (@sk.size).upto(n) do |m|
67
+ @sk[m] = (@fact * @sk[m-1] - @sk[m-2])
68
+ end
69
+ @sk[n]
70
+ end
71
+
72
+ # Same as second_kind, but uncached (saves memory but the amortized time
73
+ # consumption is higher).
74
+ #
75
+ def self.uncached_second_kind(n)
76
+ n >= 0 or raise RangeError, 'degree should be non-negative'
77
+
78
+ case n
79
+ when 0 then Polynomial.new(1)
80
+ when 1 then Polynomial.new(0,2)
81
+ else
82
+ prev2 = self.second_kind(0)
83
+ prev1 = self.second_kind(1)
84
+ curr = nil # scope
85
+ (n-1).times do
86
+ curr = (@fact * prev1 - prev2)
87
+ prev1, prev2 = curr, prev1
88
+ end
89
+ curr
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,57 @@
1
+ my_path = File.dirname(File.expand_path(__FILE__))
2
+ require File.join(my_path, '../polynomial')
3
+
4
+ class Polynomial
5
+
6
+ # Generate Legendre polynomials.
7
+ # For the mathematics, see {Wikipedia entry}[http://en.wikipedia.org/wiki/Legendre_polynomials].
8
+ #
9
+ module Legendre
10
+
11
+ @cache = [Polynomial.new(1), Polynomial.new(0,1)]
12
+
13
+ # Generate the n-th Legendre polynomial using a cached interactive
14
+ # implementation of the recurrence relation.
15
+ # Caching reduces amortized (average after many calls) time consumption
16
+ # to O(1) in the hypothetical case of infinite calls with uniform
17
+ # distribution in a bounded range.
18
+ #
19
+ # Warning: due to caching, memory usage is proportional to the square of
20
+ # the greatest degree computed. Use the uncached_first_kind method
21
+ # if no caching is wanted.
22
+ #
23
+ def self.cached_legendre(n)
24
+ n >= 0 or raise RangeError, 'degree should be non-negative'
25
+
26
+ for m in @cache.size-1 ... n
27
+ @cache[m+1] = (Polynomial.new(0,2*m+1)*@cache[m] - m*@cache[m-1]).quo(m+1)
28
+ end
29
+ @cache[n]
30
+ end
31
+ class <<self; alias legendre cached_legendre; end
32
+
33
+ # Generate the n-th Legendre polynomial, but uncached.
34
+ # It saves memory but the amortized time consumption is higher,
35
+ # namely O(n^2).
36
+ #
37
+ def self.uncached_legendre(n)
38
+ n >= 0 or raise RangeError, 'degree should be non-negative'
39
+
40
+ case n
41
+ when 0 then @cache[0]
42
+ when 1 then @cache[1]
43
+ else
44
+ prev2 = @cache[0]
45
+ prev1 = @cache[1]
46
+ curr = nil # scope
47
+ for m in 1 ... n
48
+ curr = (Polynomial.new(0,2*m+1)*prev1 - m*prev2).quo(m+1)
49
+ prev1, prev2 = curr, prev1
50
+ end
51
+ curr
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,143 @@
1
+ my_path = File.dirname(File.expand_path(__FILE__))
2
+ require File.join(my_path, '../polynomial')
3
+
4
+ # TODO: consider building an writer for @terms which sorts the value
5
+ # prior to actually setting it.
6
+
7
+ class Multivariate < Polynomial
8
+
9
+ attr_reader :vars, :terms
10
+
11
+ class << self
12
+ alias [] new
13
+ end
14
+
15
+ # Creates a new multivariate polynomial from supplied string representation,
16
+ # basically delegating to Multivariate.from_string.
17
+ #
18
+ # Examples:
19
+ # Polynomial::Multivariate[[1,1,0],[2,0,1]].to_s #=> "x + 2*y"
20
+ #--
21
+ # Polynomial::Multivariate['x+y'].to_s #=> "x + y"
22
+ # Polynomial::Multivariate['xy+y^3', :power_symbol=>'^'].to_s #=> "x*y + y**3"
23
+ #++
24
+ #
25
+ def initialize(*terms)
26
+ !terms.empty? or raise ArgumentError, 'at least one coefficient should be supplied'
27
+ arg = terms[0]
28
+ case arg
29
+ when Array
30
+ terms.flatten.all? {|a| a.is_a? Numeric } or raise TypeError, 'coefficient-power should be Numeric'
31
+ sz = arg.size
32
+ sz > 2 or raise ArgumentError, 'coefficient-power tuplets should have length > 2'
33
+ terms.all? {|ary| ary.size == sz } or raise ArgumentError, 'inconsistent data'
34
+ when String
35
+ raise NotImplementedError
36
+ # coefs = self.class.coefs_from_string(coefs[0], coefs[1] || {})
37
+ else
38
+ raise TypeError, 'coefficient-power should be Numeric'
39
+ end
40
+ @vars = sz - 1
41
+ @terms = self.class.reduce_terms(terms).sort
42
+ end
43
+
44
+ # Evaluates Multivariate polynomial
45
+ #--
46
+ # FIXME: replace current straightforward but slow implementation
47
+ # by some efficient Horner's rule inspired algorithm.
48
+ #++
49
+ def substitute(*xs)
50
+ xs.size == @vars or raise ArgumentError, "wrong number of arguments (#{xs.size} for #{@vars})"
51
+ total = 0
52
+ @terms.each do |coef, *powers|
53
+ result = coef
54
+ for i in 0 ... @vars
55
+ result *= xs[i] ** powers[i]
56
+ end
57
+ total += result
58
+ end
59
+ total
60
+ end
61
+
62
+ def *(other)
63
+ @vars == other.vars or raise ArgumentError, "number of variables must be the same for both multiplicands"
64
+ new_terms = []
65
+ @terms.each do |my_term|
66
+ other.terms.each do |other_term|
67
+ new_coef = my_term[0] * other_term[0]
68
+ new_powers = my_term[1..-1].zip(other_term[1..-1]).map {|a,b| a + b }
69
+ new_term = [new_coef] + new_powers
70
+ new_terms << new_term
71
+ end
72
+ end
73
+ self.class.new(*new_terms.sort)
74
+ end
75
+
76
+ # FIXME: implement efficiently, i.e., O(m) where m is the number of terms
77
+ def **(n)
78
+ n >= 0 or raise RangeError, "negative argument"
79
+ n.is_a?(Integer) or raise TypeError, "non-integer argument"
80
+ ([self]*n).inject(Unity) {|s,k| s*k }
81
+ end
82
+
83
+ def ==(other)
84
+ self.instance_variables.all? do |var_name|
85
+ self.instance_variable_get(var_name) == other.instance_variable_get(var_name)
86
+ end
87
+ end
88
+
89
+ def degree(index)
90
+ if index == 0
91
+ 0
92
+ elsif index > 0 && index <= @vars
93
+ @terms.map {|cp| cp[index] }.max
94
+ else
95
+ raise RangeError, 'invalid variable index'
96
+ end
97
+ end
98
+
99
+ def coerce(other)
100
+ case other
101
+ when Numeric
102
+ [self.class.new([other]), self]
103
+ when Polynomial
104
+ [other, self]
105
+ else
106
+ raise TypeError, "#{other.class} can't be coerced into Polynomial"
107
+ end
108
+ end
109
+
110
+ # private
111
+ =begin
112
+ def self.remove_trailing_zeroes(ary)
113
+ m = 0
114
+ ary.reverse.each.with_index do |a,n|
115
+ unless a.all? {|z| z.zero? }
116
+ m = n+1
117
+ break
118
+ end
119
+ end
120
+ ary[0..-m]
121
+ end
122
+ =end
123
+
124
+ # Group same powered terms.
125
+ #
126
+ # The result is always non-empty, the base case being [[0,..,0]]
127
+ #
128
+ def self.reduce_terms(terms)
129
+ new_terms = []
130
+ terms.group_by {|a,*powers| powers }.each_pair do |powers, terms|
131
+ new_coef = 0
132
+ terms.each {|coef, *rest| new_coef += coef }
133
+ new_terms << [new_coef, *powers] unless new_coef.zero?
134
+ end
135
+ new_terms.empty? ? [terms[0].map { 0 }] : new_terms
136
+ end
137
+
138
+ public
139
+
140
+ Unity = new([1,0,0])
141
+ Zero = new([0,0,0])
142
+
143
+ end
@@ -0,0 +1,47 @@
1
+ my_path = File.dirname(File.expand_path(__FILE__))
2
+ require File.join(my_path, 'chebyshev')
3
+ require File.join(my_path, 'legendre')
4
+
5
+ class Polynomial
6
+
7
+ # Generate ultraspherical (or Gegenbauer) polynomials.
8
+ # For the mathematics, see {Wikipedia}[http://en.wikipedia.org/wiki/Gegenbauer_polynomials]
9
+ # or {Mathworld entry}[http://mathworld.wolfram.com/GegenbauerPolynomial.html].
10
+ #
11
+ module Ultraspherical
12
+
13
+ # Generates the ultraspherical (or Gegenbauer) polynomial of given degree
14
+ # and parameter using an interactive implementation of the recurrence
15
+ # relation which takes O(r^2) time.
16
+ #
17
+ def self.ultraspherical(degree, alpha)
18
+ degree >= 0 or raise RangeError, 'degree should be non-negative'
19
+ case alpha
20
+ when 0 then Polynomial::Chebyshev.first_kind(degree)
21
+ when 0.5 then Polynomial::Legendre.legendre(degree)
22
+ when 1 then Polynomial::Chebyshev.second_kind(degree)
23
+ else
24
+ case degree
25
+ when 0
26
+ Polynomial.new(1)
27
+ when 1
28
+ Polynomial.new(0, 2*alpha)
29
+ else
30
+ prev2, prev1 = self.ultraspherical(0, alpha), self.ultraspherical(1, alpha)
31
+ curr = nil # scope reasons
32
+ 2.upto(degree) do |m|
33
+ a = Polynomial.new(0, 2*(m+alpha-1)) * prev1
34
+ b = Polynomial.new(m+2*alpha-2) * prev2
35
+ curr = (a - b).quo(m)
36
+ raise RangeError, "alpha #{alpha} is degenerate for specified degree #{degree}" if curr.degree < m
37
+ prev1, prev2 = curr, prev1
38
+ end
39
+ curr
40
+ end
41
+ end
42
+ end
43
+ class <<self; alias us ultraspherical; end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,9 @@
1
+ class Polynomial #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end