line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Package: Math::Yapp.pm: Yet another Polynomial Package |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
package Math::Yapp; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
use overload |
6
|
6
|
|
|
|
|
51
|
'+' => Yapp_add, |
7
|
|
|
|
|
|
|
'+=' => Yapp_plus, |
8
|
|
|
|
|
|
|
'!' => Yapp_negate, # Negate |
9
|
|
|
|
|
|
|
'-' => Yapp_sub, |
10
|
|
|
|
|
|
|
'-=' => Yapp_minus, |
11
|
|
|
|
|
|
|
'*' => Yapp_times, |
12
|
|
|
|
|
|
|
'*=' => Yapp_mult, |
13
|
|
|
|
|
|
|
'/' => Yapp_dividedby, |
14
|
|
|
|
|
|
|
'/=' => Yapp_divide, |
15
|
|
|
|
|
|
|
'~' => Yapp_conj, # Conjugate complex coefficients |
16
|
|
|
|
|
|
|
'**' => Yapp_power # Raise a polynomial to an integer power |
17
|
|
|
|
|
|
|
# '.' => Yapp_innerProd # Inner product, for vector operations. |
18
|
|
|
|
|
|
|
# # Note: This operation is not supported in this |
19
|
|
|
|
|
|
|
# # module. A separate Math::Yapp::Vector will |
20
|
|
|
|
|
|
|
# # handle vector-space stuff |
21
|
6
|
|
|
6
|
|
203887
|
; |
|
6
|
|
|
|
|
7492
|
|
22
|
|
|
|
|
|
|
|
23
|
6
|
|
|
6
|
|
1223
|
use 5.014002; |
|
6
|
|
|
|
|
27
|
|
|
6
|
|
|
|
|
222
|
|
24
|
6
|
|
|
6
|
|
32
|
use strict; |
|
6
|
|
|
|
|
16
|
|
|
6
|
|
|
|
|
305
|
|
25
|
6
|
|
|
6
|
|
33
|
use warnings; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
188
|
|
26
|
6
|
|
|
6
|
|
33
|
use Carp; |
|
6
|
|
|
|
|
20
|
|
|
6
|
|
|
|
|
680
|
|
27
|
6
|
|
|
6
|
|
8302
|
use Math::Complex; |
|
6
|
|
|
|
|
151730
|
|
|
6
|
|
|
|
|
2945
|
|
28
|
|
|
|
|
|
|
#use Math::BigFloat; # Needed for accuracy(), though I don't do |
29
|
|
|
|
|
|
|
# # anything with that yet. |
30
|
6
|
|
|
6
|
|
9214
|
use Storable 'dclone'; |
|
6
|
|
|
|
|
38296
|
|
|
6
|
|
|
|
|
728
|
|
31
|
6
|
|
|
6
|
|
7949
|
use Data::Dumper; # For debugging |
|
6
|
|
|
|
|
87295
|
|
|
6
|
|
|
|
|
854
|
|
32
|
6
|
|
|
6
|
|
63
|
use Scalar::Util qw(refaddr); # Gets pointer values. Also for debugging. |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
5123
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
require Exporter; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
our @ISA = ('Exporter'); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
39
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
40
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# This allows declaration use Math::Yapp ':all'; |
43
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK |
44
|
|
|
|
|
|
|
# will save memory. |
45
|
|
|
|
|
|
|
# |
46
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw(Yapp Yapp_decimals Yapp_print0 |
47
|
|
|
|
|
|
|
Yapp_start_high) ] ); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
#our @EXPORT = qw(all); |
52
|
|
|
|
|
|
|
our @EXPORT = qw(Yapp Yapp_interpolate Yapp_by_roots |
53
|
|
|
|
|
|
|
Yapp_testmode |
54
|
|
|
|
|
|
|
Yapp_decimals Yapp_print0 Yapp_start_high Yapp_margin |
55
|
|
|
|
|
|
|
Csprint); # Unexport Csprint after debug |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
our $VERSION = '1.03'; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my $class_name = "Math::Yapp"; # So I don't have to use the literal in tests |
60
|
|
|
|
|
|
|
my $class_cplx = "Math::Complex"; # Same idea - avoid literal use |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Here are the patterns I need to validate and parse the terms of a polynomial: |
63
|
|
|
|
|
|
|
# They will be used by str2yapp() and str2cplx() |
64
|
|
|
|
|
|
|
# |
65
|
|
|
|
|
|
|
my $sign_pat = qr/[+-]/; # Allows me to isolate the optional sign |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Combine integer_pat and decimal_pat for real_pat |
68
|
|
|
|
|
|
|
# |
69
|
|
|
|
|
|
|
my $integer_pat = qr/\d+/; # Integer is one or more digits |
70
|
|
|
|
|
|
|
my $decimal_pat = qr/\.\d+/; # Decimal: A period followed by digits |
71
|
|
|
|
|
|
|
my $decnum_pat = qr/($integer_pat)($decimal_pat)/; # Complete decimal number |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# A real number may be: |
74
|
|
|
|
|
|
|
# - An integer /\d+/ |
75
|
|
|
|
|
|
|
# - A pure decimal number: /\.\d+/ |
76
|
|
|
|
|
|
|
# - An integer followd by a pure decimal i.e. a complete decimal number |
77
|
|
|
|
|
|
|
# I am checking these in reverse of above order, testing for the more |
78
|
|
|
|
|
|
|
# complicated pattern first. |
79
|
|
|
|
|
|
|
# Of course, all the above may be preceeded by an optinal sign |
80
|
|
|
|
|
|
|
# |
81
|
|
|
|
|
|
|
my $real_pat = qr/(($decnum_pat)|($decimal_pat)|($integer_pat))/; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $cplx_pat = qr/\([+-]?$real_pat[+-]($real_pat)i\)/; # Cplx: (+-real+-real-i) |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# Now a coefficient is a real or complex number at the beginning of a term, |
86
|
|
|
|
|
|
|
# optionally preceeded by a sign. If sign is omitted we will assume positive. |
87
|
|
|
|
|
|
|
# On the other hand, it may be an implicit 1 with only a sign. Hence, both |
88
|
|
|
|
|
|
|
# parts are basically optional; if omitted e.g. x^31, then coefficient is +1 |
89
|
|
|
|
|
|
|
# |
90
|
|
|
|
|
|
|
# As to a regex pattern: A coefficient is at the start of the term and |
91
|
|
|
|
|
|
|
# may be: |
92
|
|
|
|
|
|
|
# - A sign alone => an implicit coefficient of 1.0 ($sign_only_pat) |
93
|
|
|
|
|
|
|
# - A complex or real number alone => implicit sign of +1 ($usigned_coef_pat) |
94
|
|
|
|
|
|
|
# = A real or complex number preceded by a sign. ($signed_coef_pat) |
95
|
|
|
|
|
|
|
# The proper pattern tests for the most complicated possibility first |
96
|
|
|
|
|
|
|
# |
97
|
|
|
|
|
|
|
my $signed_real_pat = qr/^(($sign_pat)?($real_pat))$/; |
98
|
|
|
|
|
|
|
my $signed_coef_pat = qr/((^$sign_pat)(($cplx_pat)|($real_pat)))/; |
99
|
|
|
|
|
|
|
my $usigned_coef_pat = qr/^(($cplx_pat)|($real_pat))/; |
100
|
|
|
|
|
|
|
my $sign_only_pat = qr/(^$sign_pat)/; |
101
|
|
|
|
|
|
|
my $coef_pat = qr/($signed_coef_pat)|($usigned_coef_pat)|($sign_only_pat)/; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my $varia_pat = qr/[A-Za-z]+/; # The "variable" part of the term |
104
|
|
|
|
|
|
|
my $power_pat = qr/\^\d+$/; # Power: caret (^) followed by am integer. |
105
|
|
|
|
|
|
|
# Must be at the end of the term |
106
|
|
|
|
|
|
|
# With apologies: This is a patch to fix a bug in Ysprint(): It was printing |
107
|
|
|
|
|
|
|
# the 0-term as X^0 when formatting starting at low-degree terms. |
108
|
|
|
|
|
|
|
# This will help get rid of that. |
109
|
|
|
|
|
|
|
# |
110
|
|
|
|
|
|
|
my $zero_power_pat = qr/($varia_pat)(\^0)$/; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $const_pat = qr/^$coef_pat$/; # Coefficient only; no variable or exponent |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Now that I have defined the patterns for the parts of a term, I can define |
115
|
|
|
|
|
|
|
# the pattern for a complete term. Each part is optional. |
116
|
|
|
|
|
|
|
# |
117
|
|
|
|
|
|
|
my $term_pat = qr/($coef_pat)?($varia_pat)?($power_pat)?/; |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# However, one pattern not allowed is a $power_pat (exponent) without a |
120
|
|
|
|
|
|
|
# variable. Since, at the momemtn, I cannot think of a regex to test for |
121
|
|
|
|
|
|
|
# this condition, I'll have to handle it in the code. (Sigh) |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Format strings for printf-ing the terms of the polynomial. |
124
|
|
|
|
|
|
|
# First: For a real coefficient: Assume the data to printf includes: |
125
|
|
|
|
|
|
|
# - Width of the coefficient, including decimal point and decimal places |
126
|
|
|
|
|
|
|
# - Number of decimal places alone |
127
|
|
|
|
|
|
|
# - The coeficient, a floating point number |
128
|
|
|
|
|
|
|
# - The variable string (like X, Z, GIZORP etc.) This is omitted for the |
129
|
|
|
|
|
|
|
# constant term |
130
|
|
|
|
|
|
|
# - The exponent (Omitted for the constant term of the polynomial) |
131
|
|
|
|
|
|
|
# |
132
|
|
|
|
|
|
|
my $zero_term_fmt = "%+*.*f"; # No X term in here |
133
|
|
|
|
|
|
|
my $one_term_fmt = "%+*.*f%s"; # For 1st degree term, omit the exponent |
134
|
|
|
|
|
|
|
my $term_fmt = "%+*.*f%s^%d"; # For other terms, always use sign |
135
|
|
|
|
|
|
|
my $first_term_fmt = "%*.*f%s^%s"; # For first term, skip + for first if |
136
|
|
|
|
|
|
|
# it is positive anyway |
137
|
|
|
|
|
|
|
# Now for a complex coefficient: Here format gets a bit more, er, complex; |
138
|
|
|
|
|
|
|
# Each term includes the following in the data portion of printf: |
139
|
|
|
|
|
|
|
# - Width of the real part of the coeficient |
140
|
|
|
|
|
|
|
# - Number of decimal places in the real part |
141
|
|
|
|
|
|
|
# - The real part itself |
142
|
|
|
|
|
|
|
# - Width of the imaginary part of the coeficient |
143
|
|
|
|
|
|
|
# - Number of decimal places in the imaginary part |
144
|
|
|
|
|
|
|
# - The imaginary part itself |
145
|
|
|
|
|
|
|
# - The variable string (Omitted in the constant term) |
146
|
|
|
|
|
|
|
# - The exponent (Omitted in the constant term) |
147
|
|
|
|
|
|
|
# |
148
|
|
|
|
|
|
|
my $zero_term_fmt_i = "+(%*.*f%+*.*fi)"; # No variable or exponent |
149
|
|
|
|
|
|
|
my $one_term_fmt_i = "+(%*.*f%+*.*fi)%s"; # +(Re+/-Im_i)X Variable, no |
150
|
|
|
|
|
|
|
# exponent |
151
|
|
|
|
|
|
|
my $term_fmt_i = "+(%*.*f%+*.*fi)%s^%d";# Sign, Variable, exponent |
152
|
|
|
|
|
|
|
my $first_term_fmt_i = "(%*.*f%+*.*fi)%s^%d";# No sign but variable and |
153
|
|
|
|
|
|
|
# exponent |
154
|
|
|
|
|
|
|
# Some other constants I need for this module, before starting the work in |
155
|
|
|
|
|
|
|
# earnest: |
156
|
|
|
|
|
|
|
# |
157
|
|
|
|
|
|
|
our $czero = Math::Complex->make(0,0); # Complex zero |
158
|
|
|
|
|
|
|
our $inner_prod = "(generic)"; # Default inner priduct type; set with |
159
|
|
|
|
|
|
|
# inner_prod() |
160
|
|
|
|
|
|
|
# These globals control behavior of Ysprint. Changed by indicated functions |
161
|
|
|
|
|
|
|
our $dec_places = 3; # Can be changed with call to Yapp_decimals() |
162
|
|
|
|
|
|
|
our $dec_width = 4; # Minimum width: 4, with 3 decimal paces |
163
|
|
|
|
|
|
|
our $print_zero = 0; # Default: FALSE: Skip zero-coefficient terms |
164
|
|
|
|
|
|
|
# Changed by Yapp_print0() |
165
|
|
|
|
|
|
|
our $start_high = 1; # Default: True: Start printing from high-degree terms. |
166
|
|
|
|
|
|
|
# If FALSE, start from low-degree terms. Set by |
167
|
|
|
|
|
|
|
# Yapp_start_high() |
168
|
|
|
|
|
|
|
our $margin = 1/(10**10); # What is close enugh to ignore rounding error? |
169
|
|
|
|
|
|
|
#our $margin = 1/(10**8); # What is close enugh to ignore rounding error? |
170
|
|
|
|
|
|
|
our $testmode = 0; # Turned on by class method Yapp_testmode() |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
BEGIN |
173
|
6
|
|
|
6
|
|
95612
|
{ |
174
|
|
|
|
|
|
|
#Math::BigFloat->accuracy(32); # In case I decide on a way to use complex |
175
|
|
|
|
|
|
|
# numbers with BigFloat |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
# |
178
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
179
|
|
|
|
|
|
|
# Class setting functions: These functions set [semi] global variables used |
180
|
|
|
|
|
|
|
# only by other function this package. |
181
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
182
|
|
|
|
|
|
|
# Yapp_testmode() Turns on or off the semi-global variable $testmode. |
183
|
|
|
|
|
|
|
# This is not a class method - it is a straightforward function call. |
184
|
|
|
|
|
|
|
# |
185
|
|
|
|
|
|
|
sub Yapp_testmode |
186
|
|
|
|
|
|
|
{ |
187
|
5
|
50
|
|
5
|
0
|
80
|
$testmode = $_[0] if (defined($_[0])); # Set the variable, if user sent |
188
|
|
|
|
|
|
|
# a value to stick in there |
189
|
5
|
|
|
|
|
15
|
return $testmode; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
192
|
|
|
|
|
|
|
# Yapp_margin(): Set the threshold for how close to require something be to |
193
|
|
|
|
|
|
|
# a solution in order to be considered close enough. This sets the variable |
194
|
|
|
|
|
|
|
# $margin, whcih is set to 1.0/(10.0 ** 10). It is intended to compensate |
195
|
|
|
|
|
|
|
# for inevitable rounding errors that, for example, make a real number look |
196
|
|
|
|
|
|
|
# like a complex number with a really tiny imaginary part (like 1.23456e-15i). |
197
|
|
|
|
|
|
|
# Parameter: |
198
|
|
|
|
|
|
|
# - (Optional) The floating point value to be the closeness factor. |
199
|
|
|
|
|
|
|
# Returns: |
200
|
|
|
|
|
|
|
# - The current (or new) setting for this margin. |
201
|
|
|
|
|
|
|
# |
202
|
|
|
|
|
|
|
sub Yapp_margin |
203
|
|
|
|
|
|
|
{ # If caller passed a parameter, use it. If not, just get the current setting |
204
|
|
|
|
|
|
|
# |
205
|
1
|
50
|
|
1
|
0
|
1188
|
my $new_margin = (defined($_[0])) ? $_[0] : $margin; |
206
|
1
|
|
|
|
|
3
|
$margin = $new_margin; # May be wasted but checking if different |
207
|
|
|
|
|
|
|
# also costs. |
208
|
1
|
|
|
|
|
79
|
return $margin; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
211
|
|
|
|
|
|
|
# dec_places(): Set or retrieve the precision to be used when |
212
|
|
|
|
|
|
|
# printing coefficients of the polynomial. Default is 3. |
213
|
|
|
|
|
|
|
# - To set precision: |
214
|
|
|
|
|
|
|
# yapp_ref->dec_places(17); # Set output precision of 17 decimal |
215
|
|
|
|
|
|
|
# # places to the right of the point, |
216
|
|
|
|
|
|
|
# Math::Yapp->dec_places(17); # Will have the same effect. |
217
|
|
|
|
|
|
|
# |
218
|
|
|
|
|
|
|
# - To retrieve it: |
219
|
|
|
|
|
|
|
# $int_var = jpoly_ref->dec_places(); |
220
|
|
|
|
|
|
|
# In both cases, it returns the precision to the caller. |
221
|
|
|
|
|
|
|
# |
222
|
|
|
|
|
|
|
sub dec_places |
223
|
|
|
|
|
|
|
{ |
224
|
0
|
|
|
0
|
0
|
0
|
my $self = shift(@_); |
225
|
0
|
0
|
|
|
|
0
|
if (@_ != 0) # Passed a parameter |
226
|
|
|
|
|
|
|
{ |
227
|
0
|
|
|
|
|
0
|
$dec_places = $_[0]; # Set the number of places to that |
228
|
0
|
|
|
|
|
0
|
$dec_width = $dec_places + 1; # Add 1 place for decimal point. (Probably |
229
|
|
|
|
|
|
|
# not needed but cleaner logic) |
230
|
|
|
|
|
|
|
} |
231
|
0
|
|
|
|
|
0
|
return $dec_places; # Either way, that's the return value |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
# |
234
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
235
|
|
|
|
|
|
|
sub Yapp_decimals |
236
|
|
|
|
|
|
|
{ # (Replaces dec_places method) |
237
|
13
|
100
|
|
13
|
1
|
1182
|
if (defined($_[0])) # If user passed a parameter |
238
|
|
|
|
|
|
|
{ |
239
|
12
|
|
|
|
|
23
|
$dec_places = $_[0]; # Set global decimal places for printf() |
240
|
12
|
|
|
|
|
25
|
$dec_width = $dec_places + 1; # Add 1 place for decimal point. (Probably |
241
|
|
|
|
|
|
|
} # not needed but cleaner logic) |
242
|
|
|
|
|
|
|
# else # No parameter; just want to know it |
243
|
|
|
|
|
|
|
# { } # (Nothing really; just a placeholder |
244
|
13
|
|
|
|
|
31
|
return $dec_places; # Either way, send that back to caller |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
247
|
|
|
|
|
|
|
# Yapp_print0(): Function to dictate if Ysprint will print terms with 0 |
248
|
|
|
|
|
|
|
# coefficient. |
249
|
|
|
|
|
|
|
# Parameters: |
250
|
|
|
|
|
|
|
# - (Optional) 1 (TRUE) or 0 (FALSE) |
251
|
|
|
|
|
|
|
# If called w/no parameter, just returns the current value |
252
|
|
|
|
|
|
|
# Returns: |
253
|
|
|
|
|
|
|
# The current or newley set value. |
254
|
|
|
|
|
|
|
sub Yapp_print0 |
255
|
|
|
|
|
|
|
{ |
256
|
4
|
50
|
|
4
|
1
|
40
|
if (defined($_[0])) {$print_zero = $_[0]; } |
|
4
|
|
|
|
|
12
|
|
257
|
4
|
|
|
|
|
12
|
return $print_zero; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
260
|
|
|
|
|
|
|
# Yapp_start_high() - Sets $start_high to 0 or 1, or just returns that value. |
261
|
|
|
|
|
|
|
# This decides if Ysprint will generate output starting from high-degree |
262
|
|
|
|
|
|
|
# terms (default) or start low and work its way up in degree. |
263
|
|
|
|
|
|
|
# |
264
|
|
|
|
|
|
|
sub Yapp_start_high |
265
|
|
|
|
|
|
|
{ |
266
|
4
|
50
|
|
4
|
1
|
30
|
if (defined($_[0])) {$start_high = $_[0]; } |
|
4
|
|
|
|
|
13
|
|
267
|
4
|
|
|
|
|
10
|
return $start_high; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
# |
270
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
271
|
|
|
|
|
|
|
# Constructors: |
272
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
273
|
|
|
|
|
|
|
# Math::Yapp::new(): Create a new polynomial# |
274
|
|
|
|
|
|
|
# This method is heavily overloaded but it alwys returns the same type: a |
275
|
|
|
|
|
|
|
# reference to a Yapp polynomial object. It will accept the following |
276
|
|
|
|
|
|
|
# parameters: |
277
|
|
|
|
|
|
|
# - Nothing; It returns a degenerate polynomial with no coefficients of |
278
|
|
|
|
|
|
|
# degree, the equivalent of a NULL polynomial. |
279
|
|
|
|
|
|
|
# - A list of numbers, real or complex. The first number is the 0-degree |
280
|
|
|
|
|
|
|
# coefficient and the last, [n], is the coefficient of the highest-degree |
281
|
|
|
|
|
|
|
# term. |
282
|
|
|
|
|
|
|
# - A reference to a list of numbers i.e. \@list. THis list is identical |
283
|
|
|
|
|
|
|
# to the explicit list described above. |
284
|
|
|
|
|
|
|
# - A string in the form nnnX^m1 + nnnX^m2 .. etc. |
285
|
|
|
|
|
|
|
# |
286
|
|
|
|
|
|
|
# A note about complex coefficients: |
287
|
|
|
|
|
|
|
# - In the string-form parameter, it must be in the form +-(REAL+-IMAGi) |
288
|
|
|
|
|
|
|
# - In the list (or list-reference) form, a complex may be of the above |
289
|
|
|
|
|
|
|
# string form or a true complex object, as returned by Math::Complex |
290
|
|
|
|
|
|
|
# |
291
|
|
|
|
|
|
|
sub new |
292
|
|
|
|
|
|
|
{ |
293
|
131
|
|
|
131
|
1
|
197
|
my $class = shift(@_); # Get class name out of the way |
294
|
131
|
|
|
|
|
246
|
my $self = {}; # Create new entity |
295
|
131
|
|
|
|
|
159
|
bless $self; # Makes it an object |
296
|
131
|
|
|
|
|
141
|
my $lc; # Loop counter (general purpose here) |
297
|
|
|
|
|
|
|
|
298
|
131
|
|
|
|
|
700
|
$self->{degree} = 0; # Initially IS a 0-degree polynomial |
299
|
131
|
|
|
|
|
467
|
$self->{variable} = "X"; # For printing, use this variable. Can be set |
300
|
131
|
|
|
|
|
419
|
$self->{coeff}[0] = 0; # Even a degenerate polynomial needs a coefficient |
301
|
|
|
|
|
|
|
# to any string using the method variable() |
302
|
131
|
|
|
|
|
167
|
my $aref = 0; # Will step through one array or another |
303
|
131
|
|
|
|
|
178
|
my $coef_count = @_; # How many coefficients were passed to me? |
304
|
|
|
|
|
|
|
|
305
|
131
|
100
|
|
|
|
372
|
if ($coef_count > 1) # If caller passed me a list of numbers |
|
|
100
|
|
|
|
|
|
306
|
37
|
|
|
|
|
120
|
{ $aref = \@_; } # Point to the parameter list, save for soon |
307
|
|
|
|
|
|
|
elsif ($coef_count == 1) # Passed only one parameter: Our possibilities: |
308
|
|
|
|
|
|
|
{ |
309
|
84
|
100
|
|
|
|
200
|
if (ref($_[0]) eq "ARRAY") # I got a reference to a list |
310
|
49
|
|
|
|
|
79
|
{ $aref = shift(@_); } # Reference that list with same $aref |
311
|
|
|
|
|
|
|
else # Next possibility: The parameter is a string |
312
|
35
|
|
|
|
|
108
|
{ $self->str2yapp($_[0]); } # Turn the string into a polynomial |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
# Remaining possibility: That $coef_count == 0 and user wants that degenerate |
315
|
|
|
|
|
|
|
# polynommial. Do nothing for now; Will return a zero polynomial |
316
|
|
|
|
|
|
|
# |
317
|
131
|
100
|
|
|
|
554
|
if ($aref != 0) # If we have an array pointer |
318
|
|
|
|
|
|
|
{ # step through the array to build the polynomial |
319
|
86
|
|
|
|
|
133
|
$self->{degree} = -1; # Degree shall end up 1 less than count of terms |
320
|
86
|
|
|
|
|
163
|
for ($lc = 0; $lc <= $#{$aref}; $lc++) # Build array of coefficients |
|
402
|
|
|
|
|
976
|
|
321
|
|
|
|
|
|
|
{ |
322
|
|
|
|
|
|
|
# Note: I am adding the 0.0 to force Perl to store the value as a |
323
|
|
|
|
|
|
|
# number (real or ref(complex)) |
324
|
|
|
|
|
|
|
# |
325
|
316
|
|
|
|
|
802
|
$self->{coeff}[$lc] = $aref->[$lc] + 0.0; # Use next number on list |
326
|
316
|
|
|
|
|
3783
|
$self->{degree}++; # Don't lose track of degree |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
131
|
|
|
|
|
322
|
$self->refresh(); # Last minute bookkeeping on our new object |
331
|
131
|
|
|
|
|
395
|
return $self; # See? Even the empty object gets passed back |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
# |
334
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
335
|
|
|
|
|
|
|
# A neater version of calling new() - Just call Yapp(parameter[s]). |
336
|
|
|
|
|
|
|
# Accepts all types that new() accepts. |
337
|
|
|
|
|
|
|
# |
338
|
|
|
|
|
|
|
sub Yapp |
339
|
|
|
|
|
|
|
{ |
340
|
226
|
|
|
226
|
1
|
10154
|
my $class = $_[0]; # Most likely the class name, may be string, array |
341
|
226
|
|
|
|
|
241
|
my $ryapp; # Alternative return object to caller |
342
|
|
|
|
|
|
|
|
343
|
226
|
100
|
|
|
|
642
|
if (ref($class) eq $class_name) # If this is copy-call, then the param |
344
|
|
|
|
|
|
|
{ # is not really a class name but a Yapp |
345
|
95
|
|
|
|
|
220
|
$ryapp = $class->Yapp_copy(); # instance; construct clone of original |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
else # Not a reference |
348
|
131
|
|
|
|
|
388
|
{ $ryapp = __PACKAGE__->new(@_); } # Just pass all parameter(s) to new() |
349
|
226
|
|
|
|
|
547
|
return $ryapp; # However we got it, return it to caller |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
353
|
|
|
|
|
|
|
# Yapp_copy() - Internal utility function, should be called by new() in case |
354
|
|
|
|
|
|
|
# a copy-constructor is called for. |
355
|
|
|
|
|
|
|
# Parameters: |
356
|
|
|
|
|
|
|
# - (Implict) A reference to the source Yapp-polynomial for the copy |
357
|
|
|
|
|
|
|
# Returns: |
358
|
|
|
|
|
|
|
# - A referecne to a new Yapp object identical to the source |
359
|
|
|
|
|
|
|
# |
360
|
|
|
|
|
|
|
sub Yapp_copy |
361
|
|
|
|
|
|
|
{ |
362
|
95
|
|
|
95
|
0
|
127
|
my $self = shift(@_); # Capture the object |
363
|
95
|
|
|
|
|
7297
|
my $new_yapp = dclone($self); # Duplicate object, get back reference |
364
|
95
|
|
|
|
|
217
|
return $new_yapp; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
# |
367
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
368
|
|
|
|
|
|
|
# refresh() - (Internal?) Method to perform two smoothing operations on |
369
|
|
|
|
|
|
|
# the given Yapp polynomial: |
370
|
|
|
|
|
|
|
# - In case it got omitted in a contructor, if a coefficient between 0 and |
371
|
|
|
|
|
|
|
# the degree is still undefined, stick a 0.0 in there. |
372
|
|
|
|
|
|
|
# - If any complex coefficients have a 0 imaginary part, replace it with the |
373
|
|
|
|
|
|
|
# real part only, as a real coefficient. e.g. cplx(-2.13,9) => -2.13 |
374
|
|
|
|
|
|
|
# - It looks for any 0 coefficient in the highest-degree places so that a 5th |
375
|
|
|
|
|
|
|
# degree polynomila with 0 coefficients in the6th and 7th dregee places |
376
|
|
|
|
|
|
|
# does not appear to be a 7th degree polynomial |
377
|
|
|
|
|
|
|
# Parameter: |
378
|
|
|
|
|
|
|
# - (Implicit) The Yapp to thus fixed up |
379
|
|
|
|
|
|
|
# Returns: |
380
|
|
|
|
|
|
|
# - The same reference but it has been mutated as described. |
381
|
|
|
|
|
|
|
# |
382
|
|
|
|
|
|
|
sub refresh |
383
|
|
|
|
|
|
|
{ |
384
|
131
|
|
|
131
|
0
|
179
|
my $self = shift(@_); |
385
|
131
|
|
|
|
|
271
|
my $slc; # Loop counter for counting up my own array |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# This first loop can handle the undefined coefficient problem as well as the |
388
|
|
|
|
|
|
|
# disguised real-number propmlem |
389
|
|
|
|
|
|
|
# |
390
|
131
|
|
|
|
|
371
|
for ($slc = 0; $slc <= $self->{degree}; $slc++) |
391
|
|
|
|
|
|
|
{ |
392
|
|
|
|
|
|
|
# Make sure there are no undefined coefficients in there. |
393
|
|
|
|
|
|
|
# (Zero is better than nothing.) |
394
|
|
|
|
|
|
|
# |
395
|
382
|
100
|
|
|
|
1172
|
$self->{coeff}[$slc] = 0.0 if (! defined($self->{coeff}[$slc])); |
396
|
|
|
|
|
|
|
|
397
|
382
|
|
|
|
|
799
|
realify($self->{coeff}[$slc]); # In case of complex coeff w Im == 0 |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# Next: Make sure the highest-degree term has a non-zero coefficient; |
401
|
|
|
|
|
|
|
# adjust the degree to match this. (Except if degree == 0. That's legit) |
402
|
|
|
|
|
|
|
# |
403
|
131
|
|
|
|
|
363
|
for ($slc = $self->{degree}; $slc > 0; $slc--) |
404
|
|
|
|
|
|
|
{ |
405
|
102
|
100
|
|
|
|
305
|
last if ($self->{coeff}[$slc] != 0); # Found my non-0 highest term; done |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Still here: The current high-order term is a degenerate term with a |
408
|
|
|
|
|
|
|
# zero-coefficient. Lose it! |
409
|
|
|
|
|
|
|
# |
410
|
4
|
|
|
|
|
10
|
undef($self->{coeff}[$slc]); # This loses the term |
411
|
4
|
|
|
|
|
10
|
$self->{degree}-- # This insures (?) it won't be referenced |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# Finally: Since the coefficients may have been diddled, the coefficients of |
415
|
|
|
|
|
|
|
# the derivative and integral, if they have been generated, are unreliable. |
416
|
|
|
|
|
|
|
# Rather than regenerate them now, just lose them. If they are needed again |
417
|
|
|
|
|
|
|
# they will be automatically and transparently regenreated. |
418
|
|
|
|
|
|
|
# |
419
|
131
|
50
|
|
|
|
383
|
undef $self->{derivative} if (defined($self->{derivative}[0])); |
420
|
131
|
50
|
|
|
|
367
|
undef $self->{integral} if (defined($self->{integral}[0])); |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# After all that, it is *still* possible for {variable} to be undefined. |
423
|
|
|
|
|
|
|
# This will be the case for a constant term alone, a 0-degree polynomial |
424
|
|
|
|
|
|
|
# string. Put the default in there now. |
425
|
|
|
|
|
|
|
# |
426
|
131
|
50
|
|
|
|
284
|
$self->{variable} = "X" if (!defined($self->{variable})); |
427
|
131
|
|
|
|
|
193
|
return ($self); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
# |
430
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
431
|
|
|
|
|
|
|
# Ysprint(): Returns a string with the polynomial in some standard form, |
432
|
|
|
|
|
|
|
# suitable for printing. |
433
|
|
|
|
|
|
|
# Parameters: |
434
|
|
|
|
|
|
|
# - (Implicit) The Yapp opject |
435
|
|
|
|
|
|
|
# - (Optional) The number of decimal places to use for the coefficients. If |
436
|
|
|
|
|
|
|
# omitted, we will use themodule global $dec_places. See Yapp_decimals for |
437
|
|
|
|
|
|
|
# more information on that. |
438
|
|
|
|
|
|
|
# Default options: |
439
|
|
|
|
|
|
|
# - Use X as the variable |
440
|
|
|
|
|
|
|
# - Skip terms with 0-coefficients |
441
|
|
|
|
|
|
|
# - Output highest powers first, as one would write in algebra class |
442
|
|
|
|
|
|
|
# Funtions have been provided to change these behaviors. These are: |
443
|
|
|
|
|
|
|
# - Yapp_decimals(), so that you may call method Ysprint() without a parameter. |
444
|
|
|
|
|
|
|
# - Yapp_print0() |
445
|
|
|
|
|
|
|
# - Yapp_start_high() |
446
|
|
|
|
|
|
|
# |
447
|
|
|
|
|
|
|
sub Ysprint |
448
|
|
|
|
|
|
|
{ |
449
|
79
|
|
|
79
|
1
|
1504
|
my $self = shift(@_); |
450
|
79
|
50
|
|
|
|
185
|
my $places = (defined($_[0])) ? $_[0] : $dec_places; |
451
|
79
|
50
|
|
|
|
163
|
my $dwidth = (defined($_[0])) ? $places + 1 : $dec_width; |
452
|
|
|
|
|
|
|
|
453
|
79
|
|
|
|
|
202
|
my $var_name = $self->variable(); |
454
|
79
|
|
|
|
|
116
|
my $out_string = ""; |
455
|
79
|
|
|
|
|
117
|
my $use_fmt = \$first_term_fmt; # Make sure I use this first |
456
|
|
|
|
|
|
|
|
457
|
79
|
|
|
|
|
134
|
my $lc_start; # Start loop at high or low degree terms? |
458
|
79
|
|
|
|
|
87
|
my $lc_finish = 0; # And where do I finish, based on above? |
459
|
79
|
|
|
|
|
109
|
my $lc_diff = -1; # and in which direction doed my loop counter go? |
460
|
|
|
|
|
|
|
|
461
|
79
|
100
|
|
|
|
162
|
if ($start_high) # Default: Start from high-degree terms |
462
|
|
|
|
|
|
|
{ |
463
|
37
|
|
|
|
|
63
|
$lc_start = $self->{degree}; # Start loop at high-power term |
464
|
37
|
|
|
|
|
50
|
$lc_finish = 0; # End loop at 0-power term |
465
|
37
|
|
|
|
|
52
|
$lc_diff = -1; # Normal behavior: Start high, step low |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
else |
468
|
|
|
|
|
|
|
{ |
469
|
42
|
|
|
|
|
45
|
$lc_start = 0; # Start loop at 0-degree term |
470
|
42
|
|
|
|
|
60
|
$lc_finish = $self->{degree}; # End loop at high-degree term |
471
|
42
|
|
|
|
|
42
|
$lc_diff = 1; # work my way UP the degrees |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
79
|
|
|
|
|
121
|
for (my $lc = $lc_start; ; $lc += $lc_diff) #(Check value of $lc inside |
475
|
|
|
|
|
|
|
{ # loop, not in loop header) |
476
|
585
|
100
|
100
|
|
|
2552
|
last if ( ($lc > $self->{degree}) || ($lc < 0) ); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# How to format the coefficient depends on whether it is real or complex |
479
|
|
|
|
|
|
|
# |
480
|
506
|
|
|
|
|
608
|
my $term = ""; # Start the term as a null string and build from there |
481
|
506
|
100
|
|
|
|
1319
|
if (ref($self->{coeff}[$lc]) eq $class_cplx) |
482
|
|
|
|
|
|
|
{ |
483
|
|
|
|
|
|
|
# First term should not have a + sign if coefficient is positive |
484
|
|
|
|
|
|
|
# Constant term should not dislay X to the 0th degree |
485
|
|
|
|
|
|
|
# First degree term should display as X, not X^1 |
486
|
|
|
|
|
|
|
# All other terms should include both aspects. |
487
|
|
|
|
|
|
|
# |
488
|
33
|
100
|
|
|
|
90
|
if ($lc == $lc_start) # If I'm generating the first term |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
489
|
7
|
|
|
|
|
9
|
{ $use_fmt = \$first_term_fmt_i;} # No + sign |
490
|
|
|
|
|
|
|
elsif ($lc == 1) |
491
|
5
|
|
|
|
|
8
|
{ $use_fmt = \$one_term_fmt_i; } # No exponent |
492
|
|
|
|
|
|
|
elsif ($lc == 0 ) |
493
|
1
|
|
|
|
|
2
|
{ $use_fmt = \$zero_term_fmt_i; } # No variable |
494
|
|
|
|
|
|
|
else |
495
|
20
|
|
|
|
|
27
|
{ $use_fmt = \$term_fmt_i; } # +/-,var and exponent |
496
|
|
|
|
|
|
|
|
497
|
33
|
50
|
33
|
|
|
107
|
if ( ($self->{coeff}[$lc] != $czero) || ($print_zero) ) |
498
|
|
|
|
|
|
|
{ |
499
|
33
|
|
|
|
|
563
|
$term =sprintf(${$use_fmt}, |
|
33
|
|
|
|
|
76
|
|
500
|
|
|
|
|
|
|
$dwidth, $places, ($self->coefficient($lc))->Re(), |
501
|
|
|
|
|
|
|
$dwidth, $places, ($self->coefficient($lc))->Im(), |
502
|
|
|
|
|
|
|
$var_name, $lc); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
else # The ref() function did not say "Complex". So it |
507
|
|
|
|
|
|
|
{ # must be a real coefficient. |
508
|
|
|
|
|
|
|
# Note: Same sign, variable, exponent conventions apply as above |
509
|
|
|
|
|
|
|
# |
510
|
473
|
100
|
|
|
|
1778
|
if ($lc == $lc_start) # If I'm generating the first term |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
511
|
72
|
|
|
|
|
108
|
{ $use_fmt = \$first_term_fmt;} # No + |
512
|
|
|
|
|
|
|
elsif ($lc == 1) |
513
|
74
|
|
|
|
|
120
|
{ $use_fmt = \$one_term_fmt; } # No exponent |
514
|
|
|
|
|
|
|
elsif ($lc == 0) |
515
|
36
|
|
|
|
|
69
|
{ $use_fmt = \$zero_term_fmt; } # No variable |
516
|
|
|
|
|
|
|
else |
517
|
291
|
|
|
|
|
417
|
{ $use_fmt = \$term_fmt; } # +/-,variable and exponent |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
|
520
|
473
|
100
|
66
|
|
|
1435
|
if ( ($self->{coeff}[$lc] != 0.0) || ($print_zero) ) |
521
|
|
|
|
|
|
|
{ |
522
|
450
|
|
|
|
|
536
|
$term = sprintf(${$use_fmt}, $dwidth, $places, |
|
450
|
|
|
|
|
1071
|
|
523
|
|
|
|
|
|
|
$self->coefficient($lc), $var_name, $lc); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
# Second part of the patch I described when I defined $zero_power_pat: |
527
|
|
|
|
|
|
|
# Lose the X^0 part of the term if I am on the 0-degree term. |
528
|
|
|
|
|
|
|
# |
529
|
506
|
100
|
|
|
|
3935
|
$term =~ s/($zero_power_pat)$// if ($lc == 0); |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# If the output target string is still empty, this is the first occupant |
532
|
|
|
|
|
|
|
# |
533
|
506
|
100
|
|
|
|
1022
|
if ("$out_string" eq "") { $out_string = $term; } |
|
79
|
|
|
|
|
182
|
|
534
|
|
|
|
|
|
|
else # Something alrady in there; |
535
|
|
|
|
|
|
|
{ # Append blank and current term |
536
|
427
|
100
|
|
|
|
1513
|
$out_string = sprintf("%s %s", $out_string, $term) |
537
|
|
|
|
|
|
|
if ($term ne ""); #(Provided our term has substance also) |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} #(End of loop; at exit, $out_string is built) |
540
|
79
|
|
|
|
|
7814
|
return $out_string; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
# |
543
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
544
|
|
|
|
|
|
|
# print(): Method to print a formatted Yapp polynomial. Basically, just a |
545
|
|
|
|
|
|
|
# wrapper for Ysprint. I expected this to be used mainly in debugging |
546
|
|
|
|
|
|
|
# sessions so that I can say "p $something" but that doesn't work. |
547
|
|
|
|
|
|
|
# (And I'm not ready to start with Tie::Handle at this juncture, |
548
|
|
|
|
|
|
|
# although that seems to be the way to override the built-in print |
549
|
|
|
|
|
|
|
# function.) |
550
|
|
|
|
|
|
|
# Parameters: |
551
|
|
|
|
|
|
|
# - (Implicit) Ref to a Yapp object |
552
|
|
|
|
|
|
|
# - (Optional) Number of decimal places for the coefficients |
553
|
|
|
|
|
|
|
# Returns: (Whatever) |
554
|
|
|
|
|
|
|
# |
555
|
|
|
|
|
|
|
sub print |
556
|
|
|
|
|
|
|
{ |
557
|
0
|
|
|
0
|
1
|
0
|
my $yobj = shift(@_); |
558
|
0
|
|
|
|
|
0
|
my $ystring = $yobj->Ysprint($_[0]); # There may be a places paremeter |
559
|
0
|
|
|
|
|
0
|
print $ystring; # Output it - no automatic \n |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
562
|
|
|
|
|
|
|
# Csprint(): Internal utility function (not exported) to format a complex |
563
|
|
|
|
|
|
|
# number in my display format e.g. (-3.24+6.03i), including the |
564
|
|
|
|
|
|
|
# parenteses. The number of decimal places is based on the same |
565
|
|
|
|
|
|
|
# Yapp_decimals() settings of $dec_places and $dec_width used by |
566
|
|
|
|
|
|
|
# Ysprint. |
567
|
|
|
|
|
|
|
# Parameters: |
568
|
|
|
|
|
|
|
# - [Reference to] a complex number of type Math::Complex |
569
|
|
|
|
|
|
|
# - (Optional) Number of decimal places to display. If omitted, use the |
570
|
|
|
|
|
|
|
# number in $dec_places |
571
|
|
|
|
|
|
|
# Returns: |
572
|
|
|
|
|
|
|
# - A parenthesized string in the form shown in the above blurb. |
573
|
|
|
|
|
|
|
# Note: If a scalar is passed instead, it will be returned as received |
574
|
|
|
|
|
|
|
# |
575
|
|
|
|
|
|
|
sub Csprint |
576
|
|
|
|
|
|
|
{ |
577
|
34
|
|
|
34
|
0
|
157
|
my $cnum = shift(@_); |
578
|
34
|
50
|
|
|
|
84
|
my $places = (defined($_[0])) ? $_[0] : $dec_places; |
579
|
34
|
50
|
|
|
|
62
|
my $dwidth = (defined($_[0])) ? $places + 1 : $dec_width; |
580
|
34
|
100
|
|
|
|
1233
|
return $cnum if (ref($cnum) ne $class_cplx); # Don't bother with non-complex |
581
|
|
|
|
|
|
|
|
582
|
26
|
|
|
|
|
82
|
my $rstring = sprintf("(%*.*f%+*.*fi)", |
583
|
|
|
|
|
|
|
$dwidth, $places, $cnum->Re(), |
584
|
|
|
|
|
|
|
$dwidth, $places, $cnum->Im()); |
585
|
26
|
|
|
|
|
876
|
return $rstring; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
# |
588
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
589
|
|
|
|
|
|
|
# |
590
|
|
|
|
|
|
|
# realify(): Internal utility function. Loses extremely small real or imaginary |
591
|
|
|
|
|
|
|
# component of a complex number, since we can assume it came from |
592
|
|
|
|
|
|
|
# a rounding error. |
593
|
|
|
|
|
|
|
# Parameter: |
594
|
|
|
|
|
|
|
# - A real number or [Ref to] a Math::Complex number |
595
|
|
|
|
|
|
|
# OR |
596
|
|
|
|
|
|
|
# - An array of numbers that each may be real or complex. In this case we |
597
|
|
|
|
|
|
|
# assume it was called in a list context. Violate that assumption and die! |
598
|
|
|
|
|
|
|
# Returns: |
599
|
|
|
|
|
|
|
# - If real: |
600
|
|
|
|
|
|
|
# o If non-trivial, just return that number. |
601
|
|
|
|
|
|
|
# o If trivial, return 0.0 |
602
|
|
|
|
|
|
|
# - If complex: |
603
|
|
|
|
|
|
|
# o If the imaginary part is very small, less than $margin in absolute value, |
604
|
|
|
|
|
|
|
# return only the real part as a real. |
605
|
|
|
|
|
|
|
# o If real part is trivial, set it to 0.0 outright |
606
|
|
|
|
|
|
|
# o If both components are non-trivial, return the reference unscathed. |
607
|
|
|
|
|
|
|
# - If an array; |
608
|
|
|
|
|
|
|
# o And array of numbers thus transformed |
609
|
|
|
|
|
|
|
# If called with no return (void context like the chomp function) operate |
610
|
|
|
|
|
|
|
# directly on the parameter or the array of numbers. |
611
|
|
|
|
|
|
|
# |
612
|
|
|
|
|
|
|
sub realify |
613
|
|
|
|
|
|
|
{ |
614
|
2237
|
|
|
2237
|
0
|
3041
|
my $thing = $_[0]; # Don't shift, we might need to modify in place |
615
|
2237
|
|
|
|
|
2251
|
my $rval = 0.0; |
616
|
|
|
|
|
|
|
|
617
|
2237
|
50
|
|
|
|
4938
|
die "You may not pass a list to realify(); use array reference" |
618
|
|
|
|
|
|
|
if ($#_ > 0); |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# Real or complex, if absolute value is really tiny, "correct" to 0 |
621
|
|
|
|
|
|
|
# |
622
|
2237
|
100
|
|
|
|
5608
|
if (wantarray) # Array context: Assume $thing is an |
|
|
100
|
|
|
|
|
|
623
|
|
|
|
|
|
|
{ # array reference. |
624
|
|
|
|
|
|
|
#my @rlist = map {realify($_)} @$thing; # Return list |
625
|
|
|
|
|
|
|
# Don't use map for this recursive call. Apparently because there is an |
626
|
|
|
|
|
|
|
# array as the lvalue to the map{}, realify thinks it was called in the |
627
|
|
|
|
|
|
|
# list context. Rather resort to brute force loop. |
628
|
|
|
|
|
|
|
# |
629
|
184
|
|
|
|
|
203
|
my @rlist; # List to be returned |
630
|
184
|
|
|
|
|
611
|
for (my $rlc = 0; $rlc <= $#$thing; $rlc++) |
631
|
|
|
|
|
|
|
{ |
632
|
864
|
|
|
|
|
1942
|
$rlist[$rlc] = realify($thing->[$rlc]); |
633
|
|
|
|
|
|
|
} |
634
|
184
|
|
|
|
|
520
|
return @rlist; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
elsif (defined(wantarray)) # Scalar context: assume $thing is a scalar |
637
|
|
|
|
|
|
|
{ # and caller wants a new value returned |
638
|
1369
|
100
|
|
|
|
4007
|
return (0.0) if (abs($thing) < $margin); # Just tiny abs value: Quickie |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# If either component of a complex number is really tiny, set it to 0. |
641
|
|
|
|
|
|
|
# And if it's the imaginary component, lose it altogether |
642
|
|
|
|
|
|
|
# |
643
|
1175
|
100
|
|
|
|
5684
|
if (ref($thing) eq $class_cplx) # If I have not zeroed it above then |
644
|
|
|
|
|
|
|
{ # I am permitted to check its components |
645
|
183
|
|
|
|
|
263
|
$rval = $thing; # Preserve original complex number |
646
|
183
|
100
|
|
|
|
535
|
if (abs($thing->Re) < $margin) # Need to keep real part, even if tiny |
647
|
|
|
|
|
|
|
{ |
648
|
5
|
|
|
|
|
62
|
$rval = cplx(0.0, $thing->Im) # So just zero it out |
649
|
|
|
|
|
|
|
} |
650
|
183
|
100
|
|
|
|
2607
|
if (abs($thing->Im) < $margin) # If imaginary component is tiny |
651
|
|
|
|
|
|
|
{ |
652
|
37
|
|
|
|
|
398
|
$rval = $thing->Re; # lose it completely and return a real |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
else # So it is real and its abs val is >= $margin |
656
|
|
|
|
|
|
|
{ # ==> I already know in can't zero it. |
657
|
992
|
|
|
|
|
1300
|
$rval = $thing; # So the return value is the original number |
658
|
|
|
|
|
|
|
} |
659
|
1175
|
|
|
|
|
6734
|
return $rval; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
else # Void context: Operate on the passed object. |
662
|
|
|
|
|
|
|
{ |
663
|
684
|
100
|
|
|
|
1501
|
if (ref($thing) eq "ARRAY") # Realifying every element in an array? |
664
|
|
|
|
|
|
|
{ |
665
|
184
|
|
|
|
|
337
|
@{$thing} = realify($thing); # Tickle the wantarray code above |
|
184
|
|
|
|
|
737
|
|
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
else # Realify only one object |
668
|
|
|
|
|
|
|
{ # Just work on that one |
669
|
500
|
|
|
|
|
979
|
$_[0] = realify($_[0]); # Tickle the defined(wantarray) code above |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
# Note: No return statement. |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
# |
675
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
676
|
|
|
|
|
|
|
# all_real() - Method to just know if a Yapp polynomial has strictly real |
677
|
|
|
|
|
|
|
# coefficients. |
678
|
|
|
|
|
|
|
# Parameter: |
679
|
|
|
|
|
|
|
# - [Ref to] a Yapp polynomial object |
680
|
|
|
|
|
|
|
# Returns: |
681
|
|
|
|
|
|
|
# - 1 (true) if all coeffienents are real |
682
|
|
|
|
|
|
|
# - 0 (false) if even one os complex |
683
|
|
|
|
|
|
|
# |
684
|
|
|
|
|
|
|
sub all_real |
685
|
|
|
|
|
|
|
{ |
686
|
6
|
|
|
6
|
0
|
13
|
my $self = shift(@_); |
687
|
6
|
|
|
|
|
9
|
my $allreal = 1; # OK to start optimistic |
688
|
6
|
|
|
|
|
7
|
foreach my $coef (@{$self->{coeff}}) |
|
6
|
|
|
|
|
15
|
|
689
|
|
|
|
|
|
|
{ |
690
|
29
|
50
|
|
|
|
71
|
if (ref($coef) eq $class_cplx) |
691
|
|
|
|
|
|
|
{ |
692
|
0
|
|
|
|
|
0
|
$allreal = 0; # Not all real |
693
|
0
|
|
|
|
|
0
|
last; # No point in continuing |
694
|
|
|
|
|
|
|
} |
695
|
|
|
|
|
|
|
} |
696
|
6
|
|
|
|
|
26
|
return $allreal; |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
# |
699
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
700
|
|
|
|
|
|
|
# Yapp_equiv(): Method to compare a pair of Yapp polynomials, but only by the |
701
|
|
|
|
|
|
|
# aspects that count: the degree and the coefficients. I don't |
702
|
|
|
|
|
|
|
# care about the variable name nor about {deviative} or {integral} |
703
|
|
|
|
|
|
|
# for this comparison |
704
|
|
|
|
|
|
|
# Parameters: |
705
|
|
|
|
|
|
|
# - (Implicit) One Yapp to compare against |
706
|
|
|
|
|
|
|
# - The Yapp to compare to the first. |
707
|
|
|
|
|
|
|
# (At this time, I am not overloading the == operator) |
708
|
|
|
|
|
|
|
# |
709
|
|
|
|
|
|
|
sub Yapp_equiv |
710
|
|
|
|
|
|
|
{ |
711
|
1
|
|
|
1
|
0
|
7
|
my ($self, $compr) = @_; |
712
|
1
|
|
|
|
|
3
|
my $comp_ok = 1; # Start by assuming true |
713
|
|
|
|
|
|
|
|
714
|
1
|
50
|
|
|
|
4
|
if ($self->{degree} == $compr->{degree}) |
715
|
0
|
|
|
|
|
0
|
{ |
716
|
1
|
|
|
|
|
5
|
for (my $dlc = 0; $dlc <= $self->{degree}; $dlc++) |
717
|
|
|
|
|
|
|
{ |
718
|
6
|
50
|
|
|
|
17
|
$comp_ok = 0 if ($self->{coeff}[$dlc] != $compr->{coeff}[$dlc]); |
719
|
6
|
50
|
|
|
|
18
|
last unless $comp_ok; # If found unequal coefficients, quit |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
else {$comp_ok = 0;} # If even degrees don't agree, soooo not equivalent! |
723
|
|
|
|
|
|
|
|
724
|
1
|
|
|
|
|
5
|
return $comp_ok; |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
# |
727
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
728
|
|
|
|
|
|
|
# str2cplx() - Internal utility function, not an object method. |
729
|
|
|
|
|
|
|
# This function accepts a string and converts it to a complex number. |
730
|
|
|
|
|
|
|
# |
731
|
|
|
|
|
|
|
# Parameter: |
732
|
|
|
|
|
|
|
# - A string already determined to match our syntax for a complex number |
733
|
|
|
|
|
|
|
# Returns: |
734
|
|
|
|
|
|
|
# - A [reference to] a proper Math::Complex::cplx complex number |
735
|
|
|
|
|
|
|
# |
736
|
|
|
|
|
|
|
sub str2cplx |
737
|
|
|
|
|
|
|
{ |
738
|
4
|
|
|
4
|
0
|
8
|
my $c_string = shift(@_); # Grab the string |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
# Plan: I will need to take apart the string, first stripping the parentheses |
741
|
|
|
|
|
|
|
# and the letter i, then separating the components by splitting it by the |
742
|
|
|
|
|
|
|
# sign between the components. But take care to not be fooled by a possible |
743
|
|
|
|
|
|
|
# sign before the real component |
744
|
|
|
|
|
|
|
# |
745
|
4
|
|
|
|
|
5
|
my $rsign = 1; # Initially assume real component is positive |
746
|
4
|
|
|
|
|
7
|
my $rval = $czero; # Return value: Establish it as a complex number |
747
|
4
|
|
|
|
|
19
|
$c_string =~ s/[\(\)i]//g; # First, lose the parentheses and "i" |
748
|
4
|
100
|
|
|
|
56
|
if ($c_string =~ /^($sign_pat)/) # Now, if there *is* a sign before the |
749
|
|
|
|
|
|
|
{ # real component |
750
|
2
|
50
|
|
|
|
9
|
$rsign = ($1 eq "-") ? -1 : 1; # then [re]set the multiplier accordingly |
751
|
2
|
|
|
|
|
10
|
$c_string =~ s/$sign_pat//; # and lose sign preceding the real |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
# And now the complex number (still a string) looks like A[+-]Bi |
755
|
|
|
|
|
|
|
# |
756
|
4
|
|
|
|
|
20
|
my @c_pair = split /([+-])/, $c_string; # Split by that sign, but enclose |
757
|
|
|
|
|
|
|
# the pattern in () |
758
|
|
|
|
|
|
|
# The effect of ([+-]), as opposed to [+-] without the parentheses: |
759
|
|
|
|
|
|
|
# I capture the actual splitting string; the array produced by the split |
760
|
|
|
|
|
|
|
# includes the splitting string itself. Thus, my array contains [A, -, B] |
761
|
|
|
|
|
|
|
# (or +plus) as element[1] and the imaginary component is actually |
762
|
|
|
|
|
|
|
# element[2]. We'll change that after we use it. |
763
|
|
|
|
|
|
|
# |
764
|
4
|
100
|
|
|
|
14
|
my $isign = ($c_pair[1] eq "-") ? -1 : 1; # But what was that sign? |
765
|
4
|
|
|
|
|
9
|
$c_pair[2] *= $isign; # Recover correct sign of |
766
|
|
|
|
|
|
|
# imaginary component |
767
|
4
|
|
|
|
|
13
|
@c_pair = @c_pair[0,2]; # Now lose that im sign |
768
|
4
|
|
|
|
|
8
|
$c_pair[0] *= $rsign; # Good time to recall the sign |
769
|
|
|
|
|
|
|
# on the real component |
770
|
4
|
|
|
|
|
13
|
$rval = Math::Complex::cplx(@c_pair); # Turn pair into complex ref |
771
|
4
|
|
|
|
|
189
|
return $rval; # QED |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
# |
774
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
775
|
|
|
|
|
|
|
# Method: degree(): Just return the degree of the Yapp polynomial |
776
|
|
|
|
|
|
|
# Usage: |
777
|
|
|
|
|
|
|
# ivar = jpoly_ref->degree(); |
778
|
|
|
|
|
|
|
sub degree |
779
|
|
|
|
|
|
|
{ |
780
|
1
|
|
|
1
|
1
|
892
|
my $self = shift(@_); |
781
|
1
|
|
|
|
|
5
|
return ($self->{degree}); |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
785
|
|
|
|
|
|
|
# Method: coefficient(): Return the coefficient of the inticated term. |
786
|
|
|
|
|
|
|
# - If the coefficientis a real number, returns the number |
787
|
|
|
|
|
|
|
# - If the coefficient is complex, returns the reference |
788
|
|
|
|
|
|
|
# Usage: |
789
|
|
|
|
|
|
|
# float_var = jpoly_ref->coefficient(n); # Retrieve nth-degree coefficient |
790
|
|
|
|
|
|
|
# complex_var = jpoly_ref->coefficient(n); # Retrieve nth-degree coefficient |
791
|
|
|
|
|
|
|
# |
792
|
|
|
|
|
|
|
# Error condition: If the n is higher than the degree or negative, |
793
|
|
|
|
|
|
|
# |
794
|
|
|
|
|
|
|
sub coefficient |
795
|
|
|
|
|
|
|
{ |
796
|
516
|
|
|
516
|
1
|
1064
|
my $self = shift(@_); |
797
|
516
|
|
|
|
|
573
|
my $term_id = shift(@_); |
798
|
516
|
50
|
33
|
|
|
2472
|
if ( ($term_id > $self->{degree}) || ($term_id < 0) ) |
799
|
|
|
|
|
|
|
{ |
800
|
0
|
|
|
|
|
0
|
my $msg = "Indicated degree is out of range [0, $self->{degree}]"; |
801
|
0
|
|
|
|
|
0
|
croak $msg; |
802
|
|
|
|
|
|
|
} |
803
|
516
|
|
|
|
|
2928
|
return ($self->{coeff}[$term_id]); # Degree desired is valid |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
807
|
|
|
|
|
|
|
# Method: variable(): Retrieve or set the character [string] that will be used |
808
|
|
|
|
|
|
|
# to display the polynomial in Ysprint. |
809
|
|
|
|
|
|
|
# Usage: |
810
|
|
|
|
|
|
|
# - To set the string: |
811
|
|
|
|
|
|
|
# $jpoly_ref->variable("Y"); #(Default is "X") |
812
|
|
|
|
|
|
|
# $jpoly_ref->variable("squeal"); |
813
|
|
|
|
|
|
|
# - To retrieve the string: |
814
|
|
|
|
|
|
|
# $var_str = $jpoly_ref->variable(); |
815
|
|
|
|
|
|
|
# In both cases, the method returns the string. |
816
|
|
|
|
|
|
|
# |
817
|
|
|
|
|
|
|
sub variable |
818
|
|
|
|
|
|
|
{ |
819
|
86
|
|
|
86
|
1
|
110
|
my $self = shift(@_); |
820
|
86
|
|
|
|
|
149
|
my $rval = ""; # Value to return |
821
|
86
|
100
|
|
|
|
1218
|
if (@_ == 0) # No parameter? |
822
|
79
|
|
|
|
|
169
|
{ $rval = $self->{variable}; } # User wants to know it |
823
|
|
|
|
|
|
|
else |
824
|
7
|
|
|
|
|
12
|
{ $rval = shift(@_); # Lop it off parameter list and |
825
|
7
|
|
|
|
|
13
|
$self->{variable} = $rval; # set it as the variable symbol |
826
|
|
|
|
|
|
|
} |
827
|
86
|
|
|
|
|
196
|
return ($rval); |
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
# |
830
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
831
|
|
|
|
|
|
|
# Method: str2yapp(): Accepts a string and converts it to a polynomial object. |
832
|
|
|
|
|
|
|
# The implicit "self" parameter is asssumed to be referenceing a degenerate |
833
|
|
|
|
|
|
|
# polynomial and that's where the member items will go. If you already have an |
834
|
|
|
|
|
|
|
# initialized polynomial object in there, it is *so* gone. |
835
|
|
|
|
|
|
|
# |
836
|
|
|
|
|
|
|
# Parameter: |
837
|
|
|
|
|
|
|
# - A string of terms in the form ^. |
838
|
|
|
|
|
|
|
# separated by white-spaces |
839
|
|
|
|
|
|
|
# Returns: |
840
|
|
|
|
|
|
|
# - The Yapp reference, if successful |
841
|
|
|
|
|
|
|
# - undef if failed anywhere |
842
|
|
|
|
|
|
|
# |
843
|
|
|
|
|
|
|
# Usage: This is not intended to be called from outside. Rather, new() will |
844
|
|
|
|
|
|
|
# call it in the following sequence: |
845
|
|
|
|
|
|
|
# 1. new() creates and blesses the empty Yapp object referenced by $self |
846
|
|
|
|
|
|
|
# 2. $self->str2yapp(The string> |
847
|
|
|
|
|
|
|
# |
848
|
|
|
|
|
|
|
# Note that the exponents do not need to be in any order; the exponent will |
849
|
|
|
|
|
|
|
# decide the position within the array. |
850
|
|
|
|
|
|
|
# |
851
|
|
|
|
|
|
|
# Plan: Parsing presents a problem: I wish to impose no requirement that the |
852
|
|
|
|
|
|
|
# terms and operators be blank-separated. And while the terms are surely |
853
|
|
|
|
|
|
|
# separated by + and -, the split function will remove the separators so I |
854
|
|
|
|
|
|
|
# won't know what was the sign of each term in the polynomial string. However, |
855
|
|
|
|
|
|
|
# for my initial phase, I will require a white-space separator between terms. |
856
|
|
|
|
|
|
|
# Also, each term mus match one of the patterns below, $real_term_pat or |
857
|
|
|
|
|
|
|
# cplx_term_pat. |
858
|
|
|
|
|
|
|
# For real coefficient it must look like: |
859
|
|
|
|
|
|
|
# +/-^ |
860
|
|
|
|
|
|
|
# The sign will be optional for all terms. (But don't tell that to the users.) |
861
|
|
|
|
|
|
|
# |
862
|
|
|
|
|
|
|
# For complex coefficient, it must look |
863
|
|
|
|
|
|
|
# +/-(+i)^ |
864
|
|
|
|
|
|
|
# |
865
|
|
|
|
|
|
|
sub str2yapp |
866
|
|
|
|
|
|
|
{ |
867
|
35
|
|
|
35
|
0
|
49
|
my $self = shift(@_); # Get myself out of the parameter list |
868
|
35
|
|
|
|
|
56
|
my $poly_string = shift(@_); # and get the string into my locale |
869
|
|
|
|
|
|
|
|
870
|
35
|
|
|
|
|
39
|
my $tlc = 0; # Term loop counter |
871
|
|
|
|
|
|
|
|
872
|
35
|
|
|
|
|
58
|
my $rval = undef; # Off to a pessimistic start |
873
|
35
|
|
|
|
|
42
|
my $const_term = 0; |
874
|
|
|
|
|
|
|
|
875
|
35
|
|
|
|
|
47
|
my $cur_term; # Current term from the input polynomial |
876
|
35
|
|
|
|
|
35
|
my $cur_sign = 1; # Assume positive if sign is omitted |
877
|
35
|
|
|
|
|
56
|
my $cur_coeff = 0.0; # Current coefficient |
878
|
35
|
|
|
|
|
50
|
my $cur_varia = ""; # Current variable name; should change only |
879
|
|
|
|
|
|
|
# once - the first time I see it. |
880
|
35
|
|
|
|
|
48
|
my $cur_degree = -1; # For stepping into coeficient array |
881
|
35
|
|
|
|
|
37
|
my $hi_deg = 0; # For determining the highest exponent used |
882
|
|
|
|
|
|
|
|
883
|
35
|
|
|
|
|
61
|
undef($self->{variable}); # Leave it for user to name the variable |
884
|
35
|
|
|
|
|
68
|
$self->{degree} = 0; # and for input to dictate the degree |
885
|
|
|
|
|
|
|
|
886
|
35
|
50
|
|
|
|
77
|
printf("String polynomial is:\n<%s>\n", $poly_string) |
887
|
|
|
|
|
|
|
if ($testmode); |
888
|
35
|
|
|
|
|
150
|
my @pterms = split /\s+/, $poly_string; # Separate the terms |
889
|
|
|
|
|
|
|
# |
890
|
35
|
|
|
|
|
112
|
for ($tlc = 0; $tlc <= $#pterms; $tlc++) |
891
|
|
|
|
|
|
|
{ |
892
|
|
|
|
|
|
|
# Plan: For each term I parse out, determine the coefficient (including the |
893
|
|
|
|
|
|
|
# sign, of course), the variable name and the exponent. This determines the |
894
|
|
|
|
|
|
|
# position and value in the @coeff array |
895
|
|
|
|
|
|
|
# |
896
|
|
|
|
|
|
|
# Afterthought: During debugging I discovererd that a + or - not |
897
|
|
|
|
|
|
|
# connected to the following term would also pass all the pattern |
898
|
|
|
|
|
|
|
# checks. Rather than complain or reject the string, I'd rather be nice |
899
|
|
|
|
|
|
|
# about it. |
900
|
|
|
|
|
|
|
# So here's the scheme: |
901
|
|
|
|
|
|
|
# - If the +/- is the last term of the string, ignore it. |
902
|
|
|
|
|
|
|
# - Not the last: |
903
|
|
|
|
|
|
|
# o If the following term *is* properly signed, threat this solitary |
904
|
|
|
|
|
|
|
# sign like a multiplier for the following term |
905
|
|
|
|
|
|
|
# o If the term following term is unsigned, prepend this solitary sign |
906
|
|
|
|
|
|
|
# to that term. |
907
|
|
|
|
|
|
|
# Either way, skip reat of this round and handle that following term |
908
|
|
|
|
|
|
|
# |
909
|
53
|
100
|
|
|
|
186
|
if ($pterms[$tlc] =~ m/^[-\+]$/) # If it is a + or - alone |
910
|
|
|
|
|
|
|
{ |
911
|
3
|
50
|
|
|
|
10
|
last if ($tlc == $#pterms); # It is already last term: Ignore it |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# Still here: It is not the last term. Peek ahead at next term |
914
|
|
|
|
|
|
|
# |
915
|
3
|
50
|
|
|
|
19
|
if ($pterms[$tlc+1] =~ m/^[-\+]/) # If next term is signed |
916
|
|
|
|
|
|
|
{ # Use this signs as multiplier |
917
|
0
|
0
|
|
|
|
0
|
if ($pterms[$tlc] eq substr($pterms[$tlc+1], 0, 1)) # Signs alike? |
918
|
0
|
|
|
|
|
0
|
{ substr($pterms[$tlc+1], 0, 1) = '+'; } # Next is positive |
919
|
|
|
|
|
|
|
else # Signs not alike: Whichever way, |
920
|
0
|
|
|
|
|
0
|
{ substr($pterms[$tlc+1], 0, 1) = '-'; } # Next is negative |
921
|
0
|
|
|
|
|
0
|
next; # Now handle that next term |
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
else # But if next term is unsigned; |
924
|
|
|
|
|
|
|
{ # use this to make it a signed term |
925
|
3
|
|
|
|
|
10
|
$pterms[$tlc+1] = $pterms[$tlc] . $pterms[$tlc+1]; # Prepend this sign |
926
|
3
|
|
|
|
|
9
|
next; # handle next term |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
} |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# following that afterthought... |
931
|
|
|
|
|
|
|
# Before anything else, check the sign; this makes it possible to specify |
932
|
|
|
|
|
|
|
# the negation of a complex coefficient eg. -(-6+4i) |
933
|
|
|
|
|
|
|
# |
934
|
50
|
|
|
|
|
81
|
$cur_term = $pterms[$tlc]; # Leave array enty intact; disect copy |
935
|
|
|
|
|
|
|
# |
936
|
|
|
|
|
|
|
# Next: Fetch the coefficient, of there is one. |
937
|
|
|
|
|
|
|
# |
938
|
50
|
100
|
|
|
|
999
|
if ($cur_term =~ /($coef_pat)/) # Is there a coefficient? |
939
|
|
|
|
|
|
|
{ |
940
|
45
|
|
|
|
|
104
|
$cur_coeff = $1; # Isolate coefficient for more checking |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
# First, see if there is a sign preceeding the coefficient. |
943
|
|
|
|
|
|
|
# |
944
|
45
|
100
|
|
|
|
266
|
if ($cur_coeff =~ /^($sign_pat)/) # If there is a sign |
945
|
|
|
|
|
|
|
{ |
946
|
15
|
100
|
|
|
|
41
|
$cur_sign = ($1 eq "-") ? -1 : 1; # Apply sign to coefficient later |
947
|
15
|
|
|
|
|
86
|
$cur_coeff =~ s/$sign_pat//; # but for now, lose the sign |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
# Check if the coefficient is a complex number in ofr (a[+-]bi) where |
951
|
|
|
|
|
|
|
# a and b are reals |
952
|
|
|
|
|
|
|
# |
953
|
45
|
100
|
|
|
|
237
|
if ($cur_coeff =~ /$cplx_pat/) # If it looks like a complex number |
954
|
|
|
|
|
|
|
{ |
955
|
4
|
|
|
|
|
10
|
$cur_coeff = str2cplx($cur_coeff); # Convert it to complex object |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
else # Not complex: Must match a real or a sign |
958
|
|
|
|
|
|
|
{ # What if it was just a sign? (Which is gone) |
959
|
41
|
50
|
|
|
|
112
|
$cur_coeff = 1.0 if ($cur_coeff eq ""); # Ths is implicitly 1 |
960
|
|
|
|
|
|
|
} |
961
|
45
|
|
|
|
|
84
|
$cur_coeff *= $cur_sign; # Finally, apply the original sign that |
962
|
|
|
|
|
|
|
# preceded the real or complex coefficient |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
# Now that we have the coefficient for the current term, it's just in |
965
|
|
|
|
|
|
|
# the way. Lose it. |
966
|
|
|
|
|
|
|
# |
967
|
45
|
|
|
|
|
577
|
$cur_term =~ s/$coef_pat//; # Leave the term w/o sign or coefficient. |
968
|
|
|
|
|
|
|
} |
969
|
|
|
|
|
|
|
else # No coefficient? |
970
|
5
|
|
|
|
|
9
|
{ $cur_coeff = 1.0; } # Implicitly, that is 1. (If it had |
971
|
|
|
|
|
|
|
# been -1, it would be -X) |
972
|
|
|
|
|
|
|
# Now that the term has no coefficient (either by input or because it |
973
|
|
|
|
|
|
|
# has been stripped off), let's have a look at the variable component |
974
|
|
|
|
|
|
|
# |
975
|
50
|
100
|
|
|
|
283
|
if ($cur_term =~ /^($varia_pat)/) # If there is a variable to speak of |
976
|
|
|
|
|
|
|
{ |
977
|
15
|
|
|
|
|
34
|
$cur_varia = $1; # then captue that as variable name |
978
|
15
|
|
|
|
|
107
|
$cur_term =~ s/^($varia_pat)//; # and remove it from the term. |
979
|
|
|
|
|
|
|
} |
980
|
|
|
|
|
|
|
else |
981
|
|
|
|
|
|
|
{ |
982
|
35
|
|
|
|
|
47
|
undef($cur_varia); # No variable => constant. Make sure no expo- |
983
|
35
|
|
|
|
|
50
|
$cur_degree = 0; # nent. But we're getting ahead of ourselves.. |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
# |
986
|
|
|
|
|
|
|
# Next, see about the exponent part: ^integer, which is optional |
987
|
|
|
|
|
|
|
# but if provided, there had better be a variable as well. |
988
|
|
|
|
|
|
|
# |
989
|
50
|
100
|
|
|
|
258
|
if ($cur_term =~ /($power_pat)/) # Is there an exponent |
990
|
|
|
|
|
|
|
{ # Some error checking, then work |
991
|
5
|
50
|
|
|
|
13
|
if (defined($cur_varia)) # If there is a variable |
992
|
|
|
|
|
|
|
{ |
993
|
5
|
|
|
|
|
18
|
$cur_term =~ s/^\^//; # Lose the exponent operator |
994
|
5
|
|
|
|
|
25
|
($cur_degree) = $cur_term =~ m/(\d+)/; # Capture the integer part |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
else |
997
|
0
|
|
|
|
|
0
|
{ die "Term[$tlc] <$pterms[$tlc]> has an exponent w/o a variable"; } |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
else # No exponent. Certainly OK.. But.. |
1000
|
|
|
|
|
|
|
{ # Is the degree of the term 1 or 0? Well, is there |
1001
|
45
|
100
|
|
|
|
101
|
$cur_degree = (defined($cur_varia)) ? 1 : 0; # a variable? Yes => 1 |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
# Finished parsing the term. Our state at this point: We have: |
1005
|
|
|
|
|
|
|
# - The coefficient (be it real or # complex), inclduing its sign |
1006
|
|
|
|
|
|
|
# - The variable name, which should not have changed. |
1007
|
|
|
|
|
|
|
# - The degree of the curent term, which may match a term already there. |
1008
|
|
|
|
|
|
|
# |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Degree of polynomial is that of highest degree term, so: |
1011
|
|
|
|
|
|
|
# |
1012
|
50
|
100
|
|
|
|
149
|
$self->{degree} = $cur_degree if ($cur_degree > $self->{degree}); |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
# If variable name has changed in the string, complain but do it anyway. |
1016
|
|
|
|
|
|
|
# |
1017
|
50
|
50
|
100
|
|
|
201
|
if ( (defined($cur_varia)) |
|
|
|
66
|
|
|
|
|
1018
|
|
|
|
|
|
|
&& (defined($self->{variable})) |
1019
|
|
|
|
|
|
|
&& ($self->{variable} ne $cur_varia)) |
1020
|
|
|
|
|
|
|
{ |
1021
|
0
|
|
|
|
|
0
|
my $mg; |
1022
|
0
|
|
|
|
|
0
|
$mg = sprintf("Warning: Changing variable name from <%s> to <%s>\n", |
1023
|
|
|
|
|
|
|
$self->{variable}, $cur_varia); |
1024
|
0
|
|
|
|
|
0
|
warn($mg); # Complain but let it go |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
# Set the variable name for whole polynomial (if not already set in a |
1027
|
|
|
|
|
|
|
# previous round of the loop)... provided that |
1028
|
|
|
|
|
|
|
# |
1029
|
50
|
100
|
|
|
|
109
|
$self->{variable} = $cur_varia |
1030
|
|
|
|
|
|
|
if (defined($cur_varia)); # [Re]set the variable, even with grumble |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
# If we already have a term at this degree, just add the new coefficient |
1033
|
|
|
|
|
|
|
# to the existing one. Otherwise, we are setting the coefficient now. |
1034
|
|
|
|
|
|
|
# |
1035
|
50
|
100
|
|
|
|
120
|
if (defined($self->{coeff}[$cur_degree])) # Already have a term at |
1036
|
|
|
|
|
|
|
{ # degree, then |
1037
|
35
|
|
|
|
|
178
|
$self->{coeff}[$cur_degree] += $cur_coeff; # just add this one |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
else # No term of this degree yet |
1040
|
|
|
|
|
|
|
{ # This is new here |
1041
|
15
|
|
|
|
|
64
|
$self->{coeff}[$cur_degree] = $cur_coeff; # Set the coefficient now |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
} # End of for-loop to parse individual terms |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
# Bug fix (Too lazy to locate screw-up): Somehow I can get this far |
1046
|
|
|
|
|
|
|
# without a variable component. So just in case.. |
1047
|
|
|
|
|
|
|
# |
1048
|
35
|
100
|
|
|
|
548
|
$self->{variable} = "X" if (!defined($self->{variable})); |
1049
|
|
|
|
|
|
|
|
1050
|
35
|
|
|
|
|
101
|
return $self; |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
# |
1053
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1054
|
|
|
|
|
|
|
# Yapp_plus - Function to implement the += operator |
1055
|
|
|
|
|
|
|
# Parameters: |
1056
|
|
|
|
|
|
|
# - (Implicit) [Reference to] my target object |
1057
|
|
|
|
|
|
|
# - The object to added to my target object. This may be: |
1058
|
|
|
|
|
|
|
# o Another Yapp polynomial object [reference] |
1059
|
|
|
|
|
|
|
# o A constant, either real or complex (a different, albeit smaller can |
1060
|
|
|
|
|
|
|
# of worms) |
1061
|
|
|
|
|
|
|
# |
1062
|
|
|
|
|
|
|
sub Yapp_plus |
1063
|
|
|
|
|
|
|
{ |
1064
|
19
|
|
|
19
|
0
|
32
|
my ($self, $added) = @_; # Get relevant parameters. Swap is irrelevant |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
# Now, what is $added? |
1067
|
|
|
|
|
|
|
# |
1068
|
19
|
100
|
66
|
|
|
319
|
if (ref($added) eq $class_name) # If it is another polynomial |
|
|
50
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
{ |
1070
|
17
|
|
|
|
|
22
|
my $alc = 0; # Loop counter to step up added terms |
1071
|
17
|
|
|
|
|
148
|
my $new_degree = $self->{degree}; # Track degree in case the added |
1072
|
|
|
|
|
|
|
# polynomial has a higher degree |
1073
|
17
|
|
|
|
|
24
|
my @builder = @{$self->{coeff}}; # Copy coefficients into temporary list |
|
17
|
|
|
|
|
51
|
|
1074
|
17
|
|
|
|
|
56
|
for ($alc = 0; $alc <= $added->{degree}; $alc++) # For all terms of the |
1075
|
|
|
|
|
|
|
{ # added polynomial |
1076
|
121
|
100
|
|
|
|
481
|
if (defined($builder[$alc])) # If target has term of this degree |
1077
|
108
|
|
|
|
|
200
|
{ $builder[$alc] += $added->{coeff}[$alc]; } # Just add these terms |
1078
|
13
|
|
|
|
|
21
|
else { $builder[$alc] = $added->{coeff}[$alc]; } # Or copy this term |
1079
|
121
|
100
|
|
|
|
408
|
$new_degree = $alc if ($alc > $self->{degree}); # Maintain degree |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
# Temporary areas have been evaluated. Now plug these into target Yapp |
1082
|
|
|
|
|
|
|
# |
1083
|
17
|
|
|
|
|
24
|
$self->{degree} = $new_degree; # Carry over the augmented (?) degree |
1084
|
17
|
|
|
|
|
27
|
@{$self->{coeff}} = @builder; # and the augmented polynomial |
|
17
|
|
|
|
|
271
|
|
1085
|
|
|
|
|
|
|
} # And I have my return value |
1086
|
|
|
|
|
|
|
elsif ( ($added =~ m/^($coef_pat)$/) |
1087
|
|
|
|
|
|
|
|| (ref($added) eq $class_cplx) ) # Adding real or complex const |
1088
|
|
|
|
|
|
|
{ |
1089
|
|
|
|
|
|
|
# As above: If the target term is defined, add the new term. |
1090
|
|
|
|
|
|
|
# Otherwise, set the target term to the given value |
1091
|
|
|
|
|
|
|
# |
1092
|
2
|
50
|
|
|
|
168
|
if (defined($self->{coeff}[0])) {$self->{coeff}[0] += $added;} |
|
2
|
|
|
|
|
7
|
|
|
0
|
|
|
|
|
0
|
|
1093
|
|
|
|
|
|
|
else {$self->{coeff}[0] = $added;} |
1094
|
|
|
|
|
|
|
} # (Just needed to augment the constant term) |
1095
|
|
|
|
|
|
|
else |
1096
|
0
|
|
|
|
|
0
|
{ die "Operator += requires a constant or a Yapp polynomial reference";} |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
# Didn't die - I have a good value to return |
1099
|
|
|
|
|
|
|
# |
1100
|
19
|
|
|
|
|
300
|
return ($self); # (Ought to return something) |
1101
|
|
|
|
|
|
|
} |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1104
|
|
|
|
|
|
|
# Yapp_add() - Function (and overloaded + operator) to add two polynomials. |
1105
|
|
|
|
|
|
|
# Parameters: |
1106
|
|
|
|
|
|
|
# - (Implicit) The polynomial on the left side of the + (self) |
1107
|
|
|
|
|
|
|
# - The polynomial to be added. This may be: |
1108
|
|
|
|
|
|
|
# o (Implicit) [A reference to] another Yapp polynomial object |
1109
|
|
|
|
|
|
|
# o A constant, either real or complex (See Yapp_plus) |
1110
|
|
|
|
|
|
|
# - (Implicit) The "swapped" flag, largely irrelevant for the + operator |
1111
|
|
|
|
|
|
|
# Returns: |
1112
|
|
|
|
|
|
|
# A reference to a new Yapp polynomial |
1113
|
|
|
|
|
|
|
# |
1114
|
|
|
|
|
|
|
sub Yapp_add |
1115
|
|
|
|
|
|
|
{ |
1116
|
7
|
|
|
7
|
0
|
173
|
my ($self, $adder, $swapped) = @_; #(swap is irrelelvant for add) |
1117
|
|
|
|
|
|
|
|
1118
|
7
|
|
|
|
|
17
|
my $ryapp = Yapp($self); # Clone the original |
1119
|
7
|
|
|
|
|
23
|
$ryapp += $adder; # Let _plus() function handle details |
1120
|
7
|
|
|
|
|
21
|
return $ryapp; # Assume $ryapp will be auto-destroyed? |
1121
|
|
|
|
|
|
|
} |
1122
|
|
|
|
|
|
|
# |
1123
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1124
|
|
|
|
|
|
|
# Yapp_minus(): Function (and overloaded -= operator) to subtract the passed |
1125
|
|
|
|
|
|
|
# polybnomial from the object polynomial in place. That is, |
1126
|
|
|
|
|
|
|
# it modifies the $self object |
1127
|
|
|
|
|
|
|
# - (Implicit) [Reference to] my target object |
1128
|
|
|
|
|
|
|
# - The object to added to my target object. This may be: |
1129
|
|
|
|
|
|
|
# o Another Yapp polynomial object [reference] |
1130
|
|
|
|
|
|
|
# o A constant, either real or complex |
1131
|
|
|
|
|
|
|
# Returns: |
1132
|
|
|
|
|
|
|
# - The same reference. But the target Yapp ahs been "deminished" |
1133
|
|
|
|
|
|
|
# |
1134
|
|
|
|
|
|
|
sub Yapp_minus |
1135
|
|
|
|
|
|
|
{ |
1136
|
7
|
|
|
7
|
0
|
19
|
my ($self, $subtractor) = @_[0 .. 1]; |
1137
|
|
|
|
|
|
|
|
1138
|
7
|
100
|
|
|
|
20
|
if (ref($subtractor) eq $class_name) # Subtracting another polynimal |
1139
|
|
|
|
|
|
|
{ # just use the add method |
1140
|
1
|
|
|
|
|
6
|
my $temp_subt = $subtractor->Yapp_negate(); # Quickie way out: Negate and |
1141
|
|
|
|
|
|
|
# add, However I cant |
1142
|
1
|
|
|
|
|
3
|
my $temp_self = Yapp($self); # use -= on $sef. Dunno why |
1143
|
1
|
|
|
|
|
4
|
$temp_self += $temp_subt; # Add the negated Yapp |
1144
|
1
|
|
|
|
|
2
|
@{$self->{coeff}} = @{$temp_self->{coeff}}; # Restore subtracted |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
1145
|
|
|
|
|
|
|
# coeffiecient array |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
else # Otherwise, just assume a constant |
1148
|
|
|
|
|
|
|
{ # be it real or complex. |
1149
|
|
|
|
|
|
|
# If our polynomial has no constant term, this is it, after negation. |
1150
|
|
|
|
|
|
|
# |
1151
|
6
|
50
|
|
|
|
32
|
$self->{coeff}[0] = (defined($self->{coeff}[0])) |
1152
|
|
|
|
|
|
|
? $self->{coeff}[0] - $subtractor |
1153
|
|
|
|
|
|
|
: - $subtractor ; |
1154
|
|
|
|
|
|
|
} |
1155
|
7
|
|
|
|
|
183
|
return $self; |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1159
|
|
|
|
|
|
|
# Yapp_sub(): Function (and overloaded - operator) to subtract one polynomia |
1160
|
|
|
|
|
|
|
# from another bot not in place. |
1161
|
|
|
|
|
|
|
# Parameters: |
1162
|
|
|
|
|
|
|
# - (Implicit) The polynomial on the left side of the - (self) |
1163
|
|
|
|
|
|
|
# - The polynomial to be added. This may be: |
1164
|
|
|
|
|
|
|
# o (Implicit) [A reference to] another Yapp polynomial object |
1165
|
|
|
|
|
|
|
# o A constant, either real or complex (See Yapp_plus) |
1166
|
|
|
|
|
|
|
# - (Implicit) The "swapped" flag |
1167
|
|
|
|
|
|
|
# Returns: |
1168
|
|
|
|
|
|
|
# - A reference to a new Yapp polynomial |
1169
|
|
|
|
|
|
|
# |
1170
|
|
|
|
|
|
|
sub Yapp_sub |
1171
|
|
|
|
|
|
|
{ |
1172
|
7
|
|
|
7
|
0
|
15
|
my ($self, $subtractor, $swapped) = @_; |
1173
|
|
|
|
|
|
|
|
1174
|
7
|
|
|
|
|
18
|
my $ryapp = Yapp($self); # Clone the original |
1175
|
7
|
|
|
|
|
24
|
$ryapp -= $subtractor; # Let _plus() function handle details |
1176
|
7
|
100
|
|
|
|
31
|
$ryapp = $ryapp->Yapp_negate() if ($swapped); # Negate of $self was on |
1177
|
|
|
|
|
|
|
# right of the - sign |
1178
|
7
|
|
|
|
|
26
|
return $ryapp; # Assume $ryapp will be auto-destroyed? |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
# |
1181
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1182
|
|
|
|
|
|
|
# Yapp_mult(): Function to implement the overloaded *= operator: Perform in- |
1183
|
|
|
|
|
|
|
# place multiplication of the target Yapp my a constant or another Yapp |
1184
|
|
|
|
|
|
|
# Parameters: |
1185
|
|
|
|
|
|
|
# - (Implicit) The target object ($self) |
1186
|
|
|
|
|
|
|
# - The multiplier. This may be: |
1187
|
|
|
|
|
|
|
# o Another Yapp object (Happiest with this parameter type) |
1188
|
|
|
|
|
|
|
# o A real or complex constant |
1189
|
|
|
|
|
|
|
# Returns: |
1190
|
|
|
|
|
|
|
# The $self-same reference, but it has been "mutated" by the method. |
1191
|
|
|
|
|
|
|
# |
1192
|
|
|
|
|
|
|
sub Yapp_mult |
1193
|
|
|
|
|
|
|
{ |
1194
|
155
|
|
|
155
|
0
|
230
|
my ($self, $multiplier) = @_; |
1195
|
155
|
|
|
|
|
178
|
my $aux_yapp; # I may have to create another Yapp |
1196
|
|
|
|
|
|
|
my $ylc; # Loop counter for stepping up coefficients |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
# Now what data type is the multiplier? |
1199
|
|
|
|
|
|
|
# |
1200
|
|
|
|
|
|
|
#Xif ( (ref($multiplier) eq "$class_cplx") # Complex number constant |
1201
|
|
|
|
|
|
|
#X || ( (ref(\$multiplier) eq "SCALAR") # or a scalar that happens |
1202
|
|
|
|
|
|
|
#X && ($multiplier =~ /^($coef_pat)$/))) # to be a real constant |
1203
|
|
|
|
|
|
|
#X Note: The above logic is good but for very low floating point numbers it |
1204
|
|
|
|
|
|
|
#X was failing to match the corefficient pattern. Hence, I relaxed the |
1205
|
|
|
|
|
|
|
#X matching requirement. -- Jacob |
1206
|
|
|
|
|
|
|
|
1207
|
155
|
100
|
100
|
|
|
958
|
if ( (ref($multiplier) eq "$class_cplx") # Complex number constant |
|
|
50
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
|| (ref(\$multiplier) eq "SCALAR" ) ) # or a scalar (Assume numeric) |
1209
|
|
|
|
|
|
|
{ # Just distribute multiplier |
1210
|
56
|
|
|
|
|
174
|
for ($ylc = 0; $ylc <= $self->{degree}; $ylc++) |
1211
|
|
|
|
|
|
|
{ |
1212
|
307
|
100
|
|
|
|
1517
|
$self->{coeff}[$ylc] *= $multiplier |
1213
|
|
|
|
|
|
|
if ($self->{coeff}[$ylc] != 0) ; # Don't bother multiplying 0 |
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
elsif (ref($multiplier) eq $class_name) # Multiplying by a polynomial |
1217
|
|
|
|
|
|
|
{ |
1218
|
99
|
|
|
|
|
150
|
my @builder = (); # Build result area from scratch |
1219
|
99
|
|
|
|
|
377
|
my $new_degree = $self->{degree} |
1220
|
|
|
|
|
|
|
+ $multiplier->{degree}; # (Remember your 9th grade algebra?) |
1221
|
99
|
|
|
|
|
248
|
for ($ylc = 0; $ylc <= $self->{degree}; $ylc++) |
1222
|
|
|
|
|
|
|
{ # Outer loop multiplies one target term |
1223
|
306
|
|
|
|
|
6042
|
for (my $mlc = 0; $mlc <= $multiplier->{degree}; $mlc++) |
1224
|
|
|
|
|
|
|
{ # Inner loop multiplies by one |
1225
|
|
|
|
|
|
|
# multiplier term |
1226
|
805
|
|
|
|
|
7616
|
my $term_degree = $ylc + $mlc; # Degree of this target term |
1227
|
805
|
100
|
|
|
|
1969
|
$builder[$term_degree] = 0.0 # Make sure there is a |
1228
|
|
|
|
|
|
|
if (! defined($builder[$term_degree])); # value in here to start |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
# Accumulate product term of that degree: Product of the terms whose |
1231
|
|
|
|
|
|
|
# exponents add up to the [eventual] exponent of this term |
1232
|
|
|
|
|
|
|
# |
1233
|
805
|
|
|
|
|
3023
|
$builder[$term_degree] += $self->{coeff}[$ylc] |
1234
|
|
|
|
|
|
|
* $multiplier->{coeff}[$mlc] ; |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
} # End loop multiplying one target term by term from multiplier |
1237
|
|
|
|
|
|
|
} # End loop multiplying whole polynomials |
1238
|
|
|
|
|
|
|
# Done multiplication: Now put it back in place |
1239
|
|
|
|
|
|
|
# |
1240
|
99
|
|
|
|
|
777
|
$self->{degree} = $new_degree; # Copy back degree of multiplied target Yapp |
1241
|
99
|
|
|
|
|
132
|
@{$self->{coeff}} = @builder; # and copy back array where we carried |
|
99
|
|
|
|
|
461
|
|
1242
|
|
|
|
|
|
|
# out the multiplication. |
1243
|
|
|
|
|
|
|
} # All done multiplying Yapps; product is in place |
1244
|
|
|
|
|
|
|
else # No more permitted possibilities |
1245
|
0
|
|
|
|
|
0
|
{ die "Operator *= requires a constant or a Yapp polynomial reference";} |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
# Afterthought: I have found that when I multiply twy poly's with conjugate |
1248
|
|
|
|
|
|
|
# complex constant terms, the result will include coefficients like like |
1249
|
|
|
|
|
|
|
# (30.00+0.00i); which is a real, of course, but doesn't look that way. |
1250
|
|
|
|
|
|
|
# Here's the fix: |
1251
|
|
|
|
|
|
|
# |
1252
|
155
|
|
|
|
|
275
|
realify(\@{$self->{coeff}}); |
|
155
|
|
|
|
|
574
|
|
1253
|
155
|
|
|
|
|
797
|
return $self; |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
# |
1256
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1257
|
|
|
|
|
|
|
# Yapp_times(): Method to implement the overloaded '*' operator |
1258
|
|
|
|
|
|
|
# Parameters: |
1259
|
|
|
|
|
|
|
# - (Implicit) The operand (usually the left) to the multiplicaton |
1260
|
|
|
|
|
|
|
# - [Reference] to the multiplier, which may be: |
1261
|
|
|
|
|
|
|
# o Another Yapp object (Happiest with this parameter type) |
1262
|
|
|
|
|
|
|
# o A real or complex constant |
1263
|
|
|
|
|
|
|
# - Swapped-operands flag, largely irrelevant for multiplication |
1264
|
|
|
|
|
|
|
# Returns: |
1265
|
|
|
|
|
|
|
# - [Reference to] a new Yapp polynomial that is the product of the first |
1266
|
|
|
|
|
|
|
# two parameters |
1267
|
|
|
|
|
|
|
# |
1268
|
|
|
|
|
|
|
sub Yapp_times |
1269
|
|
|
|
|
|
|
{ |
1270
|
61
|
|
|
61
|
0
|
121
|
my ($self, $multiplier, $swapped) = @_; |
1271
|
61
|
|
|
|
|
70
|
my $ryapp; # The object to be returned |
1272
|
|
|
|
|
|
|
|
1273
|
61
|
|
|
|
|
310
|
$ryapp = Yapp($self); # Make a copy |
1274
|
61
|
|
|
|
|
331
|
$ryapp *= $multiplier; # Carry out operation |
1275
|
61
|
|
|
|
|
198
|
return $ryapp; #(Wasnt't that simple?) |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
# |
1278
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1279
|
|
|
|
|
|
|
# Yapp_power(): Method to implement exponentiation of polynomial by an |
1280
|
|
|
|
|
|
|
# integer power. |
1281
|
|
|
|
|
|
|
# Parameters: |
1282
|
|
|
|
|
|
|
# - (Implicit) The operand, a ref to the polynomial being multiplied by itself |
1283
|
|
|
|
|
|
|
# - The power, which must be an integer |
1284
|
|
|
|
|
|
|
# - Swapped-operands flag: grounds for immediate death |
1285
|
|
|
|
|
|
|
# Returns: |
1286
|
|
|
|
|
|
|
# - A [ref to a] Yapp polynomial that is the original raised to the |
1287
|
|
|
|
|
|
|
# indicated powert |
1288
|
|
|
|
|
|
|
# |
1289
|
|
|
|
|
|
|
sub Yapp_power |
1290
|
|
|
|
|
|
|
{ |
1291
|
7
|
|
|
7
|
0
|
1186
|
my ($self, $power, $swapped) = @_; |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
# Some validations: |
1294
|
|
|
|
|
|
|
# |
1295
|
7
|
100
|
|
|
|
28
|
die "You cannot raise a number to a polynomial power" |
1296
|
|
|
|
|
|
|
if ($swapped); |
1297
|
6
|
100
|
|
|
|
32
|
die "Power must be an integer" |
1298
|
|
|
|
|
|
|
if ($power != int($power)); |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
# And now that that's been squared away: Get to work |
1301
|
|
|
|
|
|
|
# |
1302
|
5
|
|
|
|
|
11
|
my $ryapp = Yapp(1); # Start with a unit polynomial |
1303
|
5
|
|
|
|
|
15
|
while ($power-- > 0) # (Decrement after testing) |
1304
|
|
|
|
|
|
|
{ |
1305
|
11
|
|
|
|
|
23
|
$ryapp *= $self; # Carry out the self-multiplication |
1306
|
|
|
|
|
|
|
} |
1307
|
5
|
|
|
|
|
13
|
return $ryapp; |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1310
|
|
|
|
|
|
|
# Yapp_divide(): Method to implement the overloaded '/=' operator. Unlike |
1311
|
|
|
|
|
|
|
# the multiply operator, I am allowing only for deviding by a |
1312
|
|
|
|
|
|
|
# constant value. Scheme is simple: Take reciprocal of divisor |
1313
|
|
|
|
|
|
|
# and multiply the Yapp by that. |
1314
|
|
|
|
|
|
|
# Parameters: |
1315
|
|
|
|
|
|
|
# - (Implicit) the Yapp object itself |
1316
|
|
|
|
|
|
|
# - The divisor, which may be real or complex |
1317
|
|
|
|
|
|
|
# Returns: |
1318
|
|
|
|
|
|
|
# - The original Yapp reference |
1319
|
|
|
|
|
|
|
# |
1320
|
|
|
|
|
|
|
sub Yapp_divide |
1321
|
|
|
|
|
|
|
{ |
1322
|
20
|
|
|
20
|
0
|
31
|
my ($self, $divisor) = @_; |
1323
|
20
|
|
|
|
|
40
|
my $recipro = (1.0 / $divisor); # Take reciprocal |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
#$self *= $recipro; # (This causes "No Method" error, so..) |
1326
|
20
|
|
|
|
|
101
|
$self = $self * $recipro; # Carry out that multiplication |
1327
|
20
|
|
|
|
|
160
|
return $self; |
1328
|
|
|
|
|
|
|
} |
1329
|
|
|
|
|
|
|
# |
1330
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1331
|
|
|
|
|
|
|
# Yapp_dividedby(): Method to implement the overloaded '/' operator. |
1332
|
|
|
|
|
|
|
# Scheme: Identical to the Yapp_times() method. |
1333
|
|
|
|
|
|
|
# |
1334
|
|
|
|
|
|
|
# Parameters: |
1335
|
|
|
|
|
|
|
# - (Implicit) the Yapp object itself |
1336
|
|
|
|
|
|
|
# - The divisor, which may be real or complex |
1337
|
|
|
|
|
|
|
# Returns: |
1338
|
|
|
|
|
|
|
# - A reference to a new Yapp object wherin the division has been carried # out |
1339
|
|
|
|
|
|
|
# |
1340
|
|
|
|
|
|
|
sub Yapp_dividedby |
1341
|
|
|
|
|
|
|
{ |
1342
|
9
|
|
|
9
|
0
|
78
|
my ($self, $multiplier, $swapped) = @_; # $swapped is irrelevant |
1343
|
9
|
|
|
|
|
12
|
my $ryapp; # The object to be returned |
1344
|
|
|
|
|
|
|
|
1345
|
9
|
|
|
|
|
21
|
$ryapp = Yapp($self); # Make a copy |
1346
|
9
|
|
|
|
|
27
|
$ryapp /= $multiplier; # Carry out operation |
1347
|
9
|
|
|
|
|
41
|
return $ryapp; #(Wasnt't that simple?) |
1348
|
|
|
|
|
|
|
} |
1349
|
|
|
|
|
|
|
# |
1350
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1351
|
|
|
|
|
|
|
# Negation and conjagation of the coefficients of the polynomial: |
1352
|
|
|
|
|
|
|
# |
1353
|
|
|
|
|
|
|
# Yapp_negate(): Method to handle the overloaded ! operator, to give a Yapp |
1354
|
|
|
|
|
|
|
# whose coefficientes are the negatives of the given Yapp. |
1355
|
|
|
|
|
|
|
# Parameter: (Implicit) |
1356
|
|
|
|
|
|
|
# - A Yapp reference |
1357
|
|
|
|
|
|
|
# Returns: |
1358
|
|
|
|
|
|
|
# - A new one with the negated coefficcients |
1359
|
|
|
|
|
|
|
# |
1360
|
|
|
|
|
|
|
sub Yapp_negate |
1361
|
|
|
|
|
|
|
{ |
1362
|
7
|
|
|
7
|
0
|
12
|
my $self = shift(@_); |
1363
|
7
|
|
|
|
|
17
|
my $ryapp = Yapp($self); # Make a copy |
1364
|
|
|
|
|
|
|
|
1365
|
7
|
|
|
|
|
24
|
$ryapp *= -1; # Multiply by -1 |
1366
|
7
|
|
|
|
|
19
|
return $ryapp; |
1367
|
|
|
|
|
|
|
} |
1368
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
1369
|
|
|
|
|
|
|
# Yapp_conj(): Method to handle the ~ operator; Conjagate the complex |
1370
|
|
|
|
|
|
|
# coefficents of the given yapp |
1371
|
|
|
|
|
|
|
# Parameter: (Implicit) |
1372
|
|
|
|
|
|
|
# - A Yapp reference |
1373
|
|
|
|
|
|
|
# Returns: |
1374
|
|
|
|
|
|
|
# - A new one with the conjugate coefficcients |
1375
|
|
|
|
|
|
|
# |
1376
|
|
|
|
|
|
|
sub Yapp_conj |
1377
|
|
|
|
|
|
|
{ |
1378
|
1
|
|
|
1
|
0
|
2
|
my $self = shift(@_); |
1379
|
1
|
|
|
|
|
3
|
my $ryapp = Yapp($self); # Make a copy |
1380
|
1
|
|
|
|
|
2
|
my $clc; # Loop counter |
1381
|
|
|
|
|
|
|
|
1382
|
1
|
|
|
|
|
6
|
for ($clc = 0; $clc <= $self->{degree}; $clc++) |
1383
|
|
|
|
|
|
|
{ |
1384
|
7
|
100
|
|
|
|
300
|
next unless (ref($self->{coeff}[$clc]) eq $class_cplx); # Skip real coeffs |
1385
|
6
|
|
|
|
|
84
|
$self->{coeff}[$clc] = ~ $self->{coeff}[$clc]; |
1386
|
|
|
|
|
|
|
} |
1387
|
1
|
|
|
|
|
42
|
return $ryapp; |
1388
|
|
|
|
|
|
|
} |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
# |
1391
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1392
|
|
|
|
|
|
|
# Evaluation: Plug a value into the polynimal and calculate the result |
1393
|
|
|
|
|
|
|
# This is a simple application of Horner's "Synthetic Division" |
1394
|
|
|
|
|
|
|
# |
1395
|
|
|
|
|
|
|
# Parameters: |
1396
|
|
|
|
|
|
|
# - (Implicit) Reference to the Yapp polynomial |
1397
|
|
|
|
|
|
|
# - A real number or reference to complex number |
1398
|
|
|
|
|
|
|
# Returns: |
1399
|
|
|
|
|
|
|
# - The result of the plugging it in. |
1400
|
|
|
|
|
|
|
# - Optionally, if caller specified ($value, $quotient) = $some_yapp(number) |
1401
|
|
|
|
|
|
|
# also returns [a refernce to] the quotient polynomial |
1402
|
|
|
|
|
|
|
# |
1403
|
|
|
|
|
|
|
sub Yapp_eval |
1404
|
|
|
|
|
|
|
{ |
1405
|
117
|
|
|
117
|
0
|
7589
|
my ($self, $in_val) = @_; # (Assuming correct parameters or Phzzzt!) |
1406
|
117
|
|
|
|
|
193
|
my $elc; # Exponent loop counter for countdown |
1407
|
117
|
|
|
|
|
142
|
my $addval = 0.0; # Intermediate value in synthetic division step |
1408
|
117
|
|
|
|
|
137
|
my $sumval = 0.0; # A term in the "quotient" line |
1409
|
117
|
|
|
|
|
176
|
my @quotient = ();# Array representing the quotient sans remainder |
1410
|
|
|
|
|
|
|
|
1411
|
117
|
|
|
|
|
343
|
for ($elc = $self->{degree}; $elc >= 0; $elc--) |
1412
|
|
|
|
|
|
|
{ |
1413
|
703
|
|
|
|
|
47745
|
$quotient[$elc] = $self->{coeff}[$elc] + $addval; # A term in quotient |
1414
|
703
|
|
|
|
|
39298
|
$addval = $quotient[$elc] * $in_val; # Add this in next round |
1415
|
|
|
|
|
|
|
} |
1416
|
|
|
|
|
|
|
# Last term of the quotient is the evaluation of the polynomial at that |
1417
|
|
|
|
|
|
|
# input value. The rest is the quotient polynomial, albeit looking like a |
1418
|
|
|
|
|
|
|
# degree too high. Use shift to solve both issues: |
1419
|
|
|
|
|
|
|
# |
1420
|
117
|
|
|
|
|
4297
|
my $result = shift(@quotient); # Get value and reduce degree |
1421
|
117
|
|
|
|
|
266
|
realify($result); # In case of rounding error, if result |
1422
|
|
|
|
|
|
|
# is near zero, make it zero |
1423
|
117
|
100
|
|
|
|
788
|
if (wantarray()) # Did caller specify two return values? |
1424
|
107
|
|
|
|
|
678
|
{ |
1425
|
10
|
|
|
|
|
28
|
my $y_quotient = Yapp(\@quotient); # Turn the quotient array into poly |
1426
|
10
|
|
|
|
|
59
|
return($result, $y_quotient); # and return both to caller |
1427
|
|
|
|
|
|
|
} |
1428
|
|
|
|
|
|
|
else {return $result;} # Otherwise, we happy with result alone |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
# for ($elc = $self->{degree}; $elc >= 0; $elc--) |
1431
|
|
|
|
|
|
|
# { |
1432
|
|
|
|
|
|
|
# $sumval = $self->{coeff}[$elc] + $addval; |
1433
|
|
|
|
|
|
|
# $addval = $in_val * $sumval; # Add this to the next coefficient |
1434
|
|
|
|
|
|
|
# } |
1435
|
|
|
|
|
|
|
# return $sumval; #(OK last addition was wasted) |
1436
|
|
|
|
|
|
|
} |
1437
|
|
|
|
|
|
|
# |
1438
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1439
|
|
|
|
|
|
|
# Yapp_reduce: Reduce the roots of a polynomial by the indicated amount. |
1440
|
|
|
|
|
|
|
# This is part of Horner's method for approximating the roots but will not |
1441
|
|
|
|
|
|
|
# be used here for that purpose. |
1442
|
|
|
|
|
|
|
# |
1443
|
|
|
|
|
|
|
# Parameters: |
1444
|
|
|
|
|
|
|
# - (Implicit) Reference to the Yapp polynomial |
1445
|
|
|
|
|
|
|
# - Value by which to reduce the roots. May be real or [reference to] |
1446
|
|
|
|
|
|
|
# complex |
1447
|
|
|
|
|
|
|
# Reurns: |
1448
|
|
|
|
|
|
|
# - A [ref to a] new Yapp object, whose roots are the same as those of the |
1449
|
|
|
|
|
|
|
# original but reduced by the indicated amount, |
1450
|
|
|
|
|
|
|
# |
1451
|
|
|
|
|
|
|
# Scheme: This will use an array of successive arrays representing |
1452
|
|
|
|
|
|
|
# intermdiate quitient polynomials, as well as an array or remainders to |
1453
|
|
|
|
|
|
|
# build the final result |
1454
|
|
|
|
|
|
|
# |
1455
|
|
|
|
|
|
|
sub Yapp_reduce |
1456
|
|
|
|
|
|
|
{ |
1457
|
8
|
|
|
8
|
0
|
20
|
my ($self, $reduce_by) = @_; # Get my parameters |
1458
|
8
|
|
|
|
|
21
|
my @remainders; |
1459
|
|
|
|
|
|
|
my @degrees; # Make it easier to track the degree of each quotient |
1460
|
0
|
|
|
|
|
0
|
my ($qlc, $tlc); # Loop counters for outer and inner loops |
1461
|
8
|
|
|
|
|
15
|
my $q_count = $self->{degree}; # How many quotients I will calculate |
1462
|
8
|
|
|
|
|
17
|
my $lead_coeff = $self->{coeff}[$q_count]; # Will divide by this and |
1463
|
|
|
|
|
|
|
# multiply it back later |
1464
|
8
|
|
|
|
|
17
|
my @rlist = (); # Result list, the raw polynomial we build up |
1465
|
8
|
|
|
|
|
12
|
my $rcount = 0; # Counter to make it easier to keep track to that build |
1466
|
8
|
|
|
|
|
10
|
my @coeffs = @{$self->{coeff}}; # Will start with synthetic |
|
8
|
|
|
|
|
25
|
|
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
# Divide whole new "poly" by leading coefficient that that new leading |
1469
|
|
|
|
|
|
|
# coefficient is 1. |
1470
|
|
|
|
|
|
|
# |
1471
|
8
|
|
|
|
|
24
|
for (my $clc=0; $clc <= $q_count; $clc++) {$coeffs[$clc] /= $lead_coeff;} |
|
36
|
|
|
|
|
79
|
|
1472
|
|
|
|
|
|
|
|
1473
|
8
|
|
|
|
|
33
|
for ($qlc = $self->{degree}; $qlc > 0; $qlc--) |
1474
|
|
|
|
|
|
|
{ |
1475
|
28
|
|
|
|
|
40
|
my $addval = 0.0; # Set up for synthetic division |
1476
|
28
|
|
|
|
|
36
|
my $q_deg = $qlc - 1; # Degree of new quotient |
1477
|
28
|
|
|
|
|
31
|
my @quotient; # Quotient of a synthetic division operation |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
# Note: This inner loop uses the same synthetic division used in function |
1480
|
|
|
|
|
|
|
# Yapp_eval(). But the moving expression $quotient[$tlc] stands in place |
1481
|
|
|
|
|
|
|
# of $sumval |
1482
|
|
|
|
|
|
|
# |
1483
|
28
|
|
|
|
|
66
|
for ($tlc = $qlc; $tlc >= 0; $tlc--) |
1484
|
|
|
|
|
|
|
{ |
1485
|
|
|
|
|
|
|
# First add $addval to a term to determine next coefficient. |
1486
|
|
|
|
|
|
|
# Then determine next $addval |
1487
|
|
|
|
|
|
|
# |
1488
|
92
|
|
|
|
|
140
|
$quotient[$tlc] = $coeffs[$tlc] + $addval; |
1489
|
92
|
|
|
|
|
378
|
$addval = $reduce_by * $quotient[$tlc]; |
1490
|
|
|
|
|
|
|
} |
1491
|
|
|
|
|
|
|
# When I come out of above inner loop, two items of interest: |
1492
|
|
|
|
|
|
|
# - The last value in the quotient array, which is the remainder of the |
1493
|
|
|
|
|
|
|
# synthetic division operation, is the next coefficient in the final |
1494
|
|
|
|
|
|
|
# polynomial. |
1495
|
|
|
|
|
|
|
# - The quotient array represents the next polynomial to be "divided" by |
1496
|
|
|
|
|
|
|
# the divisor, once I get rid of the remainder term. |
1497
|
|
|
|
|
|
|
# |
1498
|
28
|
|
|
|
|
47
|
$rlist[$rcount++] = shift(@quotient); # Pull remainder into the polynomial |
1499
|
|
|
|
|
|
|
# I am building up, an drop it out |
1500
|
|
|
|
|
|
|
# of the next polynomial. |
1501
|
28
|
|
|
|
|
52
|
@coeffs = @quotient; # Will next divide *this* polynomial |
1502
|
28
|
|
|
|
|
79
|
@quotient = (); # Neatness: Clear for next round |
1503
|
|
|
|
|
|
|
} |
1504
|
8
|
|
|
|
|
15
|
$rlist[$q_count] = 1; # The inevitable final quotient |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
# (Whew) I now have our list or remainders from the above sequence of |
1507
|
|
|
|
|
|
|
# synthetic divisions. Create a polynomial out of that list |
1508
|
|
|
|
|
|
|
# |
1509
|
8
|
|
|
|
|
32
|
my $ryapp = Yapp(\@rlist); # This creates the polynomial |
1510
|
8
|
|
|
|
|
22
|
$ryapp *= $lead_coeff; # Remember that? Multiply it back out |
1511
|
8
|
|
|
|
|
26
|
return $ryapp; # This has the roots reduced. |
1512
|
|
|
|
|
|
|
} |
1513
|
|
|
|
|
|
|
# |
1514
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1515
|
|
|
|
|
|
|
# Yapp_negate_roots(): Returns a polynomial whose roots are the nagatives of the |
1516
|
|
|
|
|
|
|
# roots of the given polynomial. Easy trick: Negate the |
1517
|
|
|
|
|
|
|
# coefficient of every odd-exponent term. |
1518
|
|
|
|
|
|
|
# |
1519
|
|
|
|
|
|
|
# Parameter: |
1520
|
|
|
|
|
|
|
# - (Implicit) The Yapp reference |
1521
|
|
|
|
|
|
|
# Returns: |
1522
|
|
|
|
|
|
|
# - A new Yapp with negated roots |
1523
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1524
|
|
|
|
|
|
|
# |
1525
|
|
|
|
|
|
|
sub Yapp_negate_roots |
1526
|
|
|
|
|
|
|
{ |
1527
|
1
|
|
|
1
|
0
|
5
|
my $self = shift(@_); # The only parameter I care about |
1528
|
1
|
|
|
|
|
3
|
my $ryapp = Yapp($self); # Copy to another polynomial |
1529
|
1
|
|
|
|
|
2
|
my $coefref = $ryapp->{coeff}; |
1530
|
1
|
|
|
|
|
3
|
for (my $clc = 1; $clc <=$#{$coefref}; $clc+=2) # Odd indexed terms only |
|
4
|
|
|
|
|
11
|
|
1531
|
|
|
|
|
|
|
{ |
1532
|
3
|
|
|
|
|
7
|
$coefref->[$clc] = - ($coefref->[$clc]) #(If it was 0, no harm done) |
1533
|
|
|
|
|
|
|
} |
1534
|
1
|
|
|
|
|
4
|
return $ryapp |
1535
|
|
|
|
|
|
|
} |
1536
|
|
|
|
|
|
|
# |
1537
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1538
|
|
|
|
|
|
|
# Derivative and antiderivative i.e. indefinite integral. |
1539
|
|
|
|
|
|
|
# In both cases, we will store the transformed polybnomial back into the |
1540
|
|
|
|
|
|
|
# Yapp structure because they will very likely be resused for other stuff. |
1541
|
|
|
|
|
|
|
# For example, Newton's method for finding roots uses the first derivative, |
1542
|
|
|
|
|
|
|
# and laGuerre's method uses the second derivative. |
1543
|
|
|
|
|
|
|
# |
1544
|
|
|
|
|
|
|
# Yapp_derivative() |
1545
|
|
|
|
|
|
|
# Parameters: |
1546
|
|
|
|
|
|
|
# - (Implicit) Reference to the Yapp to differentiated |
1547
|
|
|
|
|
|
|
# - Order of derivative. If omitted, assume caller meant first derivative |
1548
|
|
|
|
|
|
|
# Returns: |
1549
|
|
|
|
|
|
|
# - A [reference to a] Yapp polynomial that is the indicated derivative of |
1550
|
|
|
|
|
|
|
# the given Yapp. |
1551
|
|
|
|
|
|
|
# - Side effect: A new array of Yapp references to all the derivatives is |
1552
|
|
|
|
|
|
|
# now hanging off the given Yapp structure. @{derivatives} |
1553
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1554
|
|
|
|
|
|
|
# |
1555
|
|
|
|
|
|
|
sub Yapp_derivative |
1556
|
|
|
|
|
|
|
{ |
1557
|
20
|
|
|
20
|
0
|
58
|
my $self = shift(@_); |
1558
|
20
|
100
|
|
|
|
51
|
my $order = defined($_[0]) ? shift(@_) : 1; # Get nth derivative or assume 1 |
1559
|
20
|
|
|
|
|
26
|
my $start_n; # Highest order derivative we already have |
1560
|
|
|
|
|
|
|
my $coefref; # Use as array reference as I step into each polynomial |
1561
|
0
|
|
|
|
|
0
|
my $dlc; # Loop counter for derivatives |
1562
|
|
|
|
|
|
|
|
1563
|
20
|
100
|
|
|
|
69
|
if (defined($self->{derivative}[$order])) # If I have already derived this |
1564
|
8
|
|
|
|
|
21
|
{ return($self->{derivative}[$order]); } # just give it again, no work |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
# Still here? Two possibilities: |
1567
|
|
|
|
|
|
|
# 1. Previous derivatives have been derived, just not this order |
1568
|
|
|
|
|
|
|
# 2. This is the first time a derivative it being requested, so I have to |
1569
|
|
|
|
|
|
|
# create the derivatives array from scratch |
1570
|
|
|
|
|
|
|
# |
1571
|
12
|
100
|
|
|
|
36
|
if (defined($self->{derivative}[1])) |
1572
|
|
|
|
|
|
|
{ # If we already have derivatives saved |
1573
|
2
|
|
|
|
|
5
|
$start_n = $#{$self->{derivative}}; # Start past highest we already have |
|
2
|
|
|
|
|
6
|
|
1574
|
|
|
|
|
|
|
} |
1575
|
|
|
|
|
|
|
else |
1576
|
|
|
|
|
|
|
{ # Starting derivatives from scratch |
1577
|
10
|
|
|
|
|
21
|
$self->{derivative}[0] = Yapp(); # Empty placeholder for 0th deriv |
1578
|
10
|
|
|
|
|
17
|
$start_n = 0; # 'Cause we got none yet |
1579
|
|
|
|
|
|
|
} |
1580
|
|
|
|
|
|
|
|
1581
|
12
|
|
|
|
|
39
|
for ($dlc = $start_n; $dlc < $order; $dlc++) |
1582
|
|
|
|
|
|
|
{ |
1583
|
14
|
50
|
|
|
|
39
|
last if ($dlc >= $self->{degree}); # No more derivatives than degree |
1584
|
14
|
|
|
|
|
19
|
my @new_list; |
1585
|
14
|
100
|
|
|
|
36
|
if ($dlc == 0) {$coefref = $self->{coeff}; } # -> my coeffiecients |
|
10
|
|
|
|
|
21
|
|
1586
|
|
|
|
|
|
|
else |
1587
|
|
|
|
|
|
|
{ |
1588
|
4
|
|
|
|
|
9
|
my $cur_deriv = $self->{derivative}[$dlc]; # -> (lc)th derivative |
1589
|
4
|
|
|
|
|
8
|
$coefref = \@{$cur_deriv->{coeff}}; # and to *its* coefficients |
|
4
|
|
|
|
|
11
|
|
1590
|
|
|
|
|
|
|
} |
1591
|
14
|
|
|
|
|
24
|
for (my $tlc = 1; $tlc <= $#{$coefref}; $tlc++) # Skip 0th term |
|
84
|
|
|
|
|
187
|
|
1592
|
|
|
|
|
|
|
{ |
1593
|
70
|
|
|
|
|
159
|
$new_list[$tlc-1] = $tlc * $coefref->[$tlc]; # Coeff * its exponent |
1594
|
|
|
|
|
|
|
} |
1595
|
14
|
|
|
|
|
33
|
$self->{derivative}[$dlc+1] = Yapp(\@new_list); # New Yapp in deriv slot |
1596
|
|
|
|
|
|
|
} |
1597
|
12
|
|
|
|
|
40
|
return ($self->{derivative}[$dlc]); |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
# |
1600
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1601
|
|
|
|
|
|
|
# Yapp_Integral(): Method to calculate the first integral of the given Yapp |
1602
|
|
|
|
|
|
|
# polynomial. |
1603
|
|
|
|
|
|
|
# Let's clarify: This serves as two functions: |
1604
|
|
|
|
|
|
|
# - The indefinite integral AKA the antiderivative, returning a Yapp polynomial |
1605
|
|
|
|
|
|
|
# for the given polynomial. Will not include the arbitrary constant you were |
1606
|
|
|
|
|
|
|
# taught to always include with your antiderivative. |
1607
|
|
|
|
|
|
|
# - A definite integral: That is, supplying two numbers that represent the |
1608
|
|
|
|
|
|
|
# endpoints of an interval i.e. the limits of the integral. |
1609
|
|
|
|
|
|
|
# Returns: Well, that depends: |
1610
|
|
|
|
|
|
|
# - For indefinte integral, returns a reference to the antiderivative polynomial |
1611
|
|
|
|
|
|
|
# (which as been stored in $self anyway.) |
1612
|
|
|
|
|
|
|
# - For definite integral: Returns the value of that integral between the |
1613
|
|
|
|
|
|
|
# limits. |
1614
|
|
|
|
|
|
|
# In both cases, the antiderivative polynomial is cached along with the given |
1615
|
|
|
|
|
|
|
# Yapp so it may be re-used for various limits. |
1616
|
|
|
|
|
|
|
# |
1617
|
|
|
|
|
|
|
# Note: Unlike with derivatives, I see no need to code for additional order |
1618
|
|
|
|
|
|
|
# of integral. (I'm not anticipating a Laurent series) |
1619
|
|
|
|
|
|
|
# |
1620
|
|
|
|
|
|
|
sub Yapp_integral |
1621
|
|
|
|
|
|
|
{ |
1622
|
2
|
50
|
66
|
2
|
0
|
19
|
die ("Must supply either no integral limits or exactly two points") |
1623
|
|
|
|
|
|
|
unless ( ($#_ == 2) || ($#_ == 0)); # Bail if wrong parameters given |
1624
|
2
|
|
|
|
|
3
|
my $self = shift(@_); # If $#_ was 2, it is now 1 |
1625
|
2
|
|
|
|
|
4
|
my ($l_limit, $u_limit) = (undef, undef); # Don't know yet if user called |
1626
|
|
|
|
|
|
|
# me for a definite integral. |
1627
|
2
|
|
|
|
|
4
|
my $ryapp; # My return reference for indefinite |
1628
|
2
|
|
|
|
|
3
|
my $rval= 0.0; # My return value for definite |
1629
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
|
1631
|
2
|
100
|
|
|
|
6
|
($l_limit, $u_limit) = @_ if ($#_ == 1); # Now is when I find out about |
1632
|
|
|
|
|
|
|
# those endpoint parameters |
1633
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
# Note: If I never calculated the integral for this polynomial (or if it |
1635
|
|
|
|
|
|
|
# has "refreshed"), the {integral} component may be an array or undefined. |
1636
|
|
|
|
|
|
|
# But it *won't" be Yapp. |
1637
|
|
|
|
|
|
|
# |
1638
|
2
|
100
|
|
|
|
8
|
if (ref($self->{integral}) ne $class_name) # Ever calculated the indefinite |
1639
|
|
|
|
|
|
|
# integral for this polynomial? |
1640
|
1
|
|
|
|
|
6
|
{ # No: Do it now. |
1641
|
1
|
|
|
|
|
3
|
my @new_list = (0); # 0 in place of arbitrary constant |
1642
|
1
|
|
|
|
|
6
|
for (my $ilc = 0; $ilc <= $self->{degree}; $ilc++) |
1643
|
|
|
|
|
|
|
{ |
1644
|
6
|
|
|
|
|
20
|
push (@new_list, ($self->{coeff}[$ilc] / ($ilc + 1))); |
1645
|
|
|
|
|
|
|
} |
1646
|
1
|
|
|
|
|
4
|
$ryapp = Yapp(\@new_list); # Create the polynomial |
1647
|
1
|
|
|
|
|
4
|
$self->{integral} = $ryapp; # and cache that for re-use |
1648
|
|
|
|
|
|
|
} |
1649
|
|
|
|
|
|
|
else {$ryapp = $self->{integral}; } # Already computed: Just re-use it |
1650
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
# Part 2: What does the user want? Definite or indefinite integral? |
1652
|
|
|
|
|
|
|
# |
1653
|
2
|
100
|
|
|
|
9
|
return ($ryapp) unless (defined($l_limit)); # No limits? Wants indefinite |
1654
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
# Still here: Wants the value of that integral |
1656
|
|
|
|
|
|
|
# |
1657
|
1
|
|
|
|
|
6
|
my $u_val = $ryapp->Yapp_eval($u_limit); # Value of antideriv at upper limit |
1658
|
1
|
|
|
|
|
3
|
my $l_val = $ryapp->Yapp_eval($l_limit); # Value of antideriv at lower limit |
1659
|
1
|
|
|
|
|
2
|
$rval = $u_val - $l_val; # And that's the integral value |
1660
|
1
|
|
|
|
|
4
|
return $rval; |
1661
|
|
|
|
|
|
|
} |
1662
|
|
|
|
|
|
|
# |
1663
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1664
|
|
|
|
|
|
|
# Solving for the zeros of a polynomial: |
1665
|
|
|
|
|
|
|
# Method Yapp_solve() is the only method that should be called byt he users. |
1666
|
|
|
|
|
|
|
# This, in turn calls appropriate other methods. For example, for a quadratic |
1667
|
|
|
|
|
|
|
# polynomial, Yapp_dolve() will call Yapp_solve_quad(), which will simply use |
1668
|
|
|
|
|
|
|
# the quadratic formula. For a cubic, it uses the algorithm of Gerolamo Cardano |
1669
|
|
|
|
|
|
|
# and for the quartic (4th degree) that of Lodovico Ferrari. After that, we |
1670
|
|
|
|
|
|
|
# resort to bisection to locate a solution, then go to Newton's method. (A |
1671
|
|
|
|
|
|
|
# future release may switch to laGuerre's method.) |
1672
|
|
|
|
|
|
|
# |
1673
|
|
|
|
|
|
|
# For degree >= 5 we are guranteed at least one real root only for odd-degree |
1674
|
|
|
|
|
|
|
# polynomials so bisection is a sure thing. However, for an even-degree poly- |
1675
|
|
|
|
|
|
|
# nomial, there is no such guarantee. I have tried to discuss and analyze |
1676
|
|
|
|
|
|
|
# methods that might be analogous to bisection for complex numbers but to no |
1677
|
|
|
|
|
|
|
# avail. I have bitten the bullet and coded Laguerre's method as a starting |
1678
|
|
|
|
|
|
|
# point for even-degree polynomials of degree >= 6 |
1679
|
|
|
|
|
|
|
# |
1680
|
|
|
|
|
|
|
# Short cuts: |
1681
|
|
|
|
|
|
|
# - When a complex root is found, we will also evaluate the quotient for the |
1682
|
|
|
|
|
|
|
# conjugate of that root. |
1683
|
|
|
|
|
|
|
# - In all cases, when any root is found, we evaluate quotient for the same root |
1684
|
|
|
|
|
|
|
# in case it's a multiple root. |
1685
|
|
|
|
|
|
|
# - Whenever a root (or set of roots, as bove) is found, we make a recursive |
1686
|
|
|
|
|
|
|
# call to Yapp_solve() to get the roots of the quotient polynomial. (I have |
1687
|
|
|
|
|
|
|
# read that the technique is called deflation.) However, after getting the |
1688
|
|
|
|
|
|
|
# roots of the deflated polynomial, I still apply Newton's method on the roots |
1689
|
|
|
|
|
|
|
# with respect to the polynmomial at hand. This is to compensate for rounding |
1690
|
|
|
|
|
|
|
# errors that have crept in while solving the deflated polynomial |
1691
|
|
|
|
|
|
|
# |
1692
|
|
|
|
|
|
|
# Parameter: |
1693
|
|
|
|
|
|
|
# - (Implicit) The Yapp polynomial to solved |
1694
|
|
|
|
|
|
|
# Returns: |
1695
|
|
|
|
|
|
|
# - An array of solutions, which may include repeated values owing to repeated |
1696
|
|
|
|
|
|
|
# roots. (Not a reference but the array otself.) |
1697
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1698
|
|
|
|
|
|
|
# |
1699
|
|
|
|
|
|
|
sub Yapp_solve |
1700
|
|
|
|
|
|
|
{ |
1701
|
25
|
|
|
25
|
0
|
80
|
my $self = shift(@_); |
1702
|
25
|
|
|
|
|
55
|
my $solu_end = $self->{degree} # The degree the polynomial came it with |
1703
|
|
|
|
|
|
|
- 1; # Top index of solutions array |
1704
|
25
|
|
|
|
|
43
|
my @solutions = (); # Array to be returned starts empty |
1705
|
25
|
|
|
|
|
28
|
my $solu_begin = 0; # Where to start pushing solutions in |
1706
|
|
|
|
|
|
|
# the @soultions array |
1707
|
25
|
50
|
|
|
|
69
|
if ($solu_end == 0) |
1708
|
|
|
|
|
|
|
{ |
1709
|
0
|
|
|
|
|
0
|
carp("0-degree polynomial <$self->Ysprint()> has no solution"); |
1710
|
0
|
|
|
|
|
0
|
return @solutions; # Don't bother with it anymore |
1711
|
|
|
|
|
|
|
} |
1712
|
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
# Quick check: If the constant term of the polynomial is 0, then we know |
1714
|
|
|
|
|
|
|
# that 0 is a solution. In that case, shift the polynomial down a degree |
1715
|
|
|
|
|
|
|
# and test again. This is the point of the following loop.. Except that I |
1716
|
|
|
|
|
|
|
# am assuming that constant term is zero if it is sufficiently small; that |
1717
|
|
|
|
|
|
|
# it is the result of a rounding error in a previous computation is this |
1718
|
|
|
|
|
|
|
# highly recursive function. |
1719
|
|
|
|
|
|
|
# |
1720
|
|
|
|
|
|
|
# |
1721
|
25
|
|
|
|
|
81
|
while (abs($self->{coeff}[0]) <= $margin) |
1722
|
|
|
|
|
|
|
{ |
1723
|
0
|
|
|
|
|
0
|
$solutions[$solu_begin++] = 0.0; # Zero *is* a solution to *this* polynomial |
1724
|
0
|
|
|
|
|
0
|
shift(@{$self->{coeff}}); # Knock it down a degree for next round |
|
0
|
|
|
|
|
0
|
|
1725
|
0
|
|
|
|
|
0
|
--($self->{degree}); # Register the lowered degree |
1726
|
|
|
|
|
|
|
} |
1727
|
25
|
|
|
|
|
41
|
my $degree = $self->{degree}; #(Just neater for successive if/elsif) |
1728
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
# After this, we go structured; no returning from within "if" blocks |
1730
|
|
|
|
|
|
|
# |
1731
|
25
|
50
|
|
|
|
88
|
if ($degree == 1) # b + ax = 0 |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
{ |
1733
|
0
|
|
|
|
|
0
|
$solutions[$solu_begin] = - $self->{coeff}[0]/$self->{coeff}[1] ; # -b/a |
1734
|
|
|
|
|
|
|
} |
1735
|
|
|
|
|
|
|
elsif ($degree == 2) |
1736
|
|
|
|
|
|
|
{ |
1737
|
16
|
|
|
|
|
36
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_quad(); |
1738
|
|
|
|
|
|
|
} |
1739
|
|
|
|
|
|
|
elsif ($degree == 3) |
1740
|
|
|
|
|
|
|
{ |
1741
|
4
|
|
|
|
|
17
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_cubic(); |
1742
|
|
|
|
|
|
|
} |
1743
|
|
|
|
|
|
|
elsif ($degree == 4) |
1744
|
|
|
|
|
|
|
{ |
1745
|
3
|
|
|
|
|
17
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_quartic(); |
1746
|
|
|
|
|
|
|
} |
1747
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
# We've just run out of fomulaic methods and we must resort to pure numerical |
1749
|
|
|
|
|
|
|
# methods. Oh Joy! |
1750
|
|
|
|
|
|
|
# |
1751
|
|
|
|
|
|
|
elsif (! $self->all_real) |
1752
|
|
|
|
|
|
|
{ # Complex Coefficients: Some theorems go out the window |
1753
|
0
|
|
|
|
|
0
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_complex(); |
1754
|
|
|
|
|
|
|
} |
1755
|
|
|
|
|
|
|
elsif (($degree % 2) == 1) |
1756
|
|
|
|
|
|
|
{ |
1757
|
1
|
|
|
|
|
7
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_odd(); |
1758
|
|
|
|
|
|
|
} |
1759
|
|
|
|
|
|
|
else |
1760
|
|
|
|
|
|
|
{ |
1761
|
1
|
|
|
|
|
6
|
@solutions[$solu_begin .. $solu_end] = $self->Yapp_solve_even(); |
1762
|
|
|
|
|
|
|
} |
1763
|
|
|
|
|
|
|
# Apply this filter to the @solutions array: If we have reason to suspect |
1764
|
|
|
|
|
|
|
# that a complex number looks complex only due to rounding errors i.e. the |
1765
|
|
|
|
|
|
|
# imaginary part is so small that it looks ignorable, then do ignore it. |
1766
|
|
|
|
|
|
|
# Similarly, we can nullify (but not ignore) a trivial real component of a |
1767
|
|
|
|
|
|
|
# complex number. |
1768
|
|
|
|
|
|
|
# |
1769
|
25
|
|
|
|
|
73
|
realify(\@solutions); |
1770
|
|
|
|
|
|
|
|
1771
|
25
|
|
|
|
|
150
|
return @solutions; |
1772
|
|
|
|
|
|
|
} |
1773
|
|
|
|
|
|
|
# |
1774
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1775
|
|
|
|
|
|
|
# Yapp_newton(): Internal method. If we already have a crude (in the eye of |
1776
|
|
|
|
|
|
|
# the beholder) appoximation of a root, let's get much closer |
1777
|
|
|
|
|
|
|
# by using Newton's method. This is especially a problem |
1778
|
|
|
|
|
|
|
# with the Ferrari and Cardano's algorithms when applied as |
1779
|
|
|
|
|
|
|
# algorithms and rounding error are inevitable. |
1780
|
|
|
|
|
|
|
# Parameters: |
1781
|
|
|
|
|
|
|
# - (Implicit) The Yapp object |
1782
|
|
|
|
|
|
|
# - The initial value |
1783
|
|
|
|
|
|
|
# Returns: |
1784
|
|
|
|
|
|
|
# - A refined value, with $margin of being correct. |
1785
|
|
|
|
|
|
|
# |
1786
|
|
|
|
|
|
|
sub Yapp_newton |
1787
|
|
|
|
|
|
|
{ |
1788
|
9
|
|
|
9
|
0
|
17
|
my ($self, $xn) = @_; # Get object an our starting X-value |
1789
|
|
|
|
|
|
|
|
1790
|
9
|
|
|
|
|
87
|
my $func_name = (caller(0))[3]; #(For debugging) |
1791
|
9
|
|
|
|
|
37
|
my $yprime = $self->Yapp_derivative(1); |
1792
|
9
|
|
|
|
|
25
|
my $valxn = $self->Yapp_eval($xn); # Evaluation at starting point |
1793
|
9
|
|
|
|
|
27
|
while (abs($valxn) >= $margin) # Continue this loop until I'm REALLY close |
1794
|
|
|
|
|
|
|
{ # to a solution. |
1795
|
10
|
|
|
|
|
116
|
my $xc = $xn; # Hold a copy of current tested value |
1796
|
10
|
|
|
|
|
22
|
my $yp = $yprime->Yapp_eval($xn); # Get derivative at current $xn |
1797
|
10
|
50
|
|
|
|
28
|
last if (abs($yp) == 0); # Beware of zero-divide! |
1798
|
10
|
|
|
|
|
128
|
my $correction = $valxn / $yp; # Current value / current derivative |
1799
|
10
|
|
|
|
|
710
|
$xn -= $correction; # X[n+1] = X[n] - Y(X[n])/Y'(X[n]) |
1800
|
10
|
100
|
|
|
|
956
|
last if (abs($xn - $xc) < $margin); # Another exit condition: Almost no diff |
1801
|
|
|
|
|
|
|
# between this x and previous x |
1802
|
1
|
|
|
|
|
81
|
$valxn = $self->Yapp_eval($xn); # Ready for test or use atop next round |
1803
|
1
|
50
|
|
|
|
11
|
printf("%s: xn = <%s>; valxn = <%s>\n", |
1804
|
|
|
|
|
|
|
$func_name, Csprint($xn), Csprint($valxn)) |
1805
|
|
|
|
|
|
|
if ($testmode); |
1806
|
|
|
|
|
|
|
} |
1807
|
9
|
|
|
|
|
847
|
return($xn); # Close enough to send back to caller |
1808
|
|
|
|
|
|
|
} |
1809
|
|
|
|
|
|
|
# |
1810
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1811
|
|
|
|
|
|
|
# Yapp_laguerre(): Internal method. If we start with just about any starting |
1812
|
|
|
|
|
|
|
# point - it doesen't even have to be all that close we can |
1813
|
|
|
|
|
|
|
# get much closer by using Laguerre's method. |
1814
|
|
|
|
|
|
|
# Source of the mathetics I am using: |
1815
|
|
|
|
|
|
|
# Numerical Recipes in C; The Art of Scientific Computing |
1816
|
|
|
|
|
|
|
# Second Edition (C) 1988, 1992` |
1817
|
|
|
|
|
|
|
# Authors: William H. Press |
1818
|
|
|
|
|
|
|
# Saul A. Teukolsky |
1819
|
|
|
|
|
|
|
# William T. Vetterling |
1820
|
|
|
|
|
|
|
# Brian P. Flannery |
1821
|
|
|
|
|
|
|
# Pages: 372-374 |
1822
|
|
|
|
|
|
|
# Note: I made the chagrined discovery that rounding errors cause the test |
1823
|
|
|
|
|
|
|
# values to fluctuate a few decimal places out of my desired margin. |
1824
|
|
|
|
|
|
|
# Experiment: Use Laguerre's method to within .001, then switch to Newton's |
1825
|
|
|
|
|
|
|
# method. |
1826
|
|
|
|
|
|
|
# ... |
1827
|
|
|
|
|
|
|
# Parameters: |
1828
|
|
|
|
|
|
|
# - (Implicit) The Yapp object |
1829
|
|
|
|
|
|
|
# - The initial value |
1830
|
|
|
|
|
|
|
# Returns: |
1831
|
|
|
|
|
|
|
# - A refined value, with $margin of being correct. |
1832
|
|
|
|
|
|
|
# |
1833
|
|
|
|
|
|
|
sub Yapp_laguerre |
1834
|
|
|
|
|
|
|
{ |
1835
|
1
|
|
|
1
|
0
|
2
|
my ($self, $xn) = @_; # Get object an our starting X-value |
1836
|
1
|
|
|
|
|
11
|
my $func_name = (caller(0))[3]; #(For debugging) |
1837
|
|
|
|
|
|
|
|
1838
|
1
|
|
|
|
|
5
|
my $degree = $self->{degree}; # This will figure in recalculations |
1839
|
1
|
|
|
|
|
6
|
my $yprime = $self->Yapp_derivative(1); # First derivative of self |
1840
|
1
|
|
|
|
|
4
|
my $yprime2 = $self->Yapp_derivative(2); # And second derivative |
1841
|
1
|
|
|
|
|
6
|
my $valxn = $self->Yapp_eval($xn); # Evaluation at starting point |
1842
|
1
|
|
|
|
|
3
|
my $crude_margin = .001; # Quite close - might as well take |
1843
|
|
|
|
|
|
|
# advantage of Laguerre's faster |
1844
|
|
|
|
|
|
|
# convergence. |
1845
|
1
|
|
|
|
|
4
|
while (abs($valxn) >= $crude_margin) # Bet this looks familiar so far :) |
1846
|
|
|
|
|
|
|
{ # Well, hang on to your seats! |
1847
|
|
|
|
|
|
|
# Correction factor is: |
1848
|
|
|
|
|
|
|
# Degree / (G +/- sqrt((Degree - 1) * (Degree * H - G**2))) |
1849
|
|
|
|
|
|
|
# See evaluation of $gxn for G and $hxn for H below |
1850
|
|
|
|
|
|
|
# Ugly, ain't it? ;-) |
1851
|
|
|
|
|
|
|
# |
1852
|
4
|
|
|
|
|
49
|
my $prev_xn = $xn; # Keep current estimate for comparison |
1853
|
4
|
|
|
|
|
12
|
my $gxn = $yprime->Yapp_eval($xn) / $valxn; |
1854
|
4
|
|
|
|
|
285
|
my $hxn = $gxn**2 - ($yprime2->Yapp_eval($xn) / $valxn); |
1855
|
4
|
|
|
|
|
594
|
my $under_rad = ($degree -1) |
1856
|
|
|
|
|
|
|
* ($degree * $hxn - $gxn ** 2); # May be negative or complex |
1857
|
4
|
|
|
|
|
1797
|
my @rad_roots = root($under_rad, 2); # Take square roots of that |
1858
|
4
|
|
|
|
|
981
|
my @denoms = ($gxn - $rad_roots[0], # Choose a denominator for fraction |
1859
|
|
|
|
|
|
|
$gxn + $rad_roots[0]); # to be used in a correction. |
1860
|
4
|
|
|
|
|
443
|
my @norms = (abs($denoms[0]), abs($denoms[1])); # Which denominator has the |
1861
|
|
|
|
|
|
|
# bigger absolute value? |
1862
|
4
|
50
|
|
|
|
134
|
my $denom = ($norms[0] > $norms[1]) ? $denoms[0] : $denoms[1]; # Pick that |
1863
|
4
|
|
|
|
|
9
|
my $correction = $degree / $denom; # Correct previous estimate by this |
1864
|
4
|
|
|
|
|
236
|
$xn -= $correction; # "amount" by subtracting it |
1865
|
|
|
|
|
|
|
#? last if (abs($prev_xn - $xn) < $margin); # Quit if correction was teeny |
1866
|
4
|
|
|
|
|
123
|
$valxn = $self->Yapp_eval($xn); # Otherwise, re-evaluate at new |
1867
|
|
|
|
|
|
|
# estimate and plug in next round |
1868
|
4
|
50
|
|
|
|
70
|
printf("%s: xn = <%s>; valxn = <%s>\n", |
1869
|
|
|
|
|
|
|
$func_name, Csprint($xn), Csprint($valxn)) |
1870
|
|
|
|
|
|
|
if ($testmode); |
1871
|
|
|
|
|
|
|
} |
1872
|
|
|
|
|
|
|
# Came out of loop: I guess we're close enough to start with Newton? |
1873
|
|
|
|
|
|
|
# |
1874
|
1
|
|
|
|
|
13
|
$xn = realify($xn); # NUllify tiny Im component |
1875
|
1
|
|
|
|
|
8
|
$xn = $self->Yapp_newton($xn); # before feeding it to Newton |
1876
|
1
|
|
|
|
|
6
|
realify($xn); # and correct again afterward |
1877
|
1
|
|
|
|
|
6
|
return $xn; |
1878
|
|
|
|
|
|
|
} |
1879
|
|
|
|
|
|
|
# |
1880
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1881
|
|
|
|
|
|
|
# Yapp_solve_quad(): Solve a quadratic equation |
1882
|
|
|
|
|
|
|
# |
1883
|
|
|
|
|
|
|
sub Yapp_solve_quad |
1884
|
|
|
|
|
|
|
{ |
1885
|
16
|
|
|
16
|
0
|
26
|
my $self = shift(@_); |
1886
|
16
|
|
|
|
|
19
|
my ($real_part, $discr, $rad_part); |
1887
|
16
|
|
|
|
|
23
|
my @solutions = (); # Look familiar? :-) |
1888
|
16
|
|
|
|
|
55
|
my $i = cplx(0, 1); # The familiar imaginary unit, "i" |
1889
|
16
|
|
|
|
|
870
|
my ($c, $b, $a) = @{$self->{coeff}}; # Copy the array, for neatness |
|
16
|
|
|
|
|
40
|
|
1890
|
|
|
|
|
|
|
|
1891
|
16
|
|
|
|
|
28
|
$real_part = -$b / (2 * $a); # This part is always guaranteed real |
1892
|
|
|
|
|
|
|
# (assuming real coefficients, of course) |
1893
|
16
|
|
|
|
|
41
|
$discr = $b ** 2 - (4 * $a * $c); # Discriminant: b^2 - 4ac |
1894
|
16
|
50
|
|
|
|
50
|
if ($discr == 0) |
|
|
100
|
|
|
|
|
|
1895
|
|
|
|
|
|
|
{ |
1896
|
0
|
|
|
|
|
0
|
@solutions = ($real_part, $real_part); # Repeated roots. We're done! |
1897
|
|
|
|
|
|
|
} |
1898
|
|
|
|
|
|
|
elsif ($discr > 0) |
1899
|
|
|
|
|
|
|
{ |
1900
|
5
|
|
|
|
|
21
|
$rad_part = sqrt($discr)/ (2 * $a); # The part under the radical |
1901
|
5
|
|
|
|
|
63
|
@solutions = ($real_part - $rad_part, $real_part + $rad_part); |
1902
|
|
|
|
|
|
|
} |
1903
|
|
|
|
|
|
|
else # Negative discriminant => complex roots |
1904
|
|
|
|
|
|
|
{ |
1905
|
11
|
|
|
|
|
34
|
my @i_roots = root($discr, 2); # Get both imaginary roots |
1906
|
|
|
|
|
|
|
|
1907
|
|
|
|
|
|
|
# Because the root() function is too *&&^% comfortable in polar form, we |
1908
|
|
|
|
|
|
|
# get a miniscule (but still there) real part. Get rid of it! |
1909
|
|
|
|
|
|
|
# |
1910
|
11
|
|
|
|
|
1485
|
@i_roots = (cplx(0.0, $i_roots[0]->Im), cplx(0.0, $i_roots[1]->Im)); |
1911
|
11
|
50
|
|
|
|
1366
|
printf("Roots of discriminant <%f> are: <@i_roots>\n", $discr) |
1912
|
|
|
|
|
|
|
if ($testmode); |
1913
|
11
|
|
|
|
|
47
|
@i_roots = (($i_roots[0]/(2 * $a)), |
1914
|
|
|
|
|
|
|
($i_roots[1]/(2 * $a)) ); # Recall: All parts divided by 2a |
1915
|
11
|
|
|
|
|
1309
|
@solutions = ($real_part + $i_roots[0], $real_part + $i_roots[1]); |
1916
|
|
|
|
|
|
|
} |
1917
|
16
|
|
|
|
|
2784
|
return @solutions; |
1918
|
|
|
|
|
|
|
} |
1919
|
|
|
|
|
|
|
# |
1920
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
1921
|
|
|
|
|
|
|
# Yapp_solve_cubic() |
1922
|
|
|
|
|
|
|
# |
1923
|
|
|
|
|
|
|
sub Yapp_solve_cubic |
1924
|
|
|
|
|
|
|
{ |
1925
|
4
|
|
|
4
|
0
|
5
|
my $self = shift(@_); |
1926
|
4
|
|
|
|
|
9
|
my @solutions; # I will return this array |
1927
|
|
|
|
|
|
|
my $monic; # Original Yapp with 1 as leading coefficient |
1928
|
0
|
|
|
|
|
0
|
my $reduced; # $monic with roots reduced, if needed |
1929
|
4
|
|
|
|
|
6
|
my $reduced_by = 0; # By how much to reduce the roots in order |
1930
|
|
|
|
|
|
|
# to eliminate the degree[2] term |
1931
|
4
|
|
|
|
|
6
|
my ($zero, $quotient_2, $quotient_1); # zero is mainly a placeholder. |
1932
|
|
|
|
|
|
|
# the two quotients are for after an |
1933
|
|
|
|
|
|
|
# evaluation to get the next lower |
1934
|
|
|
|
|
|
|
# degree polynomials. |
1935
|
4
|
|
|
|
|
13
|
my $allreal = $self->all_real; # Optimistic start |
1936
|
|
|
|
|
|
|
|
1937
|
|
|
|
|
|
|
# 2 transformations: |
1938
|
|
|
|
|
|
|
# - Make sure leading coefficient is 1 (monic) |
1939
|
|
|
|
|
|
|
# - Reduce roots by 1/3 of the x^2 coefficient. This sets the x^2 coefficient |
1940
|
|
|
|
|
|
|
# (Which is always the negative of the sum of the roots) to 0 |
1941
|
|
|
|
|
|
|
# |
1942
|
4
|
50
|
|
|
|
13
|
if ($self->{coeff}[3] == 1) # If this is alrady a monic polynomial |
1943
|
|
|
|
|
|
|
{ |
1944
|
0
|
|
|
|
|
0
|
$monic = Yapp($self); # Use a copy before reducing roots |
1945
|
|
|
|
|
|
|
} |
1946
|
|
|
|
|
|
|
else |
1947
|
|
|
|
|
|
|
{ # Divide by leading coefficent |
1948
|
4
|
|
|
|
|
15
|
$monic = $self / ($self->{coeff}[3]); # to *make* it monic |
1949
|
4
|
50
|
|
|
|
13
|
printf("Self: <%s>\nMonic: <%s>\n", $self->Ysprint(), $monic->Ysprint()) |
1950
|
|
|
|
|
|
|
if ($testmode); # TESTMODE |
1951
|
|
|
|
|
|
|
} |
1952
|
4
|
50
|
|
|
|
13
|
if ($monic->{coeff}[2] != 0) # If we have a degree[2] term |
1953
|
|
|
|
|
|
|
{ |
1954
|
4
|
|
|
|
|
9
|
$reduced_by = -($monic->{coeff}[2])/3; # Reduce by 1/3 of x^2 term |
1955
|
4
|
|
|
|
|
13
|
$reduced = $monic->Yapp_reduce($reduced_by); |
1956
|
4
|
50
|
|
|
|
31
|
printf("Original <%s>, with roots reduced by <%.5f>\nYields: <%s>\n", |
1957
|
|
|
|
|
|
|
$self->Ysprint(), $reduced_by, $reduced->Ysprint()) |
1958
|
|
|
|
|
|
|
if($testmode) # TESTMODE |
1959
|
|
|
|
|
|
|
} |
1960
|
|
|
|
|
|
|
else |
1961
|
|
|
|
|
|
|
{ |
1962
|
0
|
|
|
|
|
0
|
$reduced = Yapp($monic); # Had no X^2 term. |
1963
|
|
|
|
|
|
|
} |
1964
|
4
|
|
|
|
|
9
|
my $p = $reduced->{coeff}[1]; |
1965
|
4
|
|
|
|
|
8
|
my $q = $reduced->{coeff}[0]; |
1966
|
|
|
|
|
|
|
# |
1967
|
|
|
|
|
|
|
# In Cardano's algorithm, I set X = U + V, which lets me play games with |
1968
|
|
|
|
|
|
|
# these new variables. After I pluck in (U+V) for X in the reduced monic |
1969
|
|
|
|
|
|
|
# polynomial, I collect terms and get an expression like this: |
1970
|
|
|
|
|
|
|
# U^3 + (U + V)(3*U*V + p) + V^3 + q = 0 |
1971
|
|
|
|
|
|
|
# If I set (3*U*V + p) to 0 I get a much simpler expression: |
1972
|
|
|
|
|
|
|
# U^3 + V^3 + q = 0. But, in light of the 0 setting above, I need to |
1973
|
|
|
|
|
|
|
# substitute V = -p/(3*U). This results in the 6th degre equation: |
1974
|
|
|
|
|
|
|
# 27U^6 + 27q*U^3 - p^3 = 0. |
1975
|
|
|
|
|
|
|
# Hey! This is quadratic in U^3. I can solve this! |
1976
|
|
|
|
|
|
|
# |
1977
|
4
|
|
|
|
|
20
|
my $u_six = Yapp((-$p ** 3), (27 * $q), 27); |
1978
|
4
|
|
|
|
|
15
|
$u_six->variable("U-Cubed"); # Set this variable for clarity |
1979
|
4
|
50
|
|
|
|
11
|
printf("U-Cubed quadratic is <%s>\n", $u_six->Ysprint()) |
1980
|
|
|
|
|
|
|
if ($testmode); # TESTMODE |
1981
|
4
|
|
|
|
|
30
|
my @u_solution = $u_six->Yapp_solve(); # I only need one of these |
1982
|
4
|
|
|
|
|
8
|
my $u_cubed = $u_solution[0]; # Pick first one - Easy |
1983
|
4
|
|
|
|
|
6
|
my ($u, $v); # Components for X = U + V |
1984
|
|
|
|
|
|
|
|
1985
|
|
|
|
|
|
|
# $u_cubed may be a complex number; I need to be circumspect about taking |
1986
|
|
|
|
|
|
|
# its cube root. |
1987
|
|
|
|
|
|
|
# |
1988
|
4
|
|
|
|
|
14
|
my @u_roots = root($u_cubed, 3); # Get all three cube roots |
1989
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
# Note: Even the real cube root of a real number may come back as a complex |
1991
|
|
|
|
|
|
|
# with an extremely small imaginary part, on the order of 10^-15 or so. This |
1992
|
|
|
|
|
|
|
# is a legitimate rounding error, which I can lose with relative impunity. |
1993
|
|
|
|
|
|
|
# |
1994
|
4
|
|
|
|
|
1191
|
my $use_this_u = $u_roots[0]; # Ready to use first root, even complex |
1995
|
4
|
|
|
|
|
10
|
realify(\@u_roots); # Lose insignificant imaginary com- |
1996
|
|
|
|
|
|
|
# ponents from rounding errors. |
1997
|
4
|
|
|
|
|
16
|
for (my $rlc = 0; $rlc < 3; $rlc++) # Search for a real cube root |
1998
|
|
|
|
|
|
|
{ |
1999
|
11
|
100
|
|
|
|
39
|
if (ref($u_roots[$rlc]) ne $class_cplx) # Fair to assume: If it ain't |
2000
|
|
|
|
|
|
|
{ # complex, it's the real we prefer |
2001
|
1
|
|
|
|
|
2
|
$use_this_u = $u_roots[$rlc]; # If we found it, grab it and run |
2002
|
1
|
|
|
|
|
2
|
last; |
2003
|
|
|
|
|
|
|
} |
2004
|
|
|
|
|
|
|
} |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
# Now I can set $u and $v properly. $u may be real or complex as per above |
2007
|
|
|
|
|
|
|
# loop. But if it is complex, $v seems to inevitable end up as the |
2008
|
|
|
|
|
|
|
# conjugate of $u. Prefer to use a real root but if none is available, |
2009
|
|
|
|
|
|
|
# one root is as good as another for this purpose; in that case, use the |
2010
|
|
|
|
|
|
|
# first cube root. |
2011
|
|
|
|
|
|
|
# |
2012
|
4
|
|
|
|
|
8
|
$u = $use_this_u; # This is the cube root to work with. |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
# Now recall that V = -p/(3*U) from the 3*U*V +p = 0 setting |
2015
|
|
|
|
|
|
|
# |
2016
|
4
|
|
|
|
|
15
|
$v = -$p / (3 * $u); # The other component ox X = U + V |
2017
|
4
|
|
|
|
|
367
|
$solutions[0] = $u + $v # Almost there; but remember this is the root |
2018
|
|
|
|
|
|
|
+ $reduced_by; # reduced in the second transformation |
2019
|
|
|
|
|
|
|
# Now THAT's a root! .. er, almost. |
2020
|
4
|
|
|
|
|
685
|
$solutions[0] = realify($solutions[0]); # Lose trivial imaginary component |
2021
|
|
|
|
|
|
|
# |
2022
|
|
|
|
|
|
|
# OK, I have my first solution. If it is complex *and* the original |
2023
|
|
|
|
|
|
|
# coefficients are all real, math guarantees me that the comjugate is also |
2024
|
|
|
|
|
|
|
# a solution. In that case, [synthetic] divide by this complex solution |
2025
|
|
|
|
|
|
|
# as well so we are left with a simple linear equation to solve. |
2026
|
|
|
|
|
|
|
# Otherwise (Either this one is real or we had complex coefficients) just |
2027
|
|
|
|
|
|
|
# [synthetic] divide by this solution and solve the rusulting quadratic |
2028
|
|
|
|
|
|
|
# quotient. |
2029
|
|
|
|
|
|
|
# |
2030
|
4
|
50
|
33
|
|
|
25
|
if ( (ref($solutions[0]) eq $class_cplx) # First solution is complex |
2031
|
|
|
|
|
|
|
&&($allreal) ) # and no complex coefficients |
2032
|
|
|
|
|
|
|
{ # Conjugate is also a solution |
2033
|
0
|
|
|
|
|
0
|
$solutions[1] = ~$solutions[0]; # from the original monic |
2034
|
0
|
|
|
|
|
0
|
($zero, $quotient_2) = $monic->Yapp_eval($solutions[0]); |
2035
|
|
|
|
|
|
|
# Had *better* evaluate to 0! |
2036
|
0
|
|
|
|
|
0
|
($zero, $quotient_1) = $quotient_2->Yapp_eval($solutions[1]); |
2037
|
|
|
|
|
|
|
|
2038
|
|
|
|
|
|
|
# $quotient_1 is a [ref to a] 1st degree polynomial. |
2039
|
|
|
|
|
|
|
# |
2040
|
0
|
|
|
|
|
0
|
($solutions[3]) = $quotient_1->Yapp_solve(); # This is kinda trivial |
2041
|
|
|
|
|
|
|
} |
2042
|
|
|
|
|
|
|
else # If I can't depend on conjugate, for what- |
2043
|
|
|
|
|
|
|
{ # ever reason, just divide this one out |
2044
|
4
|
|
|
|
|
18
|
($zero, $quotient_2) = $monic->Yapp_eval($solutions[0]); # -> quadratic |
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
# $quotient_2 is a quadratic expression. Just solve that. |
2047
|
|
|
|
|
|
|
# |
2048
|
4
|
|
|
|
|
13
|
@solutions[1,2] = $quotient_2->Yapp_solve(); |
2049
|
|
|
|
|
|
|
} |
2050
|
4
|
|
|
|
|
76
|
return @solutions; |
2051
|
|
|
|
|
|
|
} |
2052
|
|
|
|
|
|
|
# |
2053
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2054
|
|
|
|
|
|
|
sub Yapp_solve_quartic |
2055
|
|
|
|
|
|
|
{ # Much of the setup here is similar to that of Yapp_solve_cubic, but with |
2056
|
|
|
|
|
|
|
# tiny differences, enough to not try to make subroutins of this code. |
2057
|
3
|
|
|
3
|
0
|
6
|
my $self = shift(@_); |
2058
|
3
|
|
|
|
|
6
|
my @solutions; # I will return this array |
2059
|
|
|
|
|
|
|
my $monic; # Original Yapp with 1 as leading coefficient |
2060
|
0
|
|
|
|
|
0
|
my $reduced; # $monic with roots reduced, if needed |
2061
|
3
|
|
|
|
|
5
|
my $reduced_by = 0; # By how much to reduce the roots in order |
2062
|
|
|
|
|
|
|
# to eliminate the degree[2] term |
2063
|
3
|
|
|
|
|
5
|
my ($zero, $quotient_2, $quotient_1); # zero is mainly a placeholder. |
2064
|
|
|
|
|
|
|
# the two quotients are for after an |
2065
|
|
|
|
|
|
|
# evaluation to get the next lower |
2066
|
|
|
|
|
|
|
# degree polynomials. |
2067
|
|
|
|
|
|
|
|
2068
|
|
|
|
|
|
|
# 2 transformations: |
2069
|
|
|
|
|
|
|
# - Make sure leading coefficient is 1 (monic) |
2070
|
|
|
|
|
|
|
# - Reduce roots by 1/4 of the x^3 coefficient. This sets the x^3 coefficient |
2071
|
|
|
|
|
|
|
# (Which is always the negative of the sum of the roots) to 0 |
2072
|
|
|
|
|
|
|
# |
2073
|
3
|
50
|
|
|
|
10
|
if ($self->{coeff}[4] == 1) # If this is already a monic polynomial |
2074
|
|
|
|
|
|
|
{ |
2075
|
0
|
|
|
|
|
0
|
$monic = Yapp($self); # Use a copy before reducing roots |
2076
|
|
|
|
|
|
|
} |
2077
|
|
|
|
|
|
|
else |
2078
|
|
|
|
|
|
|
{ # Divide by leading coefficent |
2079
|
3
|
|
|
|
|
15
|
$monic = $self / ($self->{coeff}[4]); # to *make* it monic |
2080
|
3
|
50
|
|
|
|
14
|
printf("Self: <%s>\nMonic: <%s>\n", $self->Ysprint(), $monic->Ysprint()) |
2081
|
|
|
|
|
|
|
if ($testmode); #TESTMODE |
2082
|
|
|
|
|
|
|
} |
2083
|
3
|
50
|
|
|
|
12
|
if ($monic->{coeff}[3] != 0) # If we have a degree[3] term |
2084
|
|
|
|
|
|
|
{ |
2085
|
3
|
|
|
|
|
8
|
$reduced_by = -($monic->{coeff}[3])/4; # Reduce by -1/4 of x^3 term |
2086
|
3
|
|
|
|
|
12
|
$reduced = $monic->Yapp_reduce($reduced_by); |
2087
|
3
|
50
|
|
|
|
10
|
printf("Original <%s>, with roots reduced by <%.5f>\nYields: <%s>\n", |
2088
|
|
|
|
|
|
|
$self->Ysprint(), $reduced_by, $reduced->Ysprint()) #TESTMODE |
2089
|
|
|
|
|
|
|
if($testmode); |
2090
|
|
|
|
|
|
|
} |
2091
|
|
|
|
|
|
|
else |
2092
|
|
|
|
|
|
|
{ |
2093
|
0
|
|
|
|
|
0
|
$reduced = Yapp($monic); # Had no X^3 term. |
2094
|
|
|
|
|
|
|
} |
2095
|
|
|
|
|
|
|
# |
2096
|
|
|
|
|
|
|
# In Ferrari's algorithm, we transpose the X^2, X^1 and constant term to the |
2097
|
|
|
|
|
|
|
# right side. Of course, it is not expected to be perfect square. However, |
2098
|
|
|
|
|
|
|
# we can add a new variable, u, to the left side so that it becomes |
2099
|
|
|
|
|
|
|
# (1) (X^2 + u)^2. |
2100
|
|
|
|
|
|
|
# That is, add 2*u*X^2 + u^2 to the left side, keeping it a perfect square. |
2101
|
|
|
|
|
|
|
# The right side, with the transposed 3 terms, had been: |
2102
|
|
|
|
|
|
|
# (2) -cX^2 -d*X -e |
2103
|
|
|
|
|
|
|
# now becomes (after collecting terms): |
2104
|
|
|
|
|
|
|
# (3) (2u -c)X^2 -d*X + (u^2 -e) |
2105
|
|
|
|
|
|
|
# (3a) A B C |
2106
|
|
|
|
|
|
|
# Is this a perfect square? Well, that depends: Is *is* a quadradic |
2107
|
|
|
|
|
|
|
# expression but it *can* be a perfect square if is discriminant is 0; |
2108
|
|
|
|
|
|
|
# that is: B^2 - 4*A*C, which is an expression containing that unknown u |
2109
|
|
|
|
|
|
|
# term, can be set to 0. That is: |
2110
|
|
|
|
|
|
|
# (4) (-d)^2 - 4*(2u -c)*(u^2 -e) == 0 |
2111
|
|
|
|
|
|
|
# Multiplying out and collecting terms, this becomes: |
2112
|
|
|
|
|
|
|
# (4a) 8*u^3 -4*c*u^2 -8*e*u +(d^2 -4c*e) |
2113
|
|
|
|
|
|
|
# A cubic equation in the unknown u. This is called the resolvent cubic |
2114
|
|
|
|
|
|
|
# of the original polynomial. (The last two terms in parentheses comprise |
2115
|
|
|
|
|
|
|
# the constant of that cubic equation.) |
2116
|
|
|
|
|
|
|
# The point is that: Any solution to (4a) can be plugged back into (3) to |
2117
|
|
|
|
|
|
|
# make it a perfect square to match against the perfect square left hand side |
2118
|
|
|
|
|
|
|
# (LHS) in (1). |
2119
|
|
|
|
|
|
|
# Here goes! |
2120
|
|
|
|
|
|
|
# |
2121
|
3
|
|
|
|
|
8
|
my $c = $reduced->{coeff}[2]; # X^2 coefficient |
2122
|
3
|
|
|
|
|
8
|
my $d = $reduced->{coeff}[1]; # X^1 coefficient |
2123
|
3
|
|
|
|
|
6
|
my $e = $reduced->{coeff}[0]; # Constant term |
2124
|
|
|
|
|
|
|
|
2125
|
3
|
|
|
|
|
6
|
my @rca = (); # Array that will generate the resolvent cubic equation |
2126
|
3
|
|
|
|
|
16
|
push @rca, 4*$c*$e - $d**2; # Constant term |
2127
|
3
|
|
|
|
|
7
|
push @rca, -8*$e; # u^1 term |
2128
|
3
|
|
|
|
|
5
|
push @rca, -4*$c; # u^2 term |
2129
|
3
|
|
|
|
|
4
|
push @rca, 8; # u^3 term |
2130
|
3
|
|
|
|
|
7
|
my $rc = Yapp(\@rca); # Turn that into a polynomial |
2131
|
3
|
|
|
|
|
10
|
$rc->variable("u"); # Just for clarity: With variable letter u |
2132
|
3
|
|
|
|
|
9
|
my @rc_solutions = $rc->Yapp_solve(); # (Obvious purpose) |
2133
|
3
|
|
|
|
|
6
|
my $rc_plug; # The one we wil plug in to (3) |
2134
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
# Now life is easier (with fewer rounding errors) if I choose a real solution. |
2136
|
|
|
|
|
|
|
# I can't be sure it won't return a complex with a *very* low Im part (on the |
2137
|
|
|
|
|
|
|
# order of 10^-15 ir so) due to rounding errors. So I merely look for a root |
2138
|
|
|
|
|
|
|
# with # sufficiently low Im part that I would ignore it. |
2139
|
|
|
|
|
|
|
# |
2140
|
3
|
|
|
|
|
8
|
foreach my $rc_val (@rc_solutions) #(Had used $rc_plug here but it went |
2141
|
|
|
|
|
|
|
{ # undefined after loop exit. Hence |
2142
|
3
|
|
|
|
|
4
|
$rc_plug = $rc_val; # using throwaway variable $rv_val |
2143
|
3
|
50
|
|
|
|
13
|
last if (ref($rc_plug) ne $class_cplx); # It came back as a real! Use it |
2144
|
|
|
|
|
|
|
} |
2145
|
|
|
|
|
|
|
# |
2146
|
|
|
|
|
|
|
# Truth be known, *any* one of the roots is usable but I would prefer a real |
2147
|
|
|
|
|
|
|
# Now plug the found value, $rc_plug, back into (3); |
2148
|
|
|
|
|
|
|
# Reminder: (3) (2u -c)X^2 -d*X + (u^2 -e) |
2149
|
|
|
|
|
|
|
# |
2150
|
3
|
|
|
|
|
5
|
my @rhsa = (); # Array that will become the above quadratic |
2151
|
3
|
|
|
|
|
7
|
push @rhsa, ($rc_plug**2 -$e); # Constant term of above quadratic |
2152
|
3
|
|
|
|
|
7
|
push @rhsa, -$d; # X^1 term of the quadratic is original |
2153
|
|
|
|
|
|
|
# X^1 term of the root-reduced monic |
2154
|
3
|
|
|
|
|
7
|
push @rhsa, (2*$rc_plug -$c); # The X^2 term of above quadratic |
2155
|
3
|
50
|
|
|
|
9
|
if ($testmode) |
2156
|
|
|
|
|
|
|
{ |
2157
|
0
|
|
|
|
|
0
|
my $rhs = Yapp(\@rhsa); #(Actually, it is not necessary to generate |
2158
|
|
|
|
|
|
|
# this right-had-side polynomial.) |
2159
|
0
|
|
|
|
|
0
|
printf("RHS Yapp is <%s>\n", $rhs->Ysprint); |
2160
|
|
|
|
|
|
|
} |
2161
|
|
|
|
|
|
|
|
2162
|
|
|
|
|
|
|
# Testing has confirmed that $rhs is indeed a quadratric perfect square. |
2163
|
|
|
|
|
|
|
# and that it is the square of yapp(sqrt(c), sqrt(a)) |
2164
|
|
|
|
|
|
|
# |
2165
|
3
|
|
|
|
|
12
|
my $c_sqrt = sqrt($rhsa[0]); # Since the $rhs polynomial is a square |
2166
|
3
|
|
|
|
|
60
|
my $a_sqrt = sqrt($rhsa[2]); # I'm guaranteed these are positive. |
2167
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
# The above quadratic polynomial is the square of either: |
2169
|
|
|
|
|
|
|
# * +$c_sqrt*X+$a_sqrt or |
2170
|
|
|
|
|
|
|
# * -$c_sqrt*X-$a_sqrt |
2171
|
|
|
|
|
|
|
# |
2172
|
|
|
|
|
|
|
# Now the perfect square on left-had side, is the square of (X^2 + U) and |
2173
|
|
|
|
|
|
|
# so the binomia itself may be set equal to either of these: |
2174
|
|
|
|
|
|
|
# X^2 + U = AX + C or: X^2 -AX + (U - C) = 0 |
2175
|
|
|
|
|
|
|
# X^2 + U = -Ax - C or: X^2 +AX + (U + C) = 0 |
2176
|
|
|
|
|
|
|
# Where: |
2177
|
|
|
|
|
|
|
# * U is $rc_plug |
2178
|
|
|
|
|
|
|
# * A is $a_sqrt |
2179
|
|
|
|
|
|
|
# * C is $c_sqrt |
2180
|
|
|
|
|
|
|
# Solve these two quadratics and I have *almost* solved the original quartic. |
2181
|
|
|
|
|
|
|
# |
2182
|
3
|
|
|
|
|
26
|
my @quad1_a = ( ($rc_plug - $c_sqrt), -$a_sqrt, 1); # Well, in order to |
2183
|
3
|
|
|
|
|
9
|
my @quad2_a = ( ($rc_plug + $c_sqrt), $a_sqrt, 1); # them, it helps to |
2184
|
3
|
|
|
|
|
8
|
my $quad1 = Yapp(\@quad1_a); # generate them |
2185
|
3
|
|
|
|
|
8
|
my $quad2 = Yapp(\@quad2_a); |
2186
|
3
|
50
|
|
|
|
11
|
printf("quad1 = <%s>;\nquad2 = <%s>\n", $quad1->Ysprint, $quad2->Ysprint) |
2187
|
|
|
|
|
|
|
if ($testmode); |
2188
|
3
|
|
|
|
|
14
|
@solutions[0..1] = $quad1->Yapp_solve(); |
2189
|
3
|
|
|
|
|
11
|
@solutions[2..3] = $quad2->Yapp_solve(); |
2190
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
# Not there yet: Remember, I reduced the roots to produce the monic. Now |
2192
|
|
|
|
|
|
|
# undo that: |
2193
|
|
|
|
|
|
|
# |
2194
|
3
|
|
|
|
|
11
|
@solutions = map {$_ + $reduced_by} @solutions; # Restore what has been |
|
12
|
|
|
|
|
999
|
|
2195
|
|
|
|
|
|
|
# taken from them |
2196
|
3
|
|
|
|
|
382
|
return @solutions; |
2197
|
|
|
|
|
|
|
} |
2198
|
|
|
|
|
|
|
# |
2199
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2200
|
|
|
|
|
|
|
# Yapp_solve_odd(): Method to solve polynomial equations of odd degree >= 5 |
2201
|
|
|
|
|
|
|
# with all real coeficcients. |
2202
|
|
|
|
|
|
|
# |
2203
|
|
|
|
|
|
|
# Parameter: |
2204
|
|
|
|
|
|
|
# - [Ref to] a Yapp polynomial |
2205
|
|
|
|
|
|
|
# Returns: |
2206
|
|
|
|
|
|
|
# - Array of solution set |
2207
|
|
|
|
|
|
|
# Note: Initially, this function supports only polynomials with real-only |
2208
|
|
|
|
|
|
|
# coefficients, although I am leaving a stub block for handling complex |
2209
|
|
|
|
|
|
|
# coefficients as well. |
2210
|
|
|
|
|
|
|
# |
2211
|
|
|
|
|
|
|
sub Yapp_solve_odd |
2212
|
|
|
|
|
|
|
{ |
2213
|
1
|
|
|
1
|
0
|
2
|
my $self = shift(@_); |
2214
|
1
|
|
|
|
|
3
|
my $crude_margin = 0.1; # Just get close enough to start using Newton |
2215
|
1
|
|
|
|
|
2
|
my @solutions = (); # Array that I return to the caller |
2216
|
|
|
|
|
|
|
|
2217
|
|
|
|
|
|
|
# The following pair of betw variables will be set during a bisection |
2218
|
|
|
|
|
|
|
# algorithm and used for initial values once we start wuth Newton's method. |
2219
|
|
|
|
|
|
|
# Hence, we need to define them waaaay before they will be used, in order to |
2220
|
|
|
|
|
|
|
# find them in scope anyplace in this function. |
2221
|
|
|
|
|
|
|
# |
2222
|
1
|
|
|
|
|
2
|
my $betw_try; # X-value at midpoint of an interval |
2223
|
|
|
|
|
|
|
my $betw_eval; # Y-value at that X midpoint |
2224
|
|
|
|
|
|
|
|
2225
|
|
|
|
|
|
|
# Find one real root and check if it is duplicated. |
2226
|
|
|
|
|
|
|
# Set up for a bisection algorithm: Starting with some initial guess at |
2227
|
|
|
|
|
|
|
# 0, 1, and maybe -1, start doubling the interval until I get a + on one |
2228
|
|
|
|
|
|
|
# side and - on the other. Then start bisecting until I get pretty |
2229
|
|
|
|
|
|
|
# close, like within 1/10 or so. Then start using Newton. |
2230
|
|
|
|
|
|
|
# |
2231
|
1
|
|
|
|
|
2
|
my $left_try = 0.0; # First & easiest point to test is X = 0 |
2232
|
1
|
|
|
|
|
4
|
my $left_eval = $self->{coeff}[0]; # 'cause I don't need to evaluate |
2233
|
1
|
|
|
|
|
3
|
my $right_try = 1.0; |
2234
|
1
|
|
|
|
|
4
|
my $right_eval = $self->Yapp_eval($right_try); |
2235
|
|
|
|
|
|
|
|
2236
|
|
|
|
|
|
|
# Search for an interval such that the Yapp evaluates to opposite signs |
2237
|
|
|
|
|
|
|
# at the two endpoints of the interval |
2238
|
|
|
|
|
|
|
# |
2239
|
1
|
|
|
|
|
6
|
until (($left_eval * $right_eval) < 0) |
2240
|
|
|
|
|
|
|
{ # Entering loop body [again] means both tries evauated to the same |
2241
|
|
|
|
|
|
|
# sign. Sweep wider intervals.. |
2242
|
|
|
|
|
|
|
# |
2243
|
3
|
100
|
|
|
|
8
|
if ($left_try == 0.0) # At first round of this loop, don't double |
2244
|
|
|
|
|
|
|
{ # both enpoints of the interval; |
2245
|
1
|
|
|
|
|
3
|
$left_try = -1.0; # just expand in negative direction |
2246
|
|
|
|
|
|
|
} |
2247
|
|
|
|
|
|
|
else # But if we are well into the round |
2248
|
|
|
|
|
|
|
{ # we will double both enpoints of the interval |
2249
|
2
|
|
|
|
|
2
|
$left_try *= 2; |
2250
|
2
|
|
|
|
|
3
|
$right_try *= 2; |
2251
|
|
|
|
|
|
|
} |
2252
|
|
|
|
|
|
|
# Either way, re-evaluate at both endpoints. (OK, one wasted, |
2253
|
|
|
|
|
|
|
# repeated evaluateion at $right_try == 1). Minor, IMO |
2254
|
|
|
|
|
|
|
# |
2255
|
3
|
|
|
|
|
7
|
$left_eval = $self->Yapp_eval($left_try); |
2256
|
3
|
|
|
|
|
7
|
$right_eval = $self->Yapp_eval($right_try); |
2257
|
|
|
|
|
|
|
} |
2258
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
# OK, we have found X-values that evaluate to opposite signs. Now start |
2260
|
|
|
|
|
|
|
# bisecting scheme. |
2261
|
|
|
|
|
|
|
# |
2262
|
1
|
|
|
|
|
6
|
until (abs($right_try - $left_try) <= $crude_margin) |
2263
|
|
|
|
|
|
|
{ |
2264
|
4
|
|
|
|
|
8
|
$betw_try = ($left_try + $right_try)/2 ; # Bisect the interval |
2265
|
4
|
|
|
|
|
10
|
$betw_eval = $self->Yapp_eval($betw_try); # Evaluate @ midpoint |
2266
|
4
|
100
|
|
|
|
14
|
last if ($betw_eval == 0); # Hey! We hit an exact root! |
2267
|
3
|
50
|
|
|
|
8
|
my $left_sign = $left_eval >= 0 ? 1 : -1; # and determine the signs |
2268
|
3
|
50
|
|
|
|
9
|
my $right_sign = $right_eval >= 0 ? 1 : -1; # at the endpoints and |
2269
|
3
|
100
|
|
|
|
7
|
my $betw_sign = $betw_eval >= 0 ? 1 : -1; # midpoint |
2270
|
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
# Now mathematically, the sign at the midpoint evaluation must match |
2272
|
|
|
|
|
|
|
# exactly one (never both) of the endpoint evaluation signs. Ergo, it |
2273
|
|
|
|
|
|
|
# must be be different from exactly one of the endpoint evaluations |
2274
|
|
|
|
|
|
|
# |
2275
|
3
|
100
|
|
|
|
10
|
if ($left_sign != $betw_sign) # Changes sign to left |
2276
|
|
|
|
|
|
|
{ # of the midpoint |
2277
|
1
|
|
|
|
|
2
|
$right_try = $betw_try; # Then move right end to |
2278
|
1
|
|
|
|
|
3
|
$right_eval = $betw_eval; # middle, with its evaluate |
2279
|
|
|
|
|
|
|
} |
2280
|
|
|
|
|
|
|
else # Changed sign to right |
2281
|
|
|
|
|
|
|
{ # of the midpoint |
2282
|
2
|
|
|
|
|
3
|
$left_try = $betw_try; # Then move left end to |
2283
|
2
|
|
|
|
|
6
|
$left_eval = $betw_eval; # middle, with its evaluate |
2284
|
|
|
|
|
|
|
} |
2285
|
|
|
|
|
|
|
} |
2286
|
|
|
|
|
|
|
|
2287
|
|
|
|
|
|
|
# OK, however we got here, we are now close enough to a solution to start |
2288
|
|
|
|
|
|
|
# using the Newton method. That is, unless we actually hit an exact root! |
2289
|
|
|
|
|
|
|
# |
2290
|
1
|
50
|
|
|
|
5
|
$solutions[0] = ($betw_eval == 0.0) ? # Start at latest midpoint |
2291
|
|
|
|
|
|
|
$betw_try : $self->Yapp_newton($betw_try); |
2292
|
1
|
|
|
|
|
4
|
my ($zero, $quotient) = $self->Yapp_eval($solutions[0]); |
2293
|
|
|
|
|
|
|
#(A good debugging step would be to check |
2294
|
|
|
|
|
|
|
# that $zero is indeed 0.0) |
2295
|
1
|
|
|
|
|
3
|
my $xn = $solutions[0]; #(Shorter var name for neater code) |
2296
|
|
|
|
|
|
|
|
2297
|
|
|
|
|
|
|
# So $xn is a root. Question: Is it a multiple root? |
2298
|
|
|
|
|
|
|
# |
2299
|
1
|
|
|
|
|
6
|
while($quotient->{degree} > 0) # Keep reducing it as we loop |
2300
|
|
|
|
|
|
|
{ |
2301
|
1
|
|
|
|
|
4
|
my ($zzero, $quotient2) = $quotient->Yapp_eval($xn); # Solves quotient? |
2302
|
1
|
50
|
|
|
|
8
|
last if (abs($zzero) >= $margin); # Clearly not. Done checking; Outahere! |
2303
|
|
|
|
|
|
|
|
2304
|
|
|
|
|
|
|
# Still here: It is a solution again! |
2305
|
|
|
|
|
|
|
# |
2306
|
0
|
|
|
|
|
0
|
$quotient = $quotient2; # This is next quotient to use |
2307
|
0
|
|
|
|
|
0
|
push (@solutions, $xn); # and add same root into solutions set |
2308
|
|
|
|
|
|
|
} |
2309
|
|
|
|
|
|
|
# OK, I've exhausted my repeated copies of first root. |
2310
|
|
|
|
|
|
|
# If anything's left, solve it separately. But first: How many times was |
2311
|
|
|
|
|
|
|
# that first root repeated? |
2312
|
|
|
|
|
|
|
# |
2313
|
|
|
|
|
|
|
#?my $rest_roots = @solutions; # This will be the starting index |
2314
|
|
|
|
|
|
|
# for a newton-refinement for the |
2315
|
|
|
|
|
|
|
# remaining roots. |
2316
|
1
|
50
|
|
|
|
4
|
if ($quotient->{degree} > 0) |
2317
|
|
|
|
|
|
|
{ # as repeated and conjugate roots |
2318
|
1
|
|
|
|
|
5
|
my @q_solutions = $quotient->Yapp_solve(); # Solve the deflated polynomial |
2319
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
# Now, to compensate for additional rounding errors that creep in when |
2321
|
|
|
|
|
|
|
# solving the lower-degree quotient polynomials, let's up the accuracy; |
2322
|
|
|
|
|
|
|
# apply Newtons's method to the roots returned by the recursive calls. |
2323
|
|
|
|
|
|
|
# Laguerre has already been applied to this first root (plus duplicates |
2324
|
|
|
|
|
|
|
# and conjugates). |
2325
|
|
|
|
|
|
|
# |
2326
|
1
|
|
|
|
|
3
|
@q_solutions = map {$self->Yapp_newton($_)} @q_solutions; |
|
4
|
|
|
|
|
13
|
|
2327
|
1
|
|
|
|
|
9
|
push (@solutions, @q_solutions); # THOSE solutions go into my set |
2328
|
|
|
|
|
|
|
} |
2329
|
1
|
|
|
|
|
14
|
return @solutions; |
2330
|
|
|
|
|
|
|
} |
2331
|
|
|
|
|
|
|
# |
2332
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2333
|
|
|
|
|
|
|
# Yapp_solve_even(): Method to solve polynomial equations of even degree >= 6 |
2334
|
|
|
|
|
|
|
# with real-only coefficients |
2335
|
|
|
|
|
|
|
# Also good for solving polynomials with complex coefficients; the only |
2336
|
|
|
|
|
|
|
# difference is that for real-ony, the conjugate of of a complex root is |
2337
|
|
|
|
|
|
|
# guaranteed by theorem to be a root as well. |
2338
|
|
|
|
|
|
|
# |
2339
|
|
|
|
|
|
|
# Parameter: |
2340
|
|
|
|
|
|
|
# - [Ref to] a Yapp polynomial |
2341
|
|
|
|
|
|
|
# Returns: |
2342
|
|
|
|
|
|
|
# - Array of solution set |
2343
|
|
|
|
|
|
|
# |
2344
|
|
|
|
|
|
|
sub Yapp_solve_even |
2345
|
|
|
|
|
|
|
{ |
2346
|
1
|
|
|
1
|
0
|
3
|
my $self = shift(@_); |
2347
|
1
|
|
|
|
|
3
|
my @solutions = (); # Array that I return to the caller |
2348
|
|
|
|
|
|
|
|
2349
|
1
|
|
|
|
|
5
|
my $start = cplx(.1, .1); # Dumb starting point but we gotta start someplace! |
2350
|
1
|
|
|
|
|
63
|
$solutions[0] = $self->Yapp_laguerre($start); |
2351
|
1
|
|
|
|
|
5
|
my ($zero, $quotient) = $self->Yapp_eval($solutions[0]); # Deflate a degree |
2352
|
1
|
|
|
|
|
3
|
my ($zzero, $quotient2); # To test for repeated roots |
2353
|
1
|
|
|
|
|
3
|
my $xn = $solutions[0]; #(Shorter var name for neater code) |
2354
|
|
|
|
|
|
|
|
2355
|
|
|
|
|
|
|
# So $xn is a root. 3 questions: |
2356
|
|
|
|
|
|
|
# 1. Is it a complex root? If yes, we can just use its conjugate & # deflate |
2357
|
|
|
|
|
|
|
# 2. Is it a multiple complex root? |
2358
|
|
|
|
|
|
|
# 3. If real, is it a multiple real root? |
2359
|
|
|
|
|
|
|
# |
2360
|
1
|
50
|
|
|
|
5
|
if (ref($xn) eq $class_cplx) # Yes to question[1]: Is complex |
2361
|
|
|
|
|
|
|
{ # We have a work-free next solution |
2362
|
1
|
|
|
|
|
6
|
push(@solutions, ~$xn); # The conjugate is also a root: Stash it |
2363
|
1
|
|
|
|
|
66
|
($zero, $quotient) = $quotient->Yapp_eval(~$xn); # and deflate a degree |
2364
|
|
|
|
|
|
|
# $zero == 0; toss it |
2365
|
|
|
|
|
|
|
|
2366
|
|
|
|
|
|
|
# Now start checking for repeats of this complex root. (Question[2]) |
2367
|
|
|
|
|
|
|
# |
2368
|
1
|
|
|
|
|
21
|
while($quotient->{degree} > 0) # Keep reducing it as we loop |
2369
|
|
|
|
|
|
|
{ |
2370
|
|
|
|
|
|
|
# Question 2 rephrased: Does the same complex number solve the quotient? |
2371
|
1
|
|
|
|
|
4
|
($zzero, $quotient2) = $quotient->Yapp_eval($xn); # Evaluate to find out |
2372
|
1
|
50
|
|
|
|
4
|
last if (abs($zzero) >= $margin); # Clearly not. Done checking; Outahere! |
2373
|
|
|
|
|
|
|
|
2374
|
|
|
|
|
|
|
# Still here: Same complex number *is* a solution again! |
2375
|
|
|
|
|
|
|
# |
2376
|
0
|
|
|
|
|
0
|
push (@solutions, $xn); # First, push same root into solutions set |
2377
|
0
|
|
|
|
|
0
|
$quotient = $quotient2; # Point $quotient to above-deflated version |
2378
|
|
|
|
|
|
|
# Of course, its conjugate is also a root |
2379
|
0
|
|
|
|
|
0
|
push (@solutions, ~$xn); # Push its conjugate again into @solutions |
2380
|
0
|
|
|
|
|
0
|
($zero, $quotient) # and deflate by the conjugate root |
2381
|
|
|
|
|
|
|
= $quotient->Yapp_eval(~$xn); |
2382
|
|
|
|
|
|
|
} |
2383
|
|
|
|
|
|
|
} |
2384
|
|
|
|
|
|
|
else # Solution is real. No free conjugate ride |
2385
|
|
|
|
|
|
|
{ |
2386
|
0
|
|
|
|
|
0
|
while($quotient->{degree} > 0) # Keep reducing it as we loop |
2387
|
|
|
|
|
|
|
{ |
2388
|
0
|
|
|
|
|
0
|
($zzero, $quotient2) = $quotient->Yapp_eval($xn); # Also solves quotient? |
2389
|
0
|
0
|
|
|
|
0
|
last if (abs($zzero) >= $margin); # Clearly not. Done checking; Outahere! |
2390
|
|
|
|
|
|
|
|
2391
|
|
|
|
|
|
|
# Still here: Same real is a solution again! |
2392
|
|
|
|
|
|
|
# |
2393
|
0
|
|
|
|
|
0
|
$quotient = $quotient2; # This is next deflated quotient |
2394
|
0
|
|
|
|
|
0
|
push (@solutions, $xn); # and add same root into solutions set |
2395
|
|
|
|
|
|
|
} |
2396
|
|
|
|
|
|
|
} |
2397
|
|
|
|
|
|
|
# OK, I've exhausted my repeated and/or conjugate copies of first root. |
2398
|
|
|
|
|
|
|
# If anything's left, solve it separately. |
2399
|
|
|
|
|
|
|
# |
2400
|
1
|
50
|
|
|
|
17
|
if ($quotient->{degree} > 0) # If I have not solved for all roots |
2401
|
|
|
|
|
|
|
{ # as repeated and conjugate roots |
2402
|
1
|
|
|
|
|
6
|
my @q_solutions = $quotient->Yapp_solve(); # Solve the deflated polynomial |
2403
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
# Now, to compensate for additional rounding errors that creep in when |
2405
|
|
|
|
|
|
|
# solving the lower-degree quotient polynomials, let's up the accuracy; |
2406
|
|
|
|
|
|
|
# apply Newtons's method to the roots returned by the recursive calls. |
2407
|
|
|
|
|
|
|
# Laguerre has already been applied to this first root (plus duplicates |
2408
|
|
|
|
|
|
|
# and conjugates). |
2409
|
|
|
|
|
|
|
# |
2410
|
1
|
|
|
|
|
3
|
@q_solutions = map {$self->Yapp_newton($_)} @q_solutions; |
|
4
|
|
|
|
|
14
|
|
2411
|
1
|
|
|
|
|
6
|
push (@solutions, @q_solutions); # THOSE solutions go into my set |
2412
|
|
|
|
|
|
|
} |
2413
|
|
|
|
|
|
|
|
2414
|
1
|
|
|
|
|
24
|
return @solutions; |
2415
|
|
|
|
|
|
|
} |
2416
|
|
|
|
|
|
|
# |
2417
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2418
|
|
|
|
|
|
|
# Yapp_solve_complex(): Method to solve for the zeroes of a polynomial with |
2419
|
|
|
|
|
|
|
# complex coeffiecients. The theorem about roots in conjugate pairs flies |
2420
|
|
|
|
|
|
|
# out the window. |
2421
|
|
|
|
|
|
|
# Same parameter/returns as all others. |
2422
|
|
|
|
|
|
|
# |
2423
|
|
|
|
|
|
|
sub Yapp_solve_complex |
2424
|
|
|
|
|
|
|
{ |
2425
|
0
|
|
|
0
|
0
|
0
|
my $self = shift(@_); |
2426
|
0
|
|
|
|
|
0
|
my @solutions = (); # Array that I return to the caller |
2427
|
|
|
|
|
|
|
|
2428
|
0
|
|
|
|
|
0
|
my $start = cplx(.1, .1); # Dumb starting point but we gotta start someplace! |
2429
|
0
|
|
|
|
|
0
|
$solutions[0] = $self->Yapp_laguerre($start); |
2430
|
0
|
|
|
|
|
0
|
my ($zero, $quotient) = $self->Yapp_eval($solutions[0]); # Deflate a degree |
2431
|
0
|
|
|
|
|
0
|
my ($zzero, $quotient2); # To test for repeated roots |
2432
|
0
|
|
|
|
|
0
|
my ($conj_zero, $conj_quotient); # To test if conjugate is also a root |
2433
|
0
|
|
|
|
|
0
|
my $xn = $solutions[0]; #(Shorter var name for neater code) |
2434
|
|
|
|
|
|
|
|
2435
|
|
|
|
|
|
|
# So $xn is a root. 3 questions: |
2436
|
|
|
|
|
|
|
# 1. Is it a complex root? If yes, we can just use its conjugate & # deflate |
2437
|
|
|
|
|
|
|
# 2. Is it a multiple complex root? |
2438
|
|
|
|
|
|
|
# 3. If real, is it a multiple real root? |
2439
|
|
|
|
|
|
|
# |
2440
|
0
|
0
|
|
|
|
0
|
if (ref($xn) eq $class_cplx) # Yes to question[1]: Is complex |
2441
|
|
|
|
|
|
|
{ # We don't have a work-free next solution |
2442
|
0
|
|
|
|
|
0
|
($conj_zero, $conj_quotient) # Check: Is conjugate is also a root? |
2443
|
|
|
|
|
|
|
= $quotient->Yapp_eval(~$xn); # Create a copy deflated by conjugate |
2444
|
0
|
0
|
|
|
|
0
|
if (abs($conj_zero) < $margin) # If conjugate is also a root (Not |
2445
|
|
|
|
|
|
|
{ # likely with complex coefficients) |
2446
|
0
|
|
|
|
|
0
|
push(@solutions, ~$xn); # Aha! Conjugate is also a solution |
2447
|
0
|
|
|
|
|
0
|
$quotient = $conj_quotient; # Now $quotient is the deflated Yapp |
2448
|
|
|
|
|
|
|
} |
2449
|
|
|
|
|
|
|
|
2450
|
|
|
|
|
|
|
# Now start checking for repeats of this complex root. (Question[2]) |
2451
|
|
|
|
|
|
|
# |
2452
|
0
|
|
|
|
|
0
|
while($quotient->{degree} > 0) # Keep reducing it as we loop |
2453
|
|
|
|
|
|
|
{ |
2454
|
0
|
|
|
|
|
0
|
($zzero, $quotient2) = $quotient->Yapp_eval($xn); # Solves quotient? |
2455
|
0
|
0
|
|
|
|
0
|
last if (abs($zzero) >= $margin); # Clearly not. Done checking; Outahere! |
2456
|
|
|
|
|
|
|
|
2457
|
|
|
|
|
|
|
# Still here: Same complex number *is* a solution again! |
2458
|
|
|
|
|
|
|
# |
2459
|
0
|
|
|
|
|
0
|
push (@solutions, $xn); # First, push same root into solutions set |
2460
|
0
|
|
|
|
|
0
|
$quotient = $quotient2; # Point $quotient to above-deflated version |
2461
|
|
|
|
|
|
|
# But is its conjugate also a root? |
2462
|
0
|
|
|
|
|
0
|
($conj_zero, $conj_quotient) # Is conjugate is also a root? Create |
2463
|
|
|
|
|
|
|
= $quotient->Yapp_eval(~$xn); # a copy deflated by conjugate |
2464
|
0
|
0
|
|
|
|
0
|
if (abs($conj_zero) < $margin) # If conjugate is also a root (Not |
2465
|
|
|
|
|
|
|
{ # likely with complex coefficients) |
2466
|
0
|
|
|
|
|
0
|
push(@solutions, ~$xn); # Aha! Conjugate is also a solution |
2467
|
0
|
|
|
|
|
0
|
$quotient = $conj_quotient; # Now $quotient is the deflated Yapp |
2468
|
|
|
|
|
|
|
} |
2469
|
|
|
|
|
|
|
} |
2470
|
|
|
|
|
|
|
} |
2471
|
|
|
|
|
|
|
else # Solution is real; just check for duplicate |
2472
|
|
|
|
|
|
|
{ |
2473
|
0
|
|
|
|
|
0
|
while($quotient->{degree} > 0) # Keep reducing it as we loop |
2474
|
|
|
|
|
|
|
{ |
2475
|
0
|
|
|
|
|
0
|
($zzero, $quotient2) = $quotient->Yapp_eval($xn); # Also solves quotient? |
2476
|
0
|
0
|
|
|
|
0
|
last if (abs($zzero) >= $margin); # Clearly not. Done checking; Outahere! |
2477
|
|
|
|
|
|
|
|
2478
|
|
|
|
|
|
|
# Still here: Same real is a solution again! |
2479
|
|
|
|
|
|
|
# |
2480
|
0
|
|
|
|
|
0
|
$quotient = $quotient2; # This is next deflated quotient |
2481
|
0
|
|
|
|
|
0
|
push (@solutions, $xn); # and add same root into solutions set |
2482
|
|
|
|
|
|
|
} |
2483
|
|
|
|
|
|
|
} |
2484
|
|
|
|
|
|
|
# OK, I've exhausted my repeated and/or conjugate copies of first root. |
2485
|
|
|
|
|
|
|
# If anything's left, solve it separately. |
2486
|
|
|
|
|
|
|
# |
2487
|
0
|
0
|
|
|
|
0
|
if ($quotient->{degree} > 0) # If I have not solved for all roots |
2488
|
|
|
|
|
|
|
{ # as repeated and conjugate roots |
2489
|
0
|
|
|
|
|
0
|
my @q_solutions = $quotient->Yapp_solve(); # Solve the deflated polynomial |
2490
|
|
|
|
|
|
|
|
2491
|
|
|
|
|
|
|
# Now, to compensate for additional rounding errors that creep in when |
2492
|
|
|
|
|
|
|
# solving the lower-degree quotient polynomials, let's up the accuracy; |
2493
|
|
|
|
|
|
|
# apply Newtons's method to the roots returned by the recursive calls. |
2494
|
|
|
|
|
|
|
# Laguerre has already been applied to this first root (plus duplicates |
2495
|
|
|
|
|
|
|
# and conjugates). |
2496
|
|
|
|
|
|
|
# |
2497
|
0
|
|
|
|
|
0
|
@q_solutions = map {$self->Yapp_newton($_)} @q_solutions; |
|
0
|
|
|
|
|
0
|
|
2498
|
0
|
|
|
|
|
0
|
push (@solutions, @q_solutions); # THOSE solutions go into my set |
2499
|
|
|
|
|
|
|
} |
2500
|
|
|
|
|
|
|
|
2501
|
0
|
|
|
|
|
0
|
return @solutions; |
2502
|
|
|
|
|
|
|
} |
2503
|
|
|
|
|
|
|
# |
2504
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2505
|
|
|
|
|
|
|
# Interpolation: Effectively a new kind of constuctor, though not pacakged |
2506
|
|
|
|
|
|
|
# as such. The parameters will always be references to arrays of numbers. |
2507
|
|
|
|
|
|
|
# The 0th array is a set of X values, the 1st array is a set of Y values. |
2508
|
|
|
|
|
|
|
# This is sufficient for laGrange interpolation - the basic colocation |
2509
|
|
|
|
|
|
|
# polynomial, Additional arrays are for first and successive derivatives. |
2510
|
|
|
|
|
|
|
# The goal is to generate a polynomial of minimum degree whose Y values and |
2511
|
|
|
|
|
|
|
# derivatives at the indicated X-values match the arrays. All will be |
2512
|
|
|
|
|
|
|
# called via the function Yapp_interpolate(). This will call Yapp_lagrange() |
2513
|
|
|
|
|
|
|
# or Yapp_hermite() as the case requires. |
2514
|
|
|
|
|
|
|
# Calling sequence: |
2515
|
|
|
|
|
|
|
# $yap_interp = Yapp_interpolate(\@x_vals, \@y_vals [,\@d1_vals, # \@d2_vals]); |
2516
|
|
|
|
|
|
|
# Returns: |
2517
|
|
|
|
|
|
|
# - A polynomial whose y values and derivatives match the input. |
2518
|
|
|
|
|
|
|
# |
2519
|
|
|
|
|
|
|
sub Yapp_interpolate |
2520
|
|
|
|
|
|
|
{ |
2521
|
2
|
|
|
2
|
1
|
2644
|
my $self = Yapp("1"); # Create a 0-degree polynomial - just 1 |
2522
|
2
|
|
|
|
|
2
|
my $ryapp; # Polynomial to be returned to caller |
2523
|
|
|
|
|
|
|
|
2524
|
|
|
|
|
|
|
# One validation: All arrays must have the name number of elements |
2525
|
|
|
|
|
|
|
|
2526
|
2
|
|
|
|
|
9
|
my $pcount = @_; # Number of arrays passed to me |
2527
|
2
|
50
|
|
|
|
12
|
die "Must supply at least two arrays (X and Y values) for interpolation" |
2528
|
|
|
|
|
|
|
unless $pcount >= 2; |
2529
|
|
|
|
|
|
|
|
2530
|
2
|
|
|
|
|
3
|
my $x_vals = $_[0]; # Get -> X-values array |
2531
|
2
|
|
|
|
|
2
|
my $xcount = @{$x_vals}; # How many X-values were given? |
|
2
|
|
|
|
|
5
|
|
2532
|
|
|
|
|
|
|
|
2533
|
|
|
|
|
|
|
# Validate that all arrays have the same number of elements |
2534
|
|
|
|
|
|
|
# |
2535
|
2
|
|
|
|
|
8
|
for (my $plc = 1; $plc < $pcount; $plc++) |
2536
|
|
|
|
|
|
|
{ |
2537
|
3
|
|
|
|
|
5
|
my $y_list = $_[$plc]; # -> next array in parameter list |
2538
|
3
|
|
|
|
|
5
|
my $ycount = @{$y_list}; # How many elements does this Y array have? |
|
3
|
|
|
|
|
4
|
|
2539
|
3
|
50
|
|
|
|
13
|
die "Y-list[$plc] has $ycount elements; should have $xcount" |
2540
|
|
|
|
|
|
|
unless ($ycount == $xcount); |
2541
|
|
|
|
|
|
|
} |
2542
|
|
|
|
|
|
|
|
2543
|
|
|
|
|
|
|
# Still here: OK, we may proceed |
2544
|
|
|
|
|
|
|
# |
2545
|
2
|
|
|
|
|
5
|
my $y_vals = $_[1]; # Get first set of Y values |
2546
|
2
|
|
|
|
|
2
|
my $derivs = @_; # How many levels of derivative are wanted? |
2547
|
|
|
|
|
|
|
|
2548
|
2
|
100
|
|
|
|
13
|
$ryapp = ($derivs == 2) ? $self->Yapp_lagrange($x_vals, $y_vals) |
2549
|
|
|
|
|
|
|
: $self->Yapp_hermite(@_); |
2550
|
2
|
|
|
|
|
19
|
return ($ryapp); |
2551
|
|
|
|
|
|
|
} |
2552
|
|
|
|
|
|
|
# |
2553
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2554
|
|
|
|
|
|
|
# Yapp_lagrange() - Generate a polynomial using laGrange interpolattion |
2555
|
|
|
|
|
|
|
# Not intended for public use |
2556
|
|
|
|
|
|
|
# |
2557
|
|
|
|
|
|
|
# Parameters: |
2558
|
|
|
|
|
|
|
# - (implicit) A token polynomial, |
2559
|
|
|
|
|
|
|
# - Reference to an array of X-Values |
2560
|
|
|
|
|
|
|
# - Reference to array of Y-values |
2561
|
|
|
|
|
|
|
# Both arrays must be the same size or a fatal error will be generated |
2562
|
|
|
|
|
|
|
# Returns: |
2563
|
|
|
|
|
|
|
# - A polynomial that satisfies all those points. |
2564
|
|
|
|
|
|
|
# |
2565
|
|
|
|
|
|
|
sub Yapp_lagrange |
2566
|
|
|
|
|
|
|
{ |
2567
|
1
|
|
|
1
|
0
|
2
|
my $self = shift(@_); # Get my token polynomial |
2568
|
1
|
|
|
|
|
2
|
my ($x_vals, $y_vals) = @_; # Get my X & Y array references |
2569
|
1
|
|
|
|
|
3
|
my $ryapp = Yapp(0); # 0-degree poly, with constant: 0 |
2570
|
|
|
|
|
|
|
|
2571
|
|
|
|
|
|
|
|
2572
|
1
|
|
|
|
|
6
|
my $lag_list = Yapp_lag($x_vals, $y_vals); # Generate laGrange multipliers |
2573
|
|
|
|
|
|
|
|
2574
|
|
|
|
|
|
|
# In case you have not read the long comment in Yapp_lag: |
2575
|
|
|
|
|
|
|
# Each laGrange multiplier polynomial evaluates to 1 at its designated |
2576
|
|
|
|
|
|
|
# point and 0 at all the other points. I will multiply it by the Y-value |
2577
|
|
|
|
|
|
|
# at that point before adding it to the final polynomial. That way, each |
2578
|
|
|
|
|
|
|
# laGrange multiplier has a value of Y[i] at X[i] |
2579
|
|
|
|
|
|
|
# |
2580
|
1
|
|
|
|
|
2
|
my $lagp; |
2581
|
1
|
|
|
|
|
2
|
for (my $xlc = 0; $xlc <= $#{$x_vals}; $xlc++) |
|
8
|
|
|
|
|
22
|
|
2582
|
7
|
|
|
|
|
19
|
{ $ryapp += $lag_list->[$xlc] * $y_vals->[$xlc]; } # Add multilpied multiplier |
2583
|
|
|
|
|
|
|
# to the result |
2584
|
1
|
|
|
|
|
11
|
return $ryapp; # Show what I have built up |
2585
|
|
|
|
|
|
|
} |
2586
|
|
|
|
|
|
|
# |
2587
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2588
|
|
|
|
|
|
|
# Yapp_hermite() - Generate a polynomial using Hermite interpolattion |
2589
|
|
|
|
|
|
|
# Not intended for public use |
2590
|
|
|
|
|
|
|
# |
2591
|
|
|
|
|
|
|
# Parameters: |
2592
|
|
|
|
|
|
|
# - (implicit) A token polynomial, |
2593
|
|
|
|
|
|
|
# - Reference to an array of X-Values |
2594
|
|
|
|
|
|
|
# - Reference to array of Y-values |
2595
|
|
|
|
|
|
|
# - As many references to derivative values. |
2596
|
|
|
|
|
|
|
# Note that as of this release, only a first derivative is supported. |
2597
|
|
|
|
|
|
|
# Others will be ignored. |
2598
|
|
|
|
|
|
|
# |
2599
|
|
|
|
|
|
|
# Returns: |
2600
|
|
|
|
|
|
|
# - A polynomial that satisfies all those points. |
2601
|
|
|
|
|
|
|
# |
2602
|
|
|
|
|
|
|
sub Yapp_hermite |
2603
|
|
|
|
|
|
|
{ |
2604
|
1
|
|
|
1
|
0
|
3
|
my $self = shift(@_); # Get my token polynomial |
2605
|
1
|
|
|
|
|
3
|
my ($x_vals, $y_vals, $yp_vals) = @_; # Get my X, Y, Y' array references |
2606
|
|
|
|
|
|
|
|
2607
|
1
|
|
|
|
|
2
|
my $xlc; # Loop counter for a few loops below |
2608
|
1
|
|
|
|
|
2
|
my (@U_herm, @V_herm); # -> arrays of Hermite components |
2609
|
|
|
|
|
|
|
|
2610
|
1
|
|
|
|
|
5
|
my $lag_list = Yapp_lag($x_vals, $y_vals); # Generate laGrange multipliers |
2611
|
1
|
|
|
|
|
3
|
my $lag_prime = (); # Array: Derivatives of above |
2612
|
1
|
|
|
|
|
3
|
for ($xlc = 0; $xlc <= $#{$x_vals}; $xlc++) |
|
5
|
|
|
|
|
13
|
|
2613
|
|
|
|
|
|
|
{ |
2614
|
4
|
|
|
|
|
12
|
$lag_prime->[$xlc] = ($lag_list->[$xlc])->Yapp_derivative(1); |
2615
|
|
|
|
|
|
|
} |
2616
|
|
|
|
|
|
|
|
2617
|
|
|
|
|
|
|
# Each additive to the Hermite polynomial is the sum of U and V polynomials, |
2618
|
|
|
|
|
|
|
# as denerated below |
2619
|
|
|
|
|
|
|
# |
2620
|
1
|
|
|
|
|
3
|
for ($xlc = 0; $xlc <= $#{$x_vals}; $xlc++) |
|
5
|
|
|
|
|
15
|
|
2621
|
|
|
|
|
|
|
{ |
2622
|
|
|
|
|
|
|
# First component: V[i](X) = (X - x[i])(Lagrange[i]^2) |
2623
|
|
|
|
|
|
|
# |
2624
|
4
|
|
|
|
|
13
|
my $lag_squared = ($lag_list->[$xlc])**2; |
2625
|
4
|
|
|
|
|
10
|
my $v_herm1 = Yapp(-$x_vals->[$xlc], 1); # X - x_vals[i]; |
2626
|
4
|
|
|
|
|
10
|
$V_herm[$xlc] = $v_herm1 * $lag_squared; |
2627
|
|
|
|
|
|
|
|
2628
|
|
|
|
|
|
|
# Second component: U[i](X) = [1 - 2L'(x[i])(X-X[i])](Lagrange[i]^2) |
2629
|
|
|
|
|
|
|
# (Ugly, but it gets you there, like the old Volkswagon slogan :-) |
2630
|
|
|
|
|
|
|
# |
2631
|
|
|
|
|
|
|
# Evaluate derivative of current Lagrange multiplier polynomial at x[i] |
2632
|
|
|
|
|
|
|
# |
2633
|
4
|
|
|
|
|
15
|
my $l_prime = $lag_prime->[$xlc]->Yapp_eval($x_vals->[$xlc]); |
2634
|
4
|
|
|
|
|
11
|
my $u_herm1 = (1 - 2*$l_prime * $v_herm1); # Still a degree 1 polynomial |
2635
|
4
|
|
|
|
|
16
|
$U_herm[$xlc] = $u_herm1 * $lag_squared; |
2636
|
|
|
|
|
|
|
} |
2637
|
|
|
|
|
|
|
# I now have the components for the Hermite polynomial. Now I need to start |
2638
|
|
|
|
|
|
|
# using the Y and Y' values given> (Yes, I could have done this addition |
2639
|
|
|
|
|
|
|
# within the above loop. Clarity over elegance here.) |
2640
|
|
|
|
|
|
|
# |
2641
|
1
|
|
|
|
|
4
|
my $ryapp = Yapp(0); # Start with a constant 0 and add to it |
2642
|
1
|
|
|
|
|
3
|
for ($xlc = 0; $xlc <= $#{$x_vals}; $xlc++) |
|
5
|
|
|
|
|
18
|
|
2643
|
|
|
|
|
|
|
{ |
2644
|
4
|
|
|
|
|
11
|
$ryapp += $y_vals->[$xlc] * $U_herm[$xlc] |
2645
|
|
|
|
|
|
|
+ $yp_vals->[$xlc] * $V_herm[$xlc] ; |
2646
|
|
|
|
|
|
|
} |
2647
|
1
|
|
|
|
|
29
|
return $ryapp; |
2648
|
|
|
|
|
|
|
} |
2649
|
|
|
|
|
|
|
# |
2650
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
2651
|
|
|
|
|
|
|
# Yapp_lag() - Function to generate the laGrange polynomials that add up to the |
2652
|
|
|
|
|
|
|
# laGrange interpolating polynimal. This code was originally in |
2653
|
|
|
|
|
|
|
# Yapp_lagrange() but was separated out because it will be useful |
2654
|
|
|
|
|
|
|
# for the Hermite inerpolation scheme as well. |
2655
|
|
|
|
|
|
|
# This function is not a method and will not be exported, since its use is |
2656
|
|
|
|
|
|
|
# strictly internal. |
2657
|
|
|
|
|
|
|
# Parameters: |
2658
|
|
|
|
|
|
|
# - Reference to an array of X values |
2659
|
|
|
|
|
|
|
# - Reference to array of corresponding Y values |
2660
|
|
|
|
|
|
|
# Returns: |
2661
|
|
|
|
|
|
|
# - Reference to an array of component laGrange polynomials |
2662
|
|
|
|
|
|
|
# |
2663
|
|
|
|
|
|
|
sub Yapp_lag |
2664
|
|
|
|
|
|
|
{ |
2665
|
2
|
|
|
2
|
0
|
4
|
my ($x_vals, $y_vals) = @_; |
2666
|
2
|
|
|
|
|
3
|
my $xcount = @{$x_vals}; # Number of points in my lists |
|
2
|
|
|
|
|
4
|
|
2667
|
2
|
|
|
|
|
4
|
my @x_minus; # Array of x-x[i] polynomials |
2668
|
|
|
|
|
|
|
my @grange; # Array of largrange 1-point polynomials |
2669
|
|
|
|
|
|
|
# I will be returning a reference to this array |
2670
|
|
|
|
|
|
|
|
2671
|
2
|
|
|
|
|
7
|
for (my $mlc = 0; $mlc < $xcount; $mlc++) |
2672
|
|
|
|
|
|
|
{ |
2673
|
11
|
|
|
|
|
29
|
$x_minus[$mlc] = Yapp(-($x_vals->[$mlc]), 1); # X - x_val[mlc] |
2674
|
|
|
|
|
|
|
} |
2675
|
|
|
|
|
|
|
|
2676
|
|
|
|
|
|
|
# Now build the laGrange set for this set of X points |
2677
|
|
|
|
|
|
|
# |
2678
|
2
|
|
|
|
|
7
|
for (my $xlc = 0; $xlc < $xcount; $xlc++) |
2679
|
|
|
|
|
|
|
{ |
2680
|
11
|
|
|
|
|
21
|
$grange[$xlc] = Yapp(1); # Starting point for each laGrange poly |
2681
|
11
|
|
|
|
|
31
|
for (my $mlc = 0; $mlc < $xcount; $mlc++) |
2682
|
|
|
|
|
|
|
{ |
2683
|
65
|
100
|
|
|
|
134
|
next if ($mlc == $xlc); # Product of all but current point |
2684
|
54
|
|
|
|
|
110
|
$grange[$xlc] *= $x_minus[$mlc]; |
2685
|
|
|
|
|
|
|
} |
2686
|
|
|
|
|
|
|
# Coming out of above inner loop, $grange[$xlc] is the product of all |
2687
|
|
|
|
|
|
|
# the (X - x_val[i]) except the i for the current point ($mlc) |
2688
|
|
|
|
|
|
|
# The correct laGrange multiplier polynomial is 0 at all but the current |
2689
|
|
|
|
|
|
|
# point but 1 at the current point. To force this one into that mold: |
2690
|
|
|
|
|
|
|
# Divide the polynomial by its evaluation at x_val[xlc] (so it is 1 at |
2691
|
|
|
|
|
|
|
# this point) |
2692
|
|
|
|
|
|
|
# |
2693
|
11
|
|
|
|
|
37
|
$grange[$xlc] |
2694
|
|
|
|
|
|
|
/= ($grange[$xlc])->Yapp_eval($x_vals->[$xlc]); |
2695
|
|
|
|
|
|
|
} |
2696
|
|
|
|
|
|
|
# Coming out above outer loop, array @grange has all the lagrange polynomials |
2697
|
|
|
|
|
|
|
# pertaining to this set of points. That's what the calling function wants. |
2698
|
|
|
|
|
|
|
# |
2699
|
2
|
|
|
|
|
19
|
return \@grange; # Return reference to that array |
2700
|
|
|
|
|
|
|
} |
2701
|
|
|
|
|
|
|
# |
2702
|
|
|
|
|
|
|
# Yapp_by_roots(): Constructor to build a polynomial whose roots are in the |
2703
|
|
|
|
|
|
|
# passed array or referenced array. |
2704
|
|
|
|
|
|
|
# Parameter[s]: Either: |
2705
|
|
|
|
|
|
|
# - A complete array of the roots of the desired polynomial |
2706
|
|
|
|
|
|
|
# - A reference to such an array. |
2707
|
|
|
|
|
|
|
# It is the caller's responsibility to include conjugate pairs of complex roots |
2708
|
|
|
|
|
|
|
# if only real coefficients are desired. |
2709
|
|
|
|
|
|
|
# |
2710
|
|
|
|
|
|
|
sub Yapp_by_roots |
2711
|
|
|
|
|
|
|
{ |
2712
|
|
|
|
|
|
|
# Question: Did user pass me an array or array reference? Either way I will |
2713
|
|
|
|
|
|
|
# be stepping through the array using an array reference. That is: |
2714
|
|
|
|
|
|
|
# - If user passed me a nice array reference, just use it. |
2715
|
|
|
|
|
|
|
# - Passed me a naked array? Create reference the parameter array. |
2716
|
|
|
|
|
|
|
# |
2717
|
2
|
50
|
|
2
|
1
|
2788
|
my $roots = (ref($_[0]) eq "ARRAY") ? shift(@_) : \@_ ; |
2718
|
|
|
|
|
|
|
|
2719
|
2
|
|
|
|
|
8
|
my $ryapp = Yapp(1); # Start with nice unit Yapp - Just "1" |
2720
|
|
|
|
|
|
|
|
2721
|
2
|
|
|
|
|
8
|
for (my $rlc = 0; $rlc <= $#{$roots}; $rlc++) |
|
13
|
|
|
|
|
38
|
|
2722
|
|
|
|
|
|
|
{ |
2723
|
11
|
|
|
|
|
34
|
$ryapp *= Yapp(- ($roots->[$rlc]), 1) # Multiply by (X - $roots[i]) |
2724
|
|
|
|
|
|
|
} |
2725
|
2
|
|
|
|
|
11
|
return $ryapp; # Bet that looked easy! :-) |
2726
|
|
|
|
|
|
|
} |
2727
|
|
|
|
|
|
|
# |
2728
|
|
|
|
|
|
|
1; |
2729
|
|
|
|
|
|
|
|
2730
|
|
|
|
|
|
|
__END__ |