File Coverage

blib/lib/Math/Polynomial.pm
Criterion Covered Total %
statement 561 568 98.7
branch 261 270 96.6
condition 102 104 98.0
subroutine 77 78 98.7
pod 58 58 100.0
total 1059 1078 98.2


line stmt bran cond sub pod time code
1             package Math::Polynomial;
2              
3 14     14   263192 use 5.006;
  14         155  
4 14     14   80 use strict;
  14         29  
  14         333  
5 14     14   78 use warnings;
  14         28  
  14         499  
6 14     14   79 use Carp qw(croak);
  14         26  
  14         2427  
7              
8             require overload;
9              
10             overload->import(
11             q{neg} => 'neg',
12             q{+} => _binary('add'),
13             q{-} => _binary('sub_'),
14             q{*} => _binary('mul'),
15             q{/} => _binary('div'),
16             q{%} => _binary('mod'),
17             q{**} => _lefty('pow'),
18             q{<<} => _lefty('shift_up'),
19             q{>>} => _lefty('shift_down'),
20             q{!} => 'is_zero',
21             q{bool} => 'is_nonzero',
22             q{==} => _binary('is_equal'),
23             q{!=} => _binary('is_unequal'),
24             q{""} => 'as_string',
25             q{fallback} => undef, # auto-generate trivial substitutions
26             );
27              
28             # ----- object definition -----
29              
30             # Math::Polynomial=ARRAY(...)
31              
32             # .......... index .......... # .......... value ..........
33 14     14   115 use constant _F_COEFF => 0; # coefficients arrayref, ascending degree
  14         43  
  14         1710  
34 14     14   107 use constant _F_ZERO => 1; # zero element of coefficient space
  14         26  
  14         859  
35 14     14   103 use constant _F_ONE => 2; # unit element of coefficient space
  14         29  
  14         727  
36 14     14   84 use constant _F_CONFIG => 3; # default stringification configuration
  14         25  
  14         734  
37 14     14   95 use constant _NFIELDS => 4;
  14         37  
  14         6899  
38              
39             # ----- static data -----
40              
41             our $VERSION = '1.021';
42             our $max_degree = 10_000; # limit for power operator
43              
44             # default values for as_string options
45             my @string_defaults = (
46             ascending => 0,
47             with_variable => 1,
48             fold_sign => 0,
49             fold_zero => 1,
50             fold_one => 1,
51             fold_exp_zero => 1,
52             fold_exp_one => 1,
53             convert_coeff => sub { "$_[0]" },
54             sign_of_coeff => undef,
55             plus => q{ + },
56             minus => q{ - },
57             leading_plus => q{},
58             leading_minus => q{- },
59             times => q{ },
60             power => q{^},
61             variable => q{x},
62             prefix => q{(},
63             suffix => q{)},
64             wrap => sub { $_[1] },
65             );
66              
67             my $global_string_config = {};
68              
69             my @tree_defaults = (
70             fold_sign => 0,
71             expand_power => 0,
72             group => sub { $_[0] },
73             sign_of_coeff => undef,
74             wrap => sub { $_[1] },
75             map {
76             my $key = $_;
77             ($key => sub { croak "missing parameter: $key" })
78             } qw(
79             variable
80             constant
81             negation
82             sum
83             difference
84             product
85             power
86             ),
87             );
88              
89             # ----- private/protected subroutines -----
90              
91             # binary operator wrapper generator
92             # generates functions to be called via overload:
93             # - upgrading a non-polynomial operand to a compatible polynomial
94             # - restoring the original operand order
95             sub _binary {
96 98     98   196 my ($method) = @_;
97             return sub {
98 5754     5754   19752 my ($this, $that, $reversed) = @_;
99 5754 100 100     12811 if (!ref($that) || !eval { $that->isa('Math::Polynomial') }) {
  5748         21376  
100 7         31 $that = $this->new($that);
101             }
102 5754 100       11654 if ($reversed) {
103 2         5 ($this, $that) = ($that, $this);
104             }
105 5754         12291 return $this->$method($that);
106 98         536 };
107             }
108              
109             # asymmetrically prototyped binary operator wrapper generator
110             # generates functions to be called via overload:
111             # - disallowing reverse order of operands
112             sub _lefty {
113 42     42   90 my ($method) = @_;
114             return sub {
115 294     294   3428 my ($this, $that, $reversed) = @_;
116 294 100       735 croak 'wrong operand type' if $reversed;
117 293         676 return $this->$method($that);
118 42         156 };
119             }
120              
121             # integer argument checker
122             # - make sure arguments are non-negative integer numbers
123             sub _check_int {
124 1050     1050   1803 foreach my $arg (@_) {
125 1099 100       1607 eval {
126 14     14   115 use warnings FATAL => 'all';
  14         38  
  14         92697  
127 1099         3372 $arg == abs int $arg
128             } or croak 'non-negative integer argument expected';
129             }
130 1048         1498 return;
131             }
132              
133             # ----- methods -----
134              
135             sub new {
136 10469     10469 1 46536 my ($this, @coeff) = @_;
137 10469         17516 my $class = ref $this;
138 10469         14501 my ($zero, $one, $config);
139 10469 100       16573 if ($class) {
140 10224         12517 (undef, $zero, $one, $config) = @{$this};
  10224         18799  
141             }
142             else {
143 245 100       433 my $sample = @coeff? $coeff[-1]: 1;
144 245         718 $zero = $sample - $sample;
145 245         2219 $one = $sample ** 0;
146 245         2411 $config = undef;
147 245         397 $class = $this;
148             }
149 10469   100     30725 while (@coeff && $zero == $coeff[-1]) {
150 1328         10689 pop @coeff;
151             }
152 10469         91734 return bless [\@coeff, $zero, $one, $config], $class;
153             }
154              
155             sub clone {
156 1     1 1 3 my ($this) = @_;
157 1         2 return bless [@{$this}], ref $this;
  1         5  
158             }
159              
160             sub monomial {
161 274     274 1 3654 my ($this, $degree, $coeff) = @_;
162 274         351 my $zero;
163 274         579 _check_int($degree);
164 274 100 100     1227 croak 'exponent too large'
165             if defined($max_degree) && $degree > $max_degree;
166 272 100       548 if (ref $this) {
167 261 100       489 if (!defined $coeff) {
168 253         451 $coeff = $this->coeff_one;
169             }
170 261         456 $zero = $this->coeff_zero;
171             }
172             else {
173 11 100       22 if (!defined $coeff) {
174 4         7 $coeff = 1;
175             }
176 11         17 $zero = $coeff - $coeff;
177             }
178 272         586 return $this->new(($zero) x $degree, $coeff);
179             }
180              
181             sub from_roots {
182 5     5 1 1663 my ($this, @roots) = @_;
183 5 100       25 my $one = ref($this)? $this->coeff_one: @roots? $roots[-1] ** 0: 1;
    100          
184 5         20 my $result = $this->new($one);
185 5         12 foreach my $root (@roots) {
186 8         20 $result = $result->mul_root($root);
187             }
188 5         13 return $result;
189             }
190              
191             sub string_config {
192 60     60 1 679 my ($this, $config) = @_;
193 60         110 my $have_arg = 2 <= @_;
194 60 100       142 if (ref $this) {
195 35 100       63 if ($have_arg) {
196 4         10 $this->[_F_CONFIG] = $config;
197             }
198             else {
199 31         64 $config = $this->[_F_CONFIG];
200             }
201             }
202             else {
203 25 100       43 if ($have_arg) {
204             # note: do not leave ultimate fallback configuration undefined
205 6   100     25 $global_string_config = $config || {};
206             }
207             else {
208 19         31 $config = $global_string_config;
209             }
210             }
211 60         419 return $config;
212             }
213              
214             sub interpolate {
215 92     92 1 2672 my ($this, $xvalues, $yvalues) = @_;
216 92 100 100     369 if (
      100        
217 90         157 !ref($xvalues) || !ref($yvalues) || @{$xvalues} != @{$yvalues}
  90         217  
218             ) {
219 3         296 croak 'usage: $q = $p->interpolate([$x1, $x2, ...], [$y1, $y2, ...])';
220             }
221 89 100       133 return $this->new if !@{$xvalues};
  89         183  
222 87         140 my @alpha = @{$yvalues};
  87         141  
223 87         172 my $result = $this->new($alpha[0]);
224 87         187 my $aux = $result->monomial(0);
225 87         154 my $zero = $result->coeff_zero;
226 87         202 for (my $k=1; $k<=$#alpha; ++$k) {
227 172         374 for (my $j=$#alpha; $j>=$k; --$j) {
228 260         2645 my $dx = $xvalues->[$j] - $xvalues->[$j-$k];
229 260 100       2769 croak 'x values not disjoint' if $zero == $dx;
230 259         1838 $alpha[$j] = ($alpha[$j] - $alpha[$j-1]) / $dx;
231             }
232 171         3495 $aux = $aux->mul_root($xvalues->[$k-1]);
233 171         475 $result += $aux->mul_const($alpha[$k]);
234             }
235 86         266 return $result;
236             }
237              
238             sub coeff {
239 58156     58156 1 92007 my ($this, $degree) = @_;
240 58156 100       94228 if (defined $degree) {
241             return
242 50011 100 100     83593 0 <= $degree && $degree < @{$this->[_F_COEFF]}?
243             $this->[_F_COEFF]->[$degree]:
244             $this->[_F_ZERO];
245             }
246 8145 100       13672 croak 'array context required if called without argument' if !wantarray;
247 8144         10640 return @{$this->[_F_COEFF]};
  8144         18331  
248             }
249              
250             sub coefficients {
251 11     11 1 104 my ($this) = @_;
252 11 100       85 croak 'array context required' if !wantarray;
253 10 100       16 return $this->is_zero? ($this->coeff_zero): $this->coeff;
254             }
255              
256 19220     19220 1 23604 sub degree { return $#{ $_[0]->[_F_COEFF] }; }
  19220         37882  
257 1147     1147 1 2457 sub coeff_zero { return $_[0]->[_F_ZERO]; }
258 4159     4159 1 8229 sub coeff_one { return $_[0]->[_F_ONE]; }
259              
260             sub proper_degree {
261 2     2 1 5 my ($this) = @_;
262 2         4 my $degree = $this->degree;
263 2 100       9 return 0 <= $degree? $degree: undef;
264             }
265              
266             sub number_of_terms {
267 2     2 1 47 my ($this) = @_;
268 2         4 my $zero = $this->coeff_zero;
269 2         10 return scalar grep { $zero != $_ } $this->coeff;
  3         9  
270             }
271              
272             sub evaluate {
273 263     263 1 9855 my ($this, $x) = @_;
274 263         446 my $i = $this->degree;
275 263 100       569 my $result = 0 <= $i? $this->coeff($i): $this->coeff_zero;
276 263         515 while (0 <= --$i) {
277 426         2409 $result = $x * $result + $this->coeff($i);
278             }
279 263         2900 return $result;
280             }
281              
282             sub nest {
283 4     4 1 376 my ($this, $that) = @_;
284 4         14 my $i = $this->degree;
285 4 100       17 my $result = $that->new(0 <= $i? $this->coeff($i): ());
286 4         12 while (0 <= --$i) {
287 6         17 $result = $result->mul($that)->add_const($this->coeff($i));
288             }
289 4         11 return $result;
290             }
291              
292             sub unnest {
293 5     5 1 146 my ($this, $that) = @_;
294 5 100       12 return undef if $that->degree <= 0;
295 3         6 my @coeff = ();
296 3         6 my $q = $this;
297 3         11 while ($q) {
298 7         19 ($q, my $r) = $q->divmod($that);
299 7 100       17 return undef if $r->degree > 0;
300 6         12 push @coeff, $r->coeff(0);
301             }
302 2         6 return $this->new(@coeff);
303             }
304              
305             sub mirror {
306 16     16 1 208 my ($this) = @_;
307 16         39 my $i = 0;
308 16 100       35 return $this->new( map { $i++ & 1? -$_: $_ } $this->coeff );
  47         142  
309             }
310              
311             sub is_zero {
312 1617     1617 1 2807 my ($this) = @_;
313 1617         2588 return $this->degree < 0;
314             }
315              
316             sub is_nonzero {
317 196     196 1 732 my ($this) = @_;
318 196         346 return $this->degree >= 0;
319             }
320              
321             sub is_equal {
322 472     472 1 1645 my ($this, $that) = @_;
323 472         809 my $i = $this->degree;
324 472         812 my $eq = $i == $that->degree;
325 472   100     1535 while ($eq && 0 <= $i) {
326 1020         1640 $eq = $this->coeff($i) == $that->coeff($i);
327 1020         7450 --$i;
328             }
329 472         1437 return $eq;
330             }
331              
332             sub is_unequal {
333 948     948 1 1557 my ($this, $that) = @_;
334 948         1546 my $i = $this->degree;
335 948         1557 my $eq = $i == $that->degree;
336 948   100     2167 while ($eq && 0 <= $i) {
337 39         85 $eq = $this->coeff($i) == $that->coeff($i);
338 39         226 --$i;
339             }
340 948         2404 return !$eq;
341             }
342              
343             sub is_even {
344 6     6 1 130 my ($this) = @_;
345 6         50 return $this->is_equal($this->mirror);
346             }
347              
348             sub is_odd {
349 6     6 1 18 my ($this) = @_;
350 6         17 return $this->is_equal($this->mirror->neg);
351             }
352              
353             sub neg {
354 8     8 1 122 my ($this) = @_;
355 8 100       33 return $this if $this->degree < 0;
356 6         16 return $this->new( map { -$_ } $this->coeff );
  20         50  
357             }
358              
359             sub add {
360 180     180 1 308 my ($this, $that) = @_;
361 180         335 my $this_d = $this->degree;
362 180         305 my $that_d = $that->degree;
363 180 100       384 my $min_d = $this_d <= $that_d? $this_d: $that_d;
364             return $this->new(
365 146         816 (map { $this->coeff($_) + $that->coeff($_) } 0..$min_d),
366 68         118 (map { $this->coeff($_) } $that_d+1 .. $this_d),
367 180         400 (map { $that->coeff($_) } $this_d+1 .. $that_d),
  167         1415  
368             );
369             }
370              
371             sub sub_ {
372 1299     1299 1 2202 my ($this, $that) = @_;
373 1299         2137 my $this_d = $this->degree;
374 1299         2088 my $that_d = $that->degree;
375 1299 100       2396 my $min_d = $this_d <= $that_d? $this_d: $that_d;
376             return $this->new(
377 781         3309 (map { $this->coeff($_) - $that->coeff($_) } 0..$min_d),
378 500         2407 (map { $this->coeff($_) } $that_d+1 .. $this_d),
379 1299         2591 (map { -$that->coeff($_) } $this_d+1 .. $that_d),
  1974         9359  
380             );
381             }
382              
383             sub mul {
384 3332     3332 1 5542 my ($this, $that) = @_;
385 3332         5457 my $this_d = $this->degree;
386 3332 100       6569 return $this if $this_d < 0;
387 3106         4906 my $that_d = $that->degree;
388             return $this->new(
389             map {
390 3106 100       7570 my ($i, $j) = $_ <= $this_d? ($_, 0): ($this_d, $_-$this_d);
  12165 100       24119  
391 12165         18960 my $sum = $this->coeff($i) * $that->coeff($j);
392 12165   100     84408 while ($i > 0 && $j < $that_d) {
393 7153         23238 $sum += $this->coeff(--$i) * $that->coeff(++$j);
394             }
395             $sum
396 12165         74660 } $that_d < 0? (): (0 .. $this_d+$that_d)
397             );
398             }
399              
400             sub _divmod {
401 3399     3399   5117 my ($this, $that) = @_;
402 3399         5965 my @den = $that->coeff;
403 3399 100       7498 @den or croak 'division by zero polynomial';
404 3387         4997 my $hd = pop @den;
405 3387 100       5937 if ($that->is_monic) {
406 2959         21872 undef $hd;
407             }
408 3387         8335 my @rem = $this->coeff;
409 3387         5096 my @quot = ();
410 3387         5357 my $i = $#rem - @den;
411 3387         6755 while (0 <= $i) {
412 7285         46086 my $q = pop @rem;
413 7285 100       12298 if (defined $hd) {
414 922         1658 $q /= $hd;
415             }
416 7285         14142 $quot[$i] = $q;
417 7285         9395 my $j = $i--;
418 7285         11648 foreach my $d (@den) {
419 15664         103517 $rem[$j++] -= $q * $d;
420             }
421             }
422 3387         29939 return (\@quot, \@rem);
423             }
424              
425             sub divmod {
426 568     568 1 1711 my ($this, $that) = @_;
427 568 100       1128 croak 'array context required' if !wantarray;
428 567         977 my ($quot, $rem) = _divmod($this, $that);
429 565 100       913 return ($this->new(@{$quot}), @{$quot}? $this->new(@{$rem}): $this);
  565         1041  
  565         1085  
  560         1038  
430             }
431              
432             sub div {
433 122     122 1 477 my ($this, $that) = @_;
434 122         230 my ($quot) = _divmod($this, $that);
435 120         208 return $this->new(@{$quot});
  120         273  
436             }
437              
438             sub mod {
439 2710     2710 1 4355 my ($this, $that) = @_;
440 2710         4900 my ($quot, $rem) = _divmod($this, $that);
441 2702 100       3787 return @{$quot}? $this->new(@{$rem}): $this;
  2702         5433  
  2127         3995  
442             }
443              
444             sub mmod {
445 10     10 1 1237 my ($this, $that) = @_;
446 10         26 my @den = $that->coeff;
447 10 100       182 @den or croak 'division by zero polynomial';
448 8         15 my $hd = pop @den;
449 8 100       17 if ($that->is_monic) {
450 1         3 undef $hd;
451             }
452 8         20 my @rem = $this->coeff;
453 8         15 my $i = $#rem - @den;
454 8         19 while (0 <= $i) {
455 9         13 my $q = pop @rem;
456 9 100       62 if (defined $hd) {
457 7         21 foreach my $r (@rem) {
458 9         19 $r *= $hd;
459             }
460             }
461 9         13 my $j = $i--;
462 9         14 foreach my $d (@den) {
463 7         20 $rem[$j++] -= $q * $d;
464             }
465             }
466 8         18 return $this->new(@rem);
467             }
468              
469             sub add_const {
470 8     8 1 257 my ($this, $const) = @_;
471 8 100       18 return $this->new($const) if $this->is_zero;
472 6         13 my $i = 0;
473 6 100       14 return $this->new( map { $i++? $_: $_ + $const } $this->coeff );
  22         58  
474             }
475              
476             sub sub_const {
477 165     165 1 539 my ($this, $const) = @_;
478 165 100       308 return $this->new(-$const) if $this->is_zero;
479 164         271 my $i = 0;
480 164 100       285 return $this->new( map { $i++? $_: $_ - $const } $this->coeff );
  2106         4408  
481             }
482              
483             sub mul_const {
484 357     357 1 807 my ($this, $factor) = @_;
485 357         585 return $this->new( map { $_ * $factor } $this->coeff );
  723         4160  
486             }
487              
488             sub div_const {
489 119     119 1 501 my ($this, $divisor) = @_;
490 119 100       219 croak 'division by zero' if $this->coeff_zero == $divisor;
491 118         847 return $this->new( map { $_ / $divisor } $this->coeff );
  313         1582  
492             }
493              
494             sub mul_root {
495 182     182 1 544 my ($this, $root) = @_;
496 182         355 return $this->shift_up(1)->sub_($this->mul_const($root));
497             }
498              
499             sub _divmod_root {
500 18     18   32 my ($this, $root) = @_;
501 18         38 my $i = $this->degree;
502 18 100       53 my $rem = $this->coeff($i < 0? 0: $i);
503 18         34 my @quot;
504 18         39 while (0 <= --$i) {
505 36         61 $quot[$i] = $rem;
506 36         58 $rem = $root * $rem + $this->coeff($i);
507             }
508 18         46 return (\@quot, $rem);
509             }
510              
511             sub divmod_root {
512 7     7 1 373 my ($this, $root) = @_;
513 7 100       106 croak 'array context required' if !wantarray;
514 6         14 my ($quot, $rem) = _divmod_root($this, $root);
515 6         11 return ($this->new(@{$quot}), $this->new($rem));
  6         13  
516             }
517              
518             sub div_root {
519 12     12 1 1147 my ($this, $root, $check) = @_;
520 12         33 my ($quot, $rem) = _divmod_root($this, $root);
521 12 100 100     33 if ($check && $this->coeff_zero != $rem) {
522 3         350 croak 'non-zero remainder';
523             }
524 9         14 return $this->new(@{$quot});
  9         22  
525             }
526              
527             sub is_monic {
528 3404     3404 1 5272 my ($this) = @_;
529 3404         5292 my $degree = $this->degree;
530 3404   100     8465 return 0 <= $degree && $this->coeff_one == $this->coeff($degree);
531             }
532              
533             sub monize {
534 7     7 1 194 my ($this) = @_;
535 7 100 100     20 return $this if $this->is_zero || $this->is_monic;
536 4         31 return $this->div_const($this->coeff($this->degree));
537             }
538              
539             sub pow {
540 20     20 1 53 my ($this, $exp) = @_;
541 20         54 _check_int($exp);
542 18         45 my $degree = $this->degree;
543 18 100       63 return $this->new($this->coeff_one) if 0 == $exp;
544 12 100       31 return $this if 0 > $degree;
545 9 100       49 return $this->new($this->coeff(0) ** $exp) if 0 == $degree;
546 6 100 100     211 croak 'exponent too large'
547             if defined($max_degree) && $degree * $exp > $max_degree;
548 5         9 my @binary = ();
549 5         14 while ($exp > 1) {
550 4         11 push @binary, 1 & $exp;
551 4         11 $exp >>= 1;
552             }
553 5         6 my $result = $this;
554 5         12 while (@binary) {
555 4         11 $result *= $result;
556 4 100       15 $result *= $this if pop @binary;
557             }
558 5         23 return $result;
559             }
560              
561             sub pow_mod {
562 138     138 1 3192 my ($this, $exp, $that) = @_;
563 138         338 _check_int($exp);
564 138         284 $this = $this->mod($that);
565 132         261 my $this_d = $this->degree;
566 132 100       232 return $this->new if 0 == $that->degree;
567 129 100       298 return $this->new($this->coeff_one) if 0 == $exp;
568 124 100       283 return $this if 0 > $this_d;
569 122 100       235 return $this->new($this->coeff(0) ** $exp) if 0 == $this_d;
570 108         170 my @binary = ();
571 108         207 while ($exp > 1) {
572 481         668 push @binary, 1 & $exp;
573 481         787 $exp >>= 1;
574             }
575 108         199 my $result = $this;
576 108         564 while (@binary) {
577 481         1015 $result *= $result;
578 481 100       1246 $result *= $this if pop @binary;
579 481         981 $result %= $that;
580             }
581 108         282 return $result;
582             }
583              
584             sub exp_mod {
585 99     99 1 914 my ($this, $exp) = @_;
586 99         209 _check_int($exp);
587 99 100       165 return $this->new if 0 == $this->degree;
588 93 100       179 return $this->new($this->coeff_one) if 0 == $exp;
589 91         131 my @binary = ();
590 91         194 while ($exp > 1) {
591 346         469 push @binary, 1 & $exp;
592 346         564 $exp >>= 1;
593             }
594 91         196 my $result = $this->new($this->coeff_zero, $this->coeff_one);
595 91         197 while (@binary) {
596 346         669 $result *= $result;
597 346 100       730 if (pop @binary) {
598 166         245 local $max_degree;
599 166         318 $result <<= 1;
600             }
601 346         652 $result %= $this;
602             }
603 91         248 return $result;
604             }
605              
606             sub shift_up {
607 450     450 1 718 my ($this, $exp) = @_;
608 450         903 _check_int($exp);
609 450 100 100     1040 croak 'exponent too large' if
610             defined($max_degree) && $this->degree + $exp > $max_degree;
611 449 100       878 return $this if !$exp;
612 447         896 return $this->new(($this->coeff_zero) x $exp, $this->coeff);
613             }
614              
615             sub shift_down {
616 5     5 1 11 my ($this, $exp) = @_;
617 5         13 _check_int($exp);
618 5 100       15 return $this if !$exp;
619 3         11 return $this->new( map { $this->coeff($_) } $exp .. $this->degree );
  1         5  
620             }
621              
622             sub inflate {
623 12     12 1 697 my ($this, $exp) = @_;
624 12         28 my $degree = $this->degree;
625 12 100       35 return $this if $degree <= 0;
626 6         16 _check_int($exp);
627 6 100 100     98 croak 'exponent too large' if
628             defined($max_degree) && $degree * $exp > $max_degree;
629 5 100       16 return $this->new($this->evaluate($this->coeff_one)) if !$exp;
630 4         11 my @zeroes = ($this->coeff_zero) x ($exp - 1);
631 4 100       13 return $this if !@zeroes;
632 3         9 my ($const, @coeff) = $this->coeff;
633 3         8 return $this->new($const, map {@zeroes, $_} @coeff);
  10         35  
634             }
635              
636             sub deflate {
637 9     9 1 296 my ($this, $exp) = @_;
638 9         24 _check_int($exp);
639 9 100       20 return undef if !$exp;
640 7 100       18 return $this if $exp <= 1;
641 4         9 my $degree = $this->degree;
642 4 100       11 return $this if $degree <= 0;
643             my @coeff =
644             map {
645 2         7 my $c = $this->coeff($_);
  11         20  
646 11 100       33 !($_ % $exp)? $c: !$c? (): return undef
    100          
647             } 0 .. $degree;
648 1         5 return $this->new(@coeff);
649             }
650              
651             sub slice {
652 49     49 1 544 my ($this, $start, $count) = @_;
653 49         97 _check_int($start, $count);
654 49         85 my $degree = $this->degree;
655 49         76 my $end = $start+$count-1;
656 49 100       82 if ($degree <= $end) {
657 34 100       57 return $this if 0 == $start;
658 32         46 $end = $degree;
659             }
660 47         76 return $this->new( map { $this->coeff($_) } $start .. $end );
  60         86  
661             }
662              
663             sub differentiate {
664 3     3 1 11 my ($this) = @_;
665 3         7 my $n = $this->coeff_zero;
666 3         7 my $one = $this->coeff_one;
667             return $this->new(
668 3         9 map { $this->coeff($_) * ($n += $one) } 1..$this->degree
  12         212  
669             );
670             }
671              
672             sub integrate {
673 7     7 1 276 my ($this, $const) = @_;
674 7         17 my $zero = $this->coeff_zero;
675 7         11 my $one = $this->coeff_one;
676 7         11 my $n = $zero;
677 7 100       16 if (!defined $const) {
678 5         7 $const = $zero;
679             }
680             return $this->new(
681             $const,
682             map {
683 7         15 $n += $one;
  31         277  
684 31         227 my $c = $this->coeff($_);
685 31 100       81 $zero == $c? $c: $c / $n
686             } 0..$this->degree
687             );
688             }
689              
690             sub definite_integral {
691 2     2 1 154 my ($this, $lower, $upper) = @_;
692 2         6 my $ad = $this->integrate;
693 2         6 return $ad->evaluate($upper) - $ad->evaluate($lower);
694             }
695              
696             sub _make_ltz {
697 99     99   180 my ($config, $zero) = @_;
698 99 100       255 return 0 if !$config->{'fold_sign'};
699 45         70 my $sgn = $config->{'sign_of_coeff'};
700             return
701             defined($sgn)?
702 11     11   25 sub { $sgn->($_[0]) < 0 }:
703 45 100   82   203 sub { $_[0] < $zero };
  82         219  
704             }
705              
706             sub as_string {
707 75     75 1 2507 my ($this, $params) = @_;
708             my %config = (
709             @string_defaults,
710 75 100 100     142 %{$params || $this->string_config || (ref $this)->string_config},
  75         691  
711             );
712 75         307 my $max_exp = $this->degree;
713 75 100       176 if ($max_exp < 0) {
714 24         34 $max_exp = 0;
715             }
716 75         126 my $result = q{};
717 75         140 my $zero = $this->coeff_zero;
718 75         164 my $ltz = _make_ltz(\%config, $zero);
719 75         164 my $one = $this->coeff_one;
720 75         128 my $with_variable = $config{'with_variable'};
721 75 100       270 foreach my $exp ($config{'ascending'}? 0..$max_exp: reverse 0..$max_exp) {
722 208         384 my $coeff = $this->coeff($exp);
723              
724             # skip term?
725 208 100 100     887 if (
      100        
      100        
726             $with_variable &&
727             $exp < $max_exp &&
728             $config{'fold_zero'} &&
729             $coeff == $zero
730             ) {
731 43         140 next;
732             }
733              
734             # plus/minus
735 165 100 100     474 if ($ltz && $ltz->($coeff)) {
736 17         103 $coeff = -$coeff;
737 17 100       74 $result .= $config{q[] eq $result? 'leading_minus': 'minus'};
738             }
739             else{
740 148 100       448 $result .= $config{q[] eq $result? 'leading_plus': 'plus'};
741             }
742              
743             # coefficient
744 165 100 100     943 if (
      100        
      100        
      100        
745             !$with_variable ||
746             !$config{'fold_one'} ||
747             0 == $exp && $config{'fold_exp_zero'} ||
748             $one != $coeff
749             ) {
750 113         263 $result .= $config{'convert_coeff'}->($coeff);
751 113 100       474 next if !$with_variable;
752 99 100 100     303 if (0 != $exp || !$config{'fold_exp_zero'}) {
753 44         81 $result .= $config{'times'};
754             }
755             }
756              
757             # variable and exponent
758 151 100 100     583 if (0 != $exp || !$config{'fold_exp_zero'}) {
759 96         146 $result .= $config{'variable'};
760 96 100 100     274 if (1 != $exp || !$config{'fold_exp_one'}) {
761 52         128 $result .= $config{'power'} . $exp;
762             }
763             }
764             }
765             return join q{},
766             $config{'prefix'},
767             $config{'wrap'}->($this, $result),
768 75         222 $config{'suffix'};
769             }
770              
771             sub as_horner_tree {
772 12     12 1 30 my ($this, $params) = @_;
773 12         21 my %config = (@tree_defaults, %{$params});
  12         100  
774 12         38 my $exp = $this->degree;
775 12 100       32 if ($exp < 0) {
776 1         2 $exp = 0;
777             }
778 12         21 my $zero = $this->coeff_zero;
779 12         28 my $ltz = _make_ltz(\%config, $zero);
780 12         28 my $coeff = $this->coeff($exp);
781 12   100     33 my $first_is_neg = $ltz && $ltz->($coeff);
782 12 100       22 if ($first_is_neg) {
783 3         8 $coeff = -$coeff;
784             }
785             my $result =
786             $exp && $this->coeff_one == $coeff? undef:
787 12 100 100     38 $config{'constant'}->($coeff);
788 12         38 my $is_sum = 0;
789 12         23 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
790 12 100       26 my $variable = $var_is_dynamic? undef: $config{'variable'};
791 12         24 while (0 <= --$exp) {
792 18         51 $coeff = $this->coeff($exp);
793 18 100       35 if ($is_sum) {
794 6         13 $result = $config{'group'}->($result);
795             }
796 18 100       44 if ($var_is_dynamic) {
797 6         12 $variable = $config{'variable'}->();
798             }
799 18         43 my $power = $variable;
800 18 100       33 if ($config{'expand_power'}) {
801 8         11 $is_sum = $zero != $coeff;
802             }
803             else {
804 10         26 my $skip = 0;
805 10         21 while ($zero == $coeff) {
806 10         13 ++$skip;
807 10 100       21 last if $skip > $exp;
808 8         14 $coeff = $this->coeff($exp - $skip);
809             }
810 10 100       21 if ($skip) {
811 8         11 $exp -= $skip;
812 8 100       23 ++$skip if 0 <= $exp;
813 8 100       28 $power = $config{'power'}->($variable, $skip) if 1 < $skip;
814             }
815 10         37 $is_sum = 0 <= $exp;
816             }
817             $result =
818 18 100       51 defined($result)? $config{'product'}->($result, $power): $power;
819 18 100       72 if ($first_is_neg) {
820 2         6 $result = $config{'negation'}->($result);
821 2         9 $first_is_neg = 0;
822             }
823 18 100       38 if ($is_sum) {
824 12   100     31 my $is_neg = $ltz && $ltz->($coeff);
825 12 100       21 if ($is_neg) {
826 4         8 $coeff = -$coeff;
827             }
828 12         22 my $const = $config{'constant'}->($coeff);
829 12 100       56 $result = $config{$is_neg? 'difference': 'sum'}->($result, $const);
830             }
831             }
832 12 100       42 if ($first_is_neg) {
833 1         3 $result = $config{'negation'}->($result);
834             }
835 12         84 return $result;
836             }
837              
838             sub as_power_sum_tree {
839 12     12 1 28 my ($this, $params) = @_;
840 12         24 my %config = (@tree_defaults, %{$params});
  12         96  
841 12         32 my $exp = $this->degree;
842 12 100       31 if ($exp < 0) {
843 1         3 $exp = 0;
844             }
845 12         20 my $zero = $this->coeff_zero;
846 12         37 my $ltz = _make_ltz(\%config, $zero);
847 12         25 my $one = $this->coeff_one;
848 12         18 my $result = undef;
849 12         25 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
850 12 100       24 my $variable = $var_is_dynamic? undef: $config{'variable'};
851 12         27 while (0 <= $exp) {
852 34         62 my $coeff = $this->coeff($exp);
853              
854             # skip term?
855 34 100 100     94 next if defined($result) && $zero == $coeff;
856              
857             # variable and exponent
858 22         31 my $term = undef;
859 22 100       40 if (0 != $exp) {
860 12 100       19 if ($var_is_dynamic) {
861 3         11 $variable = $config{'variable'}->();
862             }
863 12         38 $term = $variable;
864 12 100       24 if (1 != $exp) {
865 9 100       22 if ($config{'expand_power'}) {
866 2         5 my $todo = $exp-1;
867 2         6 for (my $tmp=$exp; $tmp>1; --$tmp) {
868 2 100       5 if ($var_is_dynamic) {
869 1         2 $variable = $config{'variable'}->();
870             }
871 2         7 $term = $config{'product'}->($term, $variable);
872             }
873             }
874             else {
875 7         19 $term = $config{'power'}->($term, $exp);
876             }
877             }
878             }
879              
880             # sign and coefficient
881 22   100     78 my $is_neg = $ltz && $ltz->($coeff);
882 22 100       82 if ($is_neg) {
883 7         10 $coeff = -$coeff;
884             }
885 22 100 100     66 if (0 == $exp || $one != $coeff) {
886 16         33 my $const = $config{'constant'}->($coeff);
887 16 100       66 $term = 0 == $exp? $const: $config{'product'}->($const, $term);
888             }
889              
890             # summation
891 22 100       50 if (defined $result) {
892 10 100       27 $result = $config{$is_neg? 'difference': 'sum'}->($result, $term);
893             }
894             else {
895 12 100       32 $result = $is_neg? $config{'negation'}->($term): $term;
896             }
897             }
898             continue {
899 34         98 --$exp;
900             }
901 12         71 return $result;
902             }
903              
904             sub gcd {
905 3     3 1 167 my ($this, $that, $mod) = @_;
906 3 50       12 defined $mod or $mod = 'mod';
907 3         21 my $mod_op = $this->can($mod);
908 3 50       10 $mod_op or croak "no such method: $mod";
909 3         9 my ($this_d, $that_d) = ($this->degree, $that->degree);
910 3 50       12 if ($this_d < $that_d) {
911 3         10 ($this, $that) = ($that, $this);
912 3         8 ($this_d, $that_d) = ($that_d, $this_d);
913             }
914 3         11 while (0 <= $that_d) {
915 6         18 ($this, $that) = ($that, $this->$mod_op($that));
916 6         16 ($this_d, $that_d) = ($that_d, $that->degree);
917 6 50       23 $this_d > $that_d or croak 'bad modulo operator';
918             }
919 3         21 return $this;
920             }
921              
922             sub xgcd {
923 224     224 1 630 my ($this, $that) = @_;
924 224 50       435 croak 'array context required' if !wantarray;
925 224         465 my ($d1, $d2) = ($this->new($this->coeff_one), $this->new);
926 224 50       443 if ($this->degree < $that->degree) {
927 0         0 ($this, $that) = ($that, $this);
928 0         0 ($d1, $d2) = ($d2, $d1);
929             }
930 224         370 my ($m1, $m2) = ($d2, $d1);
931 224         440 while (!$that->is_zero) {
932 550         1215 my ($div, $mod) = $this->divmod($that);
933 550         1304 ($this, $that) = ($that, $mod);
934 550         1170 ($d1, $d2, $m1, $m2) =
935             ($m1, $m2, $d1->sub_($m1->mul($div)), $d2->sub_($m2->mul($div)));
936             }
937 224         709 return ($this, $d1, $d2, $m1, $m2);
938             }
939              
940             sub lcm {
941 0     0 1 0 my $result = shift;
942 0         0 foreach my $that (@_) {
943 0         0 my $gcd = $result->gcd($that);
944 0 0       0 $result *= $that->div($gcd) if !$that->is_equal($gcd);
945             }
946 0         0 return $result;
947             }
948              
949             sub inv_mod {
950 112     112 1 636 my ($this, $that) = @_;
951 112         298 my ($d, $d2) = ($that->xgcd($this))[0, 2];
952 112 50 33     301 croak 'division by zero polynomial' if $that->is_zero || $d2->is_zero;
953 112         245 return $d2->div_const($d->coeff($d->degree));
954             }
955              
956             1;
957             __END__