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 +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
|