polynomial 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +176 -0
- data/License.txt +20 -0
- data/Manifest.txt +34 -0
- data/README.txt +72 -0
- data/lib/polynomial/chebyshev.rb +95 -0
- data/lib/polynomial/legendre.rb +57 -0
- data/lib/polynomial/multivariate.rb +143 -0
- data/lib/polynomial/ultraspherical.rb +47 -0
- data/lib/polynomial/version.rb +9 -0
- data/lib/polynomial.rb +532 -0
- data/test/test_chebyshev.rb +103 -0
- data/test/test_helper.rb +6 -0
- data/test/test_legendre.rb +53 -0
- data/test/test_multivariate.rb +469 -0
- data/test/test_polynomial.rb +404 -0
- data/test/test_suite.rb +16 -0
- data/test/test_ultraspherical.rb +45 -0
- data/website/index.txt +57 -0
- metadata +127 -0
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
|