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