| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Math::Symbolic::Custom::Factor; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
280092
|
use 5.006; |
|
|
2
|
|
|
|
|
13
|
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
68
|
|
|
5
|
2
|
|
|
2
|
|
9
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
115
|
|
|
6
|
2
|
|
|
2
|
|
9
|
no warnings 'recursion'; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
167
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=pod |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=encoding utf8 |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Math::Symbolic::Custom::Factor - Re-arrange a Math::Symbolic expression into a product of factors |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 VERSION |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Version 0.13 |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '0.13'; |
|
23
|
|
|
|
|
|
|
|
|
24
|
2
|
|
|
2
|
|
751
|
use Math::Symbolic qw(:all); |
|
|
2
|
|
|
|
|
167172
|
|
|
|
2
|
|
|
|
|
537
|
|
|
25
|
2
|
|
|
2
|
|
17
|
use Math::Symbolic::Custom::Base; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
73
|
|
|
26
|
2
|
|
|
2
|
|
1862
|
use Math::Symbolic::Custom::Collect 0.32; |
|
|
2
|
|
|
|
|
33447
|
|
|
|
2
|
|
|
|
|
18
|
|
|
27
|
2
|
|
|
2
|
|
1571
|
use Math::Symbolic::Custom::Polynomial 0.3; |
|
|
2
|
|
|
|
|
7208
|
|
|
|
2
|
|
|
|
|
12
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
2
|
|
|
2
|
|
248
|
BEGIN {*import = \&Math::Symbolic::Custom::Base::aggregate_import} |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $Aggregate_Export = [qw/to_factored/]; |
|
32
|
|
|
|
|
|
|
|
|
33
|
2
|
|
|
2
|
|
12
|
use Carp; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
11467
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Provides method to_factored() through the Math::Symbolic module extension class. This method attempts to factorize a Math::Symbolic expression. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
This is a very early version and can only factor relatively simple expressions. It has a few factoring strategies in it, and hopefully more will come |
|
40
|
|
|
|
|
|
|
along in later versions if I have time to implement them. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 EXAMPLES |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
use strict; |
|
45
|
|
|
|
|
|
|
use Math::Symbolic qw/:all/; |
|
46
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Factor; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# to_factored() returns the full expression as a product of factors |
|
49
|
|
|
|
|
|
|
# and an array ref to the factors themselves (so that multiplying them |
|
50
|
|
|
|
|
|
|
# together and collecting up should produce the original expression). |
|
51
|
|
|
|
|
|
|
my ($factored, $factors) = parse_from_string("3*x + 12*y")->to_factored(); |
|
52
|
|
|
|
|
|
|
# $factored and the factors in $factors->[] are Math::Symbolic expressions. |
|
53
|
|
|
|
|
|
|
print "Full expression: $factored\n"; # Full expression: (x + (4 * y)) * 3 |
|
54
|
|
|
|
|
|
|
print "Factors: '", join(q{', '}, @{$factors}), "'\n\n"; # Factors: '3', 'x + (4 * y)' |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
($factored, $factors) = parse_from_string("x^2 - 81")->to_factored(); |
|
57
|
|
|
|
|
|
|
print "Full expression: $factored\n"; # Full expression: (9 + x) * (x - 9) |
|
58
|
|
|
|
|
|
|
print "Factors: '", join(q{', '}, @{$factors}), "'\n\n"; # Factors: 'x - 9', '9 + x' |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
($factored, $factors) = parse_from_string("6*x^2 + 37*x + 6")->to_factored(); |
|
61
|
|
|
|
|
|
|
print "Full expression: $factored\n"; # Full expression: (6 + x) * (1 + (6 * x)) |
|
62
|
|
|
|
|
|
|
print "Factors: '", join(q{', '}, @{$factors}), "'\n\n"; # Factors: '6 + x', '1 + (6 * x)' |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
($factored, $factors) = parse_from_string("y^4 - 5*y^3 - 5*y^2 + 23*y + 10")->to_factored(); |
|
65
|
|
|
|
|
|
|
print "Full expression: $factored\n"; # Full expression: ((y - 5) * (((y ^ 2) - 1) - (2 * y))) * (2 + y) |
|
66
|
|
|
|
|
|
|
print "Factors: '", join(q{', '}, @{$factors}), "'\n\n"; # Factors: '2 + y', 'y - 5', '((y ^ 2) - 1) - (2 * y)' |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# This one does not factor (using the strategies in this module). |
|
69
|
|
|
|
|
|
|
# The original expression is returned (albeit re-arranged) and the number of entries in |
|
70
|
|
|
|
|
|
|
# @{$factors} is 1. |
|
71
|
|
|
|
|
|
|
($factored, $factors) = parse_from_string("x^2 + 2*x + 2")->to_factored(); |
|
72
|
|
|
|
|
|
|
print "Full expression: $factored\n"; # Full expression: (2 + (2 * x)) + (x ^ 2) |
|
73
|
|
|
|
|
|
|
print "Factors: '", join(q{', '}, @{$factors}), "'\n"; # Factors: '(2 + (2 * x)) + (x ^ 2)' |
|
74
|
|
|
|
|
|
|
print "Did not factorize\n\n" if scalar(@{$factors}) == 1; # Did not factorize |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub to_factored { |
|
79
|
66
|
|
|
66
|
0
|
3292089
|
my ($t1) = @_; |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# Split the original expression into pre-existing factors. |
|
82
|
|
|
|
|
|
|
# e.g. if we are given 3*x^2*y then we want (3,x^2,y) |
|
83
|
66
|
|
|
|
|
189
|
my @original_factors; |
|
84
|
66
|
|
|
|
|
366
|
get_factors($t1, \@original_factors); |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Go through that list of factors and try to factorize more. |
|
87
|
66
|
|
|
|
|
196
|
my @factors; |
|
88
|
66
|
|
|
|
|
205
|
FACTORIZE: foreach my $expr (@original_factors) { |
|
89
|
|
|
|
|
|
|
|
|
90
|
68
|
|
|
|
|
468
|
my ($t2, $n_hr, $d_hr) = $expr->to_collected(); |
|
91
|
|
|
|
|
|
|
|
|
92
|
68
|
50
|
100
|
|
|
281222
|
if ( !defined $t2 ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
0
|
carp "to_factored: undefined result from to_collected() for [$expr]"; |
|
94
|
0
|
|
|
|
|
0
|
return undef; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
elsif ( !defined($n_hr) ) { |
|
97
|
|
|
|
|
|
|
# very simple expression |
|
98
|
0
|
|
|
|
|
0
|
push @factors, $t2; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
68
|
|
|
|
|
347
|
elsif ( scalar(keys %{$n_hr->{terms}}) == 1 ) { |
|
101
|
|
|
|
|
|
|
# just the constant accumulator |
|
102
|
1
|
|
|
|
|
8
|
push @factors, $t2; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
67
|
|
|
|
|
508
|
elsif ( (scalar(keys %{$n_hr->{terms}}) == 2) && ($n_hr->{terms}{constant_accumulator} == 0)) { |
|
105
|
|
|
|
|
|
|
# again, just one term |
|
106
|
2
|
|
|
|
|
13
|
push @factors, $t2; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
elsif ( !defined $d_hr ) { |
|
109
|
|
|
|
|
|
|
# Attempt to factorize this sub-expression using some strategies. |
|
110
|
65
|
|
|
|
|
346
|
my @new_factors = factorize($t2, $n_hr); |
|
111
|
65
|
|
|
|
|
1172
|
push @factors, @new_factors; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
else { |
|
114
|
|
|
|
|
|
|
# cannot process at the moment, pass-through |
|
115
|
0
|
|
|
|
|
0
|
push @factors, $t2; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
66
|
|
|
|
|
256
|
@factors = map { scalar($_->to_collected()) } @factors; |
|
|
142
|
|
|
|
|
91497
|
|
|
120
|
66
|
|
|
|
|
158049
|
my $product_tree = build_product_tree(@factors); |
|
121
|
|
|
|
|
|
|
|
|
122
|
66
|
50
|
|
|
|
617
|
return wantarray ? ($product_tree, \@factors) : $product_tree; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub get_factors { |
|
127
|
70
|
|
|
70
|
0
|
295
|
my ($t, $l) = @_; |
|
128
|
|
|
|
|
|
|
|
|
129
|
70
|
100
|
100
|
|
|
349
|
if ( ($t->term_type() == T_OPERATOR) && ($t->type() == B_PRODUCT) ) { |
|
130
|
2
|
|
|
|
|
35
|
get_factors($t->op1(), $l); |
|
131
|
2
|
|
|
|
|
12
|
get_factors($t->op2(), $l); |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
else { |
|
134
|
68
|
|
|
|
|
1296
|
push @{$l}, $t; |
|
|
68
|
|
|
|
|
224
|
|
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub build_product_tree { |
|
139
|
66
|
|
|
66
|
0
|
269
|
my @product_list = @_; |
|
140
|
|
|
|
|
|
|
|
|
141
|
66
|
|
|
|
|
176
|
my %h; |
|
142
|
|
|
|
|
|
|
|
|
143
|
66
|
|
|
|
|
170
|
my @str = map { $_->to_string() } @product_list; |
|
|
142
|
|
|
|
|
5336
|
|
|
144
|
66
|
|
|
|
|
8760
|
$h{$_}++ for @str; |
|
145
|
|
|
|
|
|
|
|
|
146
|
66
|
|
|
|
|
142
|
my @to_mult; |
|
147
|
66
|
|
|
|
|
398
|
foreach my $expr ( sort { $h{$b} <=> $h{$a} } keys %h ) { |
|
|
75
|
|
|
|
|
356
|
|
|
148
|
|
|
|
|
|
|
|
|
149
|
130
|
|
|
|
|
1823608
|
my $index = $h{$expr}; |
|
150
|
|
|
|
|
|
|
|
|
151
|
130
|
100
|
|
|
|
492
|
if ( $index > 1 ) { |
|
152
|
6
|
|
|
|
|
46
|
push @to_mult, parse_from_string("($expr)^$index"); |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
else { |
|
155
|
124
|
|
|
|
|
788
|
push @to_mult, parse_from_string($expr); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
66
|
|
|
|
|
1688871
|
my $ntp = shift @to_mult; |
|
160
|
66
|
|
|
|
|
327
|
while (@to_mult) { |
|
161
|
64
|
|
|
|
|
491
|
my $e = shift @to_mult; |
|
162
|
64
|
|
|
|
|
332
|
$ntp = Math::Symbolic::Operator->new( '*', $ntp, $e ); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
66
|
|
|
|
|
2075
|
return $ntp; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub factorize { |
|
169
|
65
|
|
|
65
|
0
|
194
|
my ($t, $n_hr) = @_; |
|
170
|
|
|
|
|
|
|
|
|
171
|
65
|
|
|
|
|
136
|
my %n_terms = %{ $n_hr->{terms} }; |
|
|
65
|
|
|
|
|
411
|
|
|
172
|
65
|
|
|
|
|
184
|
my %n_funcs = %{ $n_hr->{trees} }; |
|
|
65
|
|
|
|
|
245
|
|
|
173
|
|
|
|
|
|
|
|
|
174
|
65
|
|
|
|
|
176
|
my @factors; |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# extract common constants |
|
177
|
|
|
|
|
|
|
my %constants; |
|
178
|
65
|
|
|
|
|
217
|
$constants{$_}++ for grep { $_ != 0 } values %n_terms; |
|
|
190
|
|
|
|
|
4911
|
|
|
179
|
65
|
|
|
|
|
238
|
my @con = sort {$a <=> $b} map { abs } keys %constants; |
|
|
125
|
|
|
|
|
362
|
|
|
|
162
|
|
|
|
|
618
|
|
|
180
|
65
|
|
|
|
|
235
|
my @con_int = grep { $_ eq int($_) } @con; |
|
|
162
|
|
|
|
|
583
|
|
|
181
|
|
|
|
|
|
|
|
|
182
|
65
|
50
|
|
|
|
302
|
if ( scalar(@con) == scalar(@con_int) ) { # all integer, proceed |
|
183
|
65
|
|
|
|
|
149
|
my $min = $con[0]; |
|
184
|
65
|
|
|
|
|
124
|
my $GCF; |
|
185
|
65
|
|
|
|
|
274
|
FIND_CONST_GCF: foreach my $div (reverse(2..$min)) { |
|
186
|
77
|
|
|
|
|
136
|
my $div_ok = 1; |
|
187
|
77
|
|
|
|
|
139
|
CONST_DIV_TEST: foreach my $num (@con) { |
|
188
|
127
|
100
|
|
|
|
373
|
if ( $num % $div != 0 ) { |
|
189
|
64
|
|
|
|
|
94
|
$div_ok = 0; |
|
190
|
64
|
|
|
|
|
139
|
last CONST_DIV_TEST; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
} |
|
193
|
77
|
100
|
|
|
|
203
|
if ( $div_ok ) { |
|
194
|
13
|
|
|
|
|
25
|
$GCF = $div; |
|
195
|
13
|
|
|
|
|
41
|
last FIND_CONST_GCF; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
65
|
100
|
|
|
|
257
|
if ( defined $GCF ) { |
|
200
|
13
|
|
|
|
|
85
|
push @factors, Math::Symbolic::Constant->new($GCF); |
|
201
|
13
|
|
|
|
|
345
|
$n_terms{$_} /= $GCF for keys %n_terms; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# extract common variables |
|
206
|
65
|
100
|
|
|
|
334
|
if ($n_terms{constant_accumulator} == 0) { |
|
207
|
|
|
|
|
|
|
|
|
208
|
19
|
|
|
|
|
43
|
my %c_vars; |
|
209
|
19
|
|
|
|
|
77
|
foreach my $key (keys %n_terms) { |
|
210
|
57
|
|
|
|
|
182
|
my @v1 = split(/,/, $key); |
|
211
|
57
|
|
|
|
|
120
|
foreach my $v2 (@v1) { |
|
212
|
61
|
|
|
|
|
182
|
my ($v, $p) = split(/:/, $v2); |
|
213
|
61
|
100
|
|
|
|
175
|
if ( exists $c_vars{$v} ) { |
|
214
|
5
|
|
|
|
|
14
|
$c_vars{$v}{c}++; |
|
215
|
5
|
100
|
|
|
|
31
|
if ($p < $c_vars{$v}{p}) { |
|
216
|
4
|
|
|
|
|
16
|
$c_vars{$v}{p} = $p; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
else { |
|
220
|
56
|
|
|
|
|
166
|
$c_vars{$v}{c}++; |
|
221
|
56
|
|
|
|
|
185
|
$c_vars{$v}{p} = $p; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
19
|
|
|
|
|
46
|
my @all_terms; |
|
227
|
19
|
|
|
|
|
108
|
while ( my ($v, $c) = each %c_vars ) { |
|
228
|
56
|
100
|
|
|
|
266
|
if ( $c->{c} == scalar(keys %n_terms)-1 ) { |
|
229
|
5
|
|
|
|
|
28
|
push @all_terms, [$v, $c->{p}]; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
19
|
|
|
|
|
81
|
foreach my $common_var (@all_terms) { |
|
234
|
5
|
|
|
|
|
13
|
my ($var, $pow) = @{$common_var}; |
|
|
5
|
|
|
|
|
17
|
|
|
235
|
|
|
|
|
|
|
|
|
236
|
5
|
|
|
|
|
11
|
my %new_ct; |
|
237
|
5
|
|
|
|
|
16
|
$new_ct{constant_accumulator} = 0; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# remove this variable from the data structure |
|
240
|
5
|
|
|
|
|
25
|
while ( my ($t, $c) = each %n_terms ) { |
|
241
|
|
|
|
|
|
|
|
|
242
|
15
|
100
|
|
|
|
53
|
next if $t eq 'constant_accumulator'; |
|
243
|
10
|
|
|
|
|
28
|
my @v1 = split(/,/, $t); |
|
244
|
10
|
|
|
|
|
20
|
my @nt; |
|
245
|
10
|
|
|
|
|
23
|
foreach my $v2 (@v1) { |
|
246
|
14
|
|
|
|
|
41
|
my ($vv, $cc) = split(/:/, $v2); |
|
247
|
14
|
100
|
|
|
|
40
|
if ($vv eq $var) { |
|
248
|
10
|
|
|
|
|
21
|
$cc -= $pow; |
|
249
|
10
|
100
|
|
|
|
38
|
if ($cc > 0) { |
|
|
|
100
|
|
|
|
|
|
|
250
|
4
|
|
|
|
|
16
|
push @nt, "$vv:$cc"; |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
elsif ( scalar(@v1) == 1 ) { |
|
253
|
3
|
|
|
|
|
10
|
$new_ct{constant_accumulator} += $c; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
else { |
|
257
|
4
|
|
|
|
|
11
|
push @nt, $v2; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
10
|
100
|
|
|
|
35
|
if ( scalar(@nt) ) { |
|
262
|
7
|
|
|
|
|
44
|
$new_ct{join(",", @nt)} = $c; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# add it to the list of factors |
|
267
|
5
|
50
|
|
|
|
20
|
if ( $pow == 1 ) { |
|
268
|
5
|
|
|
|
|
34
|
push @factors, Math::Symbolic::Variable->new($n_funcs{$var}->new()); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
else { |
|
271
|
0
|
|
|
|
|
0
|
push @factors, Math::Symbolic::Operator->new('^', $n_funcs{$var}->new(), Math::Symbolic::Constant->new($pow)); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
5
|
|
|
|
|
274
|
%n_terms = %new_ct; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Various factoring formulas |
|
279
|
|
|
|
|
|
|
# Difference of Squares:- |
|
280
|
|
|
|
|
|
|
# x^2-y^2 = (x-y)*(x+y) |
|
281
|
|
|
|
|
|
|
# Sum/Difference of Cubes:- |
|
282
|
|
|
|
|
|
|
# x^3-y^3 = (x-y)*(x^2+x*y+y^2) |
|
283
|
|
|
|
|
|
|
# x^3+y^3 = (x+y)*(x^2−x*y+y^2) |
|
284
|
|
|
|
|
|
|
# pull out the constant accumulator |
|
285
|
65
|
|
|
|
|
177
|
my $acc = $n_terms{constant_accumulator}; |
|
286
|
65
|
|
|
|
|
179
|
delete $n_terms{constant_accumulator}; |
|
287
|
|
|
|
|
|
|
|
|
288
|
65
|
100
|
66
|
|
|
646
|
if ( (scalar(keys %n_terms) == 1) && |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
289
|
|
|
|
|
|
|
(abs($acc) > 1) && |
|
290
|
|
|
|
|
|
|
($acc eq int($acc)) ) { |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# remaining key must be the other term |
|
293
|
24
|
|
|
|
|
87
|
my $t = (keys %n_terms)[0]; |
|
294
|
24
|
|
|
|
|
72
|
my $mult = $n_terms{$t}; |
|
295
|
|
|
|
|
|
|
|
|
296
|
24
|
100
|
|
|
|
92
|
if ( $mult > 0 ) { |
|
297
|
|
|
|
|
|
|
|
|
298
|
22
|
|
|
|
|
102
|
my $cbrt_y = int_rt(abs($acc), 3); # y (accumulator) must be a cube |
|
299
|
22
|
|
|
|
|
88
|
my $cbrt_x = int_rt($mult, 3); # x constant multiplier must be a cube |
|
300
|
|
|
|
|
|
|
|
|
301
|
22
|
100
|
100
|
|
|
223
|
if ( ($t !~ /,/) && ($t =~ /:3$/) && defined($cbrt_x) && defined($cbrt_y) ) { |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
302
|
|
|
|
|
|
|
|
|
303
|
1
|
|
|
|
|
6
|
my ($vv) = split(/:/, $t); |
|
304
|
1
|
|
|
|
|
5
|
my $v = $n_funcs{$vv}->{name}; |
|
305
|
|
|
|
|
|
|
|
|
306
|
1
|
50
|
|
|
|
5
|
if ( $acc < -1 ) { |
|
|
|
0
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
|
|
308
|
1
|
50
|
|
|
|
4
|
if ( $cbrt_x == 1 ) { |
|
309
|
1
|
|
|
|
|
9
|
push @factors, parse_from_string("$v - $cbrt_y"); |
|
310
|
1
|
|
|
|
|
6834
|
push @factors, parse_from_string("$v^2 + $cbrt_y*$v + " . ($cbrt_y*$cbrt_y)); |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
else { |
|
313
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$cbrt_x*$v - $cbrt_y"); |
|
314
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v^2 + " . ($cbrt_y*$cbrt_x) . "*$v + " . ($cbrt_y*$cbrt_y)); |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
elsif ( $acc > 1 ) { |
|
318
|
|
|
|
|
|
|
|
|
319
|
0
|
0
|
|
|
|
0
|
if ( $cbrt_x == 1 ) { |
|
320
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v + $cbrt_y"); |
|
321
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v^2 - $cbrt_y*$v + " . ($cbrt_y*$cbrt_y)); |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
else { |
|
324
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$cbrt_x*$v + $cbrt_y"); |
|
325
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v^2 - " . ($cbrt_y*$cbrt_x) . "*$v + " . ($cbrt_y*$cbrt_y)); |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
1
|
|
|
|
|
10880
|
undef %n_terms; |
|
330
|
1
|
|
|
|
|
11
|
return @factors; |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
21
|
100
|
66
|
|
|
147
|
if ( %n_terms && ($acc < -1) ) { |
|
334
|
|
|
|
|
|
|
|
|
335
|
20
|
|
|
|
|
77
|
my $sqrt_y = int_rt(abs($acc), 2); # y (accumulator) must be a square |
|
336
|
20
|
|
|
|
|
54
|
my $sqrt_x = int_rt($mult, 2); # x constant multiplier must be a square |
|
337
|
20
|
|
|
|
|
110
|
my ($vv, $pow) = split(/:/, $t); # power must be a square (> 1) |
|
338
|
|
|
|
|
|
|
|
|
339
|
20
|
100
|
100
|
|
|
254
|
if ( ($t !~ /,/) && defined($sqrt_x) && ($pow > 1) && ($pow % 2 == 0) && defined($sqrt_y) ) { |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
340
|
|
|
|
|
|
|
|
|
341
|
8
|
|
|
|
|
29
|
my $v = $n_funcs{$vv}->{name}; |
|
342
|
|
|
|
|
|
|
|
|
343
|
8
|
100
|
|
|
|
31
|
if ( $pow == 2 ) { |
|
|
|
50
|
|
|
|
|
|
|
344
|
5
|
100
|
|
|
|
14
|
if ( $sqrt_x == 1 ) { |
|
345
|
4
|
|
|
|
|
27
|
push @factors, parse_from_string("$v - $sqrt_y"); |
|
346
|
4
|
|
|
|
|
23673
|
push @factors, parse_from_string("$v + $sqrt_y"); |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
else { |
|
349
|
1
|
|
|
|
|
7
|
push @factors, parse_from_string("($sqrt_x*$v) - $sqrt_y"); |
|
350
|
1
|
|
|
|
|
10069
|
push @factors, parse_from_string("($sqrt_x*$v) + $sqrt_y"); |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
elsif ( $pow % 2 == 0 ) { |
|
354
|
3
|
|
|
|
|
10
|
my $hp = $pow / 2; |
|
355
|
3
|
100
|
|
|
|
10
|
if ( $sqrt_x == 1 ) { |
|
356
|
2
|
|
|
|
|
32
|
push @factors, parse_from_string("$v^$hp - $sqrt_y"); |
|
357
|
2
|
|
|
|
|
11780
|
push @factors, parse_from_string("$v^$hp + $sqrt_y"); |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
else { |
|
360
|
1
|
|
|
|
|
17
|
push @factors, parse_from_string("($sqrt_x*$v^$hp) - $sqrt_y"); |
|
361
|
1
|
|
|
|
|
17813
|
push @factors, parse_from_string("($sqrt_x*$v^$hp) + $sqrt_y"); |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
8
|
|
|
|
|
62368
|
undef %n_terms; |
|
366
|
8
|
|
|
|
|
77
|
return @factors; |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
elsif ( (scalar(keys %n_terms) == 2) && ($acc == 0) ) { |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# try the factoring formulas with two variables |
|
374
|
16
|
|
|
|
|
68
|
my @t = keys %n_terms; |
|
375
|
16
|
|
|
|
|
49
|
my @m = map { $n_terms{$_} } @t; |
|
|
32
|
|
|
|
|
94
|
|
|
376
|
|
|
|
|
|
|
|
|
377
|
16
|
|
|
|
|
87
|
my ($vv1) = split(/:/, $t[0]); |
|
378
|
16
|
|
|
|
|
56
|
my $v1 = $n_funcs{$vv1}->{name}; |
|
379
|
16
|
|
|
|
|
54
|
my ($vv2) = split(/:/, $t[1]); |
|
380
|
16
|
|
|
|
|
53
|
my $v2 = $n_funcs{$vv2}->{name}; |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# x^2-y^2 = (x-y)*(x+y) |
|
383
|
16
|
100
|
66
|
|
|
403
|
if ( ($t[0] !~ /,/) && ($t[0] =~ /:2$/) && ($t[1] !~ /,/) && ($t[1] =~ /:2$/) ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
384
|
|
|
|
|
|
|
|
|
385
|
1
|
50
|
33
|
|
|
13
|
if ( ($m[0] > 0) && ($m[1] < 0) ) { |
|
|
|
50
|
33
|
|
|
|
|
|
386
|
0
|
|
|
|
|
0
|
my $sqrt_y = int_rt(abs($m[1]), 2); |
|
387
|
0
|
|
|
|
|
0
|
my $sqrt_x = int_rt($m[0], 2); |
|
388
|
|
|
|
|
|
|
|
|
389
|
0
|
0
|
0
|
|
|
0
|
if ( defined($sqrt_y) && defined($sqrt_x) ) { |
|
390
|
0
|
0
|
0
|
|
|
0
|
if ( ($sqrt_x == 1) && ($sqrt_y == 1) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v1 - $v2"); |
|
392
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v1 + $v2"); |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
elsif ( $sqrt_x == 1 ) { |
|
395
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v1 - $sqrt_y*$v2"); |
|
396
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v1 + $sqrt_y*$v2"); |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
elsif ( $sqrt_y == 1 ) { |
|
399
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_x*$v1 - $v2"); |
|
400
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_x*$v1 + $v2"); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
else { |
|
403
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_x*$v1 - $sqrt_y*$v2"); |
|
404
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_x*$v1 + $sqrt_y*$v2"); |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
0
|
undef %n_terms; |
|
408
|
0
|
|
|
|
|
0
|
return @factors; |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
elsif ( ($m[0] < 0) && ($m[1] > 0 ) ) { |
|
412
|
1
|
|
|
|
|
6
|
my $sqrt_y = int_rt($m[1], 2); |
|
413
|
1
|
|
|
|
|
5
|
my $sqrt_x = int_rt(abs($m[0]), 2); |
|
414
|
|
|
|
|
|
|
|
|
415
|
1
|
50
|
33
|
|
|
8
|
if ( defined($sqrt_y) && defined($sqrt_x) ) { |
|
416
|
1
|
50
|
33
|
|
|
7
|
if ( ($sqrt_x == 1) && ($sqrt_y == 1) ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
417
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v2 - $v1"); |
|
418
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$v2 + $v1"); |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
elsif ( $sqrt_y == 1 ) { |
|
421
|
1
|
|
|
|
|
9
|
push @factors, parse_from_string("$v2 - $sqrt_x*$v1"); |
|
422
|
1
|
|
|
|
|
10033
|
push @factors, parse_from_string("$v2 + $sqrt_x*$v1"); |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
elsif ( $sqrt_x == 1 ) { |
|
425
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_y*$v2 - $v1"); |
|
426
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_y*$v2 + $v1"); |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
else { |
|
429
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_y*$v2 - $sqrt_x*$v1"); |
|
430
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$sqrt_y*$v2 + $sqrt_x*$v1"); |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
1
|
|
|
|
|
9824
|
undef %n_terms; |
|
434
|
1
|
|
|
|
|
13
|
return @factors; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
elsif ( ($t[0] !~ /,/) && ($t[0] =~ /:3$/) && ($t[1] !~ /,/) && ($t[1] =~ /:3$/) ) { |
|
439
|
|
|
|
|
|
|
|
|
440
|
12
|
100
|
100
|
|
|
119
|
if ( ($m[0] > 0) && ($m[1] > 0) ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# x^3+y^3 = (x+y)*(x^2−x*y+y^2) |
|
442
|
|
|
|
|
|
|
|
|
443
|
4
|
|
|
|
|
19
|
my $cbrt_x = int_rt($m[0], 3); |
|
444
|
4
|
|
|
|
|
12
|
my $cbrt_y = int_rt($m[1], 3); |
|
445
|
|
|
|
|
|
|
|
|
446
|
4
|
50
|
33
|
|
|
26
|
if ( defined($cbrt_y) && defined($cbrt_x) ) { |
|
447
|
4
|
100
|
100
|
|
|
30
|
if ( ($cbrt_x == 1) && ($cbrt_y == 1) ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
448
|
1
|
|
|
|
|
10
|
push @factors, parse_from_string("$v1 + $v2"); |
|
449
|
1
|
|
|
|
|
9271
|
push @factors, parse_from_string("$v1^2 - $v1*$v2 + $v2^2"); |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
elsif ( $cbrt_y == 1 ) { |
|
452
|
1
|
|
|
|
|
9
|
push @factors, parse_from_string("$cbrt_x*$v1 + $v2"); |
|
453
|
1
|
|
|
|
|
10173
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v1^2 - $cbrt_x*$v1*$v2 + $v2^2"); |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
elsif ( $cbrt_x == 1 ) { |
|
456
|
1
|
|
|
|
|
29
|
push @factors, parse_from_string("$v1 + $cbrt_y*$v2"); |
|
457
|
1
|
|
|
|
|
11045
|
push @factors, parse_from_string("$v1^2 - $cbrt_y*$v1*$v2 + " . ($cbrt_y*$cbrt_y) . "*$v2^2"); |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
else { |
|
460
|
1
|
|
|
|
|
7
|
push @factors, parse_from_string("$cbrt_x*$v1 + $cbrt_y*$v2"); |
|
461
|
1
|
|
|
|
|
9057
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v1^2 - " . ($cbrt_x*$cbrt_y) . "*$v1*$v2 + " . ($cbrt_y*$cbrt_y) . "*$v2^2"); |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
4
|
|
|
|
|
75388
|
undef %n_terms; |
|
465
|
4
|
|
|
|
|
56
|
return @factors; |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
elsif ( ($m[0] > 0) && ($m[1] < 0) ) { |
|
469
|
|
|
|
|
|
|
# x^3-y^3 = (x-y)*(x^2+x*y+y^2) |
|
470
|
|
|
|
|
|
|
|
|
471
|
3
|
|
|
|
|
17
|
my $cbrt_x = int_rt($m[0], 3); |
|
472
|
3
|
|
|
|
|
9
|
my $cbrt_y = int_rt(abs($m[1]), 3); |
|
473
|
|
|
|
|
|
|
|
|
474
|
3
|
50
|
33
|
|
|
18
|
if ( defined($cbrt_y) && defined($cbrt_x) ) { |
|
475
|
3
|
100
|
100
|
|
|
36
|
if ( ($cbrt_x == 1) && ($cbrt_y == 1) ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
476
|
1
|
|
|
|
|
9
|
push @factors, parse_from_string("$v1 - $v2"); |
|
477
|
1
|
|
|
|
|
9258
|
push @factors, parse_from_string("$v1^2 + $v1*$v2 + $v2^2"); |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
elsif ( $cbrt_y == 1 ) { |
|
480
|
1
|
|
|
|
|
6
|
push @factors, parse_from_string("$cbrt_x*$v1 - $v2"); |
|
481
|
1
|
|
|
|
|
5532
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v1^2 + $cbrt_x*$v1*$v2 + $v2^2"); |
|
482
|
|
|
|
|
|
|
} |
|
483
|
|
|
|
|
|
|
elsif ( $cbrt_x == 1 ) { |
|
484
|
1
|
|
|
|
|
8
|
push @factors, parse_from_string("$v1 - $cbrt_y*$v2"); |
|
485
|
1
|
|
|
|
|
10340
|
push @factors, parse_from_string("$v1^2 + $cbrt_y*$v1*$v2 + " . ($cbrt_y*$cbrt_y) . "*$v2^2"); |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
else { |
|
488
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string("$cbrt_x*$v1 - $cbrt_y*$v2"); |
|
489
|
0
|
|
|
|
|
0
|
push @factors, parse_from_string(($cbrt_x*$cbrt_x) . "*$v1^2 + " . ($cbrt_x*$cbrt_y) . "*$v1*$v2 + " . ($cbrt_y*$cbrt_y) . "*$v2^2"); |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
|
|
492
|
3
|
|
|
|
|
43940
|
undef %n_terms; |
|
493
|
3
|
|
|
|
|
36
|
return @factors; |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
elsif ( ($m[0] < 0) && ($m[1] > 0) ) { |
|
497
|
|
|
|
|
|
|
# y^3-x^3 = (y-x)*(y^2+y*x+x^2) |
|
498
|
|
|
|
|
|
|
|
|
499
|
5
|
|
|
|
|
25
|
my $cbrt_x = int_rt(abs($m[0]), 3); |
|
500
|
5
|
|
|
|
|
22
|
my $cbrt_y = int_rt($m[1], 3); |
|
501
|
|
|
|
|
|
|
|
|
502
|
5
|
50
|
33
|
|
|
31
|
if ( defined($cbrt_y) && defined($cbrt_x) ) { |
|
503
|
5
|
100
|
100
|
|
|
37
|
if ( ($cbrt_x == 1) && ($cbrt_y == 1) ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
504
|
1
|
|
|
|
|
11
|
push @factors, parse_from_string("$v2 - $v1"); |
|
505
|
1
|
|
|
|
|
9801
|
push @factors, parse_from_string("$v2^2 + $v2*$v1 + $v1^2"); |
|
506
|
|
|
|
|
|
|
} |
|
507
|
|
|
|
|
|
|
elsif ( $cbrt_y == 1 ) { |
|
508
|
1
|
|
|
|
|
9
|
push @factors, parse_from_string("$v2 - $cbrt_x*$v1"); |
|
509
|
1
|
|
|
|
|
10052
|
push @factors, parse_from_string("$v2^2 + $cbrt_x*$v2*$v1 + " . ($cbrt_x*$cbrt_x) . "*$v1^2"); |
|
510
|
|
|
|
|
|
|
} |
|
511
|
|
|
|
|
|
|
elsif ( $cbrt_x == 1 ) { |
|
512
|
1
|
|
|
|
|
8
|
push @factors, parse_from_string("$cbrt_y*$v2 - $v1"); |
|
513
|
1
|
|
|
|
|
9974
|
push @factors, parse_from_string(($cbrt_y*$cbrt_y) . "*$v2^2 + $cbrt_y*$v2*$v1 + $v1^2"); |
|
514
|
|
|
|
|
|
|
} |
|
515
|
|
|
|
|
|
|
else { |
|
516
|
2
|
|
|
|
|
18
|
push @factors, parse_from_string("$cbrt_y*$v2 - $cbrt_x*$v1"); |
|
517
|
2
|
|
|
|
|
22800
|
push @factors, parse_from_string(($cbrt_y*$cbrt_y) . "*$v2^2 + " . ($cbrt_x*$cbrt_y) . "*$v2*$v1 + " . ($cbrt_x*$cbrt_x) . "*$v1^2"); |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
5
|
|
|
|
|
87542
|
undef %n_terms; |
|
521
|
5
|
|
|
|
|
87
|
return @factors; |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
} |
|
526
|
|
|
|
|
|
|
|
|
527
|
43
|
50
|
|
|
|
137
|
if ( %n_terms ) { |
|
528
|
|
|
|
|
|
|
# rebuild expression and continue attempting to factor |
|
529
|
43
|
50
|
|
|
|
260
|
if ( !exists $n_terms{constant_accumulator} ) { |
|
530
|
|
|
|
|
|
|
# put the accumulator back in if necessary |
|
531
|
43
|
|
|
|
|
290
|
$n_terms{constant_accumulator} = $acc; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
43
|
|
|
|
|
310
|
my $nt = Math::Symbolic::Custom::Collect::build_summation_tree({ terms => \%n_terms, trees => \%n_funcs }); |
|
534
|
|
|
|
|
|
|
|
|
535
|
43
|
|
|
|
|
18232
|
my $factors_in = scalar(@factors); |
|
536
|
|
|
|
|
|
|
|
|
537
|
43
|
|
|
|
|
161
|
POLY_FACTOR: while ( defined $nt ) { |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# test for polynomial |
|
540
|
|
|
|
|
|
|
# FIXME: looping around test_polynomial() like this is slow |
|
541
|
65
|
|
|
|
|
549
|
my ($var, $coeffs, $disc, $roots) = $nt->test_polynomial(); |
|
542
|
|
|
|
|
|
|
|
|
543
|
65
|
50
|
|
|
|
3416563
|
last POLY_FACTOR unless defined $var; |
|
544
|
65
|
50
|
|
|
|
293
|
last POLY_FACTOR unless defined $coeffs; |
|
545
|
|
|
|
|
|
|
|
|
546
|
65
|
|
|
|
|
161
|
my $degree = scalar(@{$coeffs})-1; |
|
|
65
|
|
|
|
|
222
|
|
|
547
|
65
|
100
|
|
|
|
336
|
last POLY_FACTOR if $degree <= 1; |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# check if all the coefficients are constant integers |
|
550
|
41
|
|
|
|
|
109
|
my @co = @{$coeffs}; |
|
|
41
|
|
|
|
|
115
|
|
|
551
|
148
|
|
|
|
|
653
|
my @const_co = grep { $_ eq int($_) } |
|
552
|
148
|
|
|
|
|
916
|
map { $_->value() } |
|
553
|
41
|
|
|
|
|
121
|
grep { ref($_) eq 'Math::Symbolic::Constant'} @co; |
|
|
149
|
|
|
|
|
421
|
|
|
554
|
|
|
|
|
|
|
|
|
555
|
41
|
100
|
|
|
|
188
|
if ( scalar(@const_co) == scalar(@co) ) { |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# see if it trivially collapses to a binomial |
|
558
|
40
|
50
|
|
|
|
155
|
if ( $degree < 15 ) { # FIXME: Upper limit on degree for binomials?? |
|
559
|
|
|
|
|
|
|
|
|
560
|
40
|
|
|
|
|
127
|
my $const_term = $const_co[-1]; |
|
561
|
40
|
|
|
|
|
209
|
my $root = int_rt(abs($const_term), $degree); |
|
562
|
|
|
|
|
|
|
|
|
563
|
40
|
100
|
|
|
|
207
|
if ( defined $root ) { |
|
564
|
|
|
|
|
|
|
|
|
565
|
14
|
|
|
|
|
43
|
my @pos_matches; |
|
566
|
|
|
|
|
|
|
my @neg_matches; |
|
567
|
14
|
|
|
|
|
54
|
CHECK_BINOMIAL: foreach my $k (0..$degree) { |
|
568
|
|
|
|
|
|
|
|
|
569
|
49
|
|
|
|
|
200
|
my $bin_coeff = binomial_coefficient($degree, $k); |
|
570
|
|
|
|
|
|
|
|
|
571
|
49
|
|
|
|
|
114
|
my $bin_add = $bin_coeff * $root**$k; |
|
572
|
49
|
|
|
|
|
116
|
my $bin_sub = $bin_add; |
|
573
|
49
|
100
|
|
|
|
136
|
$bin_sub *= -1 if $k % 2 == 1; |
|
574
|
|
|
|
|
|
|
|
|
575
|
49
|
100
|
|
|
|
168
|
if ( $const_co[$k] == $bin_add ) { |
|
576
|
25
|
|
|
|
|
73
|
push @pos_matches, $const_co[$k]; |
|
577
|
|
|
|
|
|
|
} |
|
578
|
49
|
100
|
|
|
|
178
|
if ( $const_co[$k] == $bin_sub ) { |
|
579
|
24
|
|
|
|
|
75
|
push @neg_matches, $const_co[$k]; |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
} |
|
582
|
|
|
|
|
|
|
|
|
583
|
14
|
100
|
|
|
|
83
|
if ( scalar(@pos_matches) == scalar(@const_co) ) { |
|
|
|
100
|
|
|
|
|
|
|
584
|
4
|
|
|
|
|
42
|
push @factors, parse_from_string("$var + $root") for (1..$degree); |
|
585
|
4
|
|
|
|
|
75656
|
undef %n_terms; |
|
586
|
4
|
|
|
|
|
84
|
undef $nt; |
|
587
|
4
|
|
|
|
|
58
|
last POLY_FACTOR; |
|
588
|
|
|
|
|
|
|
} |
|
589
|
|
|
|
|
|
|
elsif ( scalar(@neg_matches) == scalar(@const_co) ) { |
|
590
|
2
|
|
|
|
|
22
|
push @factors, parse_from_string("$var - $root") for (1..$degree); |
|
591
|
2
|
|
|
|
|
51612
|
undef %n_terms; |
|
592
|
2
|
|
|
|
|
47
|
undef $nt; |
|
593
|
2
|
|
|
|
|
25
|
last POLY_FACTOR; |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# try rational root theorem |
|
599
|
34
|
|
|
|
|
176
|
my @potential_roots = get_rational_roots($const_co[0], $const_co[-1]); |
|
600
|
34
|
|
|
|
|
82
|
my $found_root = 0; |
|
601
|
34
|
|
|
|
|
88
|
TRY_RAT_ROOT: foreach my $root (@potential_roots) { |
|
602
|
|
|
|
|
|
|
|
|
603
|
214
|
|
|
|
|
1261
|
my $MS_root = parse_from_string($root); |
|
604
|
214
|
|
|
|
|
726149
|
my $p_val = $nt->value($var => $MS_root->value()); |
|
605
|
|
|
|
|
|
|
|
|
606
|
214
|
100
|
|
|
|
274347
|
if ( abs($p_val) < 1e-11 ) { # fix failing test on uselongdouble systems. |
|
607
|
|
|
|
|
|
|
|
|
608
|
22
|
|
|
|
|
243
|
$MS_root = $MS_root->to_collected(); |
|
609
|
22
|
|
|
|
|
16746
|
my ($full_expr, $divisor, $quotient, $remainder) = $nt->apply_synthetic_division($MS_root, $var); |
|
610
|
|
|
|
|
|
|
|
|
611
|
22
|
50
|
|
|
|
2238316
|
if ( $remainder->value() == 0 ) { |
|
612
|
22
|
|
|
|
|
236
|
$found_root = 1; |
|
613
|
22
|
|
|
|
|
57
|
push @factors, $divisor; |
|
614
|
22
|
|
|
|
|
99
|
$nt = $quotient; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
22
|
|
|
|
|
317
|
last TRY_RAT_ROOT; |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
|
|
621
|
34
|
100
|
|
|
|
475
|
unless ( $found_root ) { |
|
622
|
12
|
|
|
|
|
256
|
last POLY_FACTOR; |
|
623
|
|
|
|
|
|
|
} |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
} |
|
626
|
|
|
|
|
|
|
else { |
|
627
|
1
|
|
|
|
|
27
|
last POLY_FACTOR; |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
} # /POLY_FACTOR |
|
631
|
|
|
|
|
|
|
|
|
632
|
43
|
100
|
100
|
|
|
388
|
if ( ($factors_in < scalar(@factors)) && defined($nt) ) { |
|
633
|
|
|
|
|
|
|
|
|
634
|
19
|
|
|
|
|
145
|
my ($t3, $n_hr, $d_hr) = $nt->to_collected(); |
|
635
|
19
|
|
|
|
|
48389
|
$nt = $t3; |
|
636
|
19
|
|
|
|
|
92
|
%n_terms = %{ $n_hr->{terms} }; |
|
|
19
|
|
|
|
|
123
|
|
|
637
|
19
|
|
|
|
|
44
|
%n_funcs = %{ $n_hr->{trees} }; |
|
|
19
|
|
|
|
|
156
|
|
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
} |
|
641
|
|
|
|
|
|
|
|
|
642
|
43
|
100
|
66
|
|
|
445
|
if ( scalar(@factors) == 0 ) { |
|
|
|
100
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
# could not get any new factors, return the original expression |
|
644
|
7
|
|
|
|
|
65
|
return $t; |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
elsif ( %n_terms && (scalar(@factors) > 0) ) { |
|
647
|
|
|
|
|
|
|
# got some factors and a leftover expression, rebuild and return that too |
|
648
|
30
|
50
|
|
|
|
123
|
if ( !exists $n_terms{constant_accumulator} ) { |
|
649
|
0
|
|
|
|
|
0
|
$n_terms{constant_accumulator} = $acc; |
|
650
|
|
|
|
|
|
|
} |
|
651
|
30
|
|
|
|
|
187
|
my $nt = Math::Symbolic::Custom::Collect::build_summation_tree({ terms => \%n_terms, trees => \%n_funcs }); |
|
652
|
30
|
|
|
|
|
7157
|
push @factors, $nt; |
|
653
|
|
|
|
|
|
|
} |
|
654
|
|
|
|
|
|
|
|
|
655
|
36
|
|
|
|
|
350
|
return @factors; |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub factor { |
|
659
|
68
|
|
|
68
|
0
|
161
|
my ($n) = @_; |
|
660
|
|
|
|
|
|
|
|
|
661
|
68
|
|
|
|
|
132
|
$n = abs($n); |
|
662
|
68
|
|
|
|
|
121
|
my %factors; |
|
663
|
68
|
|
|
|
|
217
|
for my $i (1 .. int(sqrt($n))) { |
|
664
|
132
|
100
|
|
|
|
366
|
if ($n % $i == 0) { |
|
665
|
105
|
|
|
|
|
247
|
my $complement = $n / $i; |
|
666
|
105
|
|
|
|
|
318
|
$factors{$i}++; |
|
667
|
105
|
|
|
|
|
531
|
$factors{$complement}++; |
|
668
|
|
|
|
|
|
|
} |
|
669
|
|
|
|
|
|
|
} |
|
670
|
68
|
|
|
|
|
232
|
my @factors = keys %factors; |
|
671
|
|
|
|
|
|
|
|
|
672
|
68
|
|
|
|
|
263
|
return @factors; |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
sub get_rational_roots { |
|
676
|
34
|
|
|
34
|
0
|
163
|
my ($leading, $const_term) = @_; |
|
677
|
|
|
|
|
|
|
|
|
678
|
34
|
|
|
|
|
140
|
my @p_factors = factor($const_term); |
|
679
|
34
|
|
|
|
|
92
|
my @q_factors = factor($leading); |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
# Generate all combinations of p/q |
|
682
|
34
|
|
|
|
|
69
|
my %roots; |
|
683
|
34
|
|
|
|
|
85
|
for my $p (@p_factors) { |
|
684
|
121
|
|
|
|
|
215
|
for my $q (@q_factors) { |
|
685
|
|
|
|
|
|
|
# Add both positive and negative roots |
|
686
|
198
|
|
|
|
|
489
|
$roots{"$p / $q"}++; |
|
687
|
198
|
|
|
|
|
542
|
$roots{"-$p / $q"}++; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
} |
|
690
|
|
|
|
|
|
|
|
|
691
|
34
|
|
|
|
|
247
|
return keys %roots; |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
sub int_rt { |
|
695
|
|
|
|
|
|
|
# determine if there is an integer nth root of a number |
|
696
|
150
|
|
|
150
|
0
|
380
|
my ($v, $n) = @_; |
|
697
|
150
|
|
|
|
|
586
|
my $guess = $v**(1/$n); |
|
698
|
150
|
|
|
|
|
299
|
$guess = int($guess); |
|
699
|
150
|
100
|
|
|
|
606
|
return $guess if $guess**$n == $v; |
|
700
|
62
|
|
|
|
|
202
|
$guess += 1; |
|
701
|
62
|
50
|
|
|
|
198
|
return $guess if $guess**$n == $v; |
|
702
|
62
|
|
|
|
|
194
|
return undef; |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub factorial { |
|
706
|
63
|
|
|
63
|
0
|
125
|
my ($num) = @_; |
|
707
|
63
|
100
|
|
|
|
180
|
return 1 if $num <= 1; # 0! = 1! = 1 |
|
708
|
35
|
|
|
|
|
62
|
my $result = 1; |
|
709
|
35
|
|
|
|
|
134
|
$result *= $_ for (2..$num); |
|
710
|
35
|
|
|
|
|
79
|
return $result; |
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
sub binomial_coefficient { |
|
714
|
49
|
|
|
49
|
0
|
120
|
my ($n, $k) = @_; |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# Handle edge cases |
|
717
|
49
|
50
|
33
|
|
|
243
|
return 0 if $k < 0 || $k > $n; # C(n, k) = 0 if k < 0 or k > n |
|
718
|
49
|
100
|
100
|
|
|
206
|
return 1 if $k == 0 || $k == $n; # C(n, 0) = C(n, n) = 1 |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
# C(n, k) = n! / (k! * (n-k)!) |
|
721
|
21
|
|
|
|
|
71
|
my $numerator = factorial($n); |
|
722
|
21
|
|
|
|
|
52
|
my $denominator = factorial($k) * factorial($n - $k); |
|
723
|
21
|
|
|
|
|
61
|
return $numerator / $denominator; |
|
724
|
|
|
|
|
|
|
} |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
L |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
L |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
L |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=head1 AUTHOR |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
Matt Johnson, C<< >> |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Steffen Mueller, author of Math::Symbolic |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
This software is copyright (c) 2025 by Matt Johnson. |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
747
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=cut |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
1; |
|
752
|
|
|
|
|
|
|
__END__ |