polynomial 0.7.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/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