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