File Coverage

blib/lib/Math/Polynomial.pm
Criterion Covered Total %
statement 542 549 98.7
branch 247 256 96.4
condition 102 104 98.0
subroutine 75 76 98.6
pod 56 56 100.0
total 1022 1041 98.1


line stmt bran cond sub pod time code
1             package Math::Polynomial;
2              
3 14     14   200359 use 5.006;
  14         115  
4 14     14   62 use strict;
  14         23  
  14         255  
5 14     14   64 use warnings;
  14         19  
  14         400  
6 14     14   70 use Carp qw(croak);
  14         22  
  14         1896  
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   89 use constant _F_COEFF => 0; # coefficients arrayref, ascending degree
  14         28  
  14         1247  
34 14     14   82 use constant _F_ZERO => 1; # zero element of coefficient space
  14         22  
  14         606  
35 14     14   78 use constant _F_ONE => 2; # unit element of coefficient space
  14         20  
  14         576  
36 14     14   82 use constant _F_CONFIG => 3; # default stringification configuration
  14         22  
  14         596  
37 14     14   74 use constant _NFIELDS => 4;
  14         20  
  14         5300  
38              
39             # ----- static data -----
40              
41             our $VERSION = '1.018';
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   166 my ($method) = @_;
97             return sub {
98 5747     5747   16035 my ($this, $that, $reversed) = @_;
99 5747 100 100     10268 if (!ref($that) || !eval { $that->isa('Math::Polynomial') }) {
  5741         18093  
100 7         25 $that = $this->new($that);
101             }
102 5747 100       9497 if ($reversed) {
103 2         3 ($this, $that) = ($that, $this);
104             }
105 5747         10447 return $this->$method($that);
106 98         390 };
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   65 my ($method) = @_;
114             return sub {
115 294     294   2721 my ($this, $that, $reversed) = @_;
116 294 100       537 croak 'wrong operand type' if $reversed;
117 293         560 return $this->$method($that);
118 42         124 };
119             }
120              
121             # integer argument checker
122             # - make sure arguments are non-negative integer numbers
123             sub _check_int {
124 1041     1041   1525 foreach my $arg (@_) {
125 1090 100       1302 eval {
126 14     14   91 use warnings FATAL => 'all';
  14         40  
  14         69542  
127 1090         2707 $arg == abs int $arg
128             } or croak 'non-negative integer argument expected';
129             }
130 1039         1205 return;
131             }
132              
133             # ----- methods -----
134              
135             sub new {
136 10439     10439 1 37540 my ($this, @coeff) = @_;
137 10439         14228 my $class = ref $this;
138 10439         11858 my ($zero, $one, $config);
139 10439 100       13714 if ($class) {
140 10194         10401 (undef, $zero, $one, $config) = @{$this};
  10194         15345  
141             }
142             else {
143 245 100       384 my $sample = @coeff? $coeff[-1]: 1;
144 245         530 $zero = $sample - $sample;
145 245         1758 $one = $sample ** 0;
146 245         1912 $config = undef;
147 245         328 $class = $this;
148             }
149 10439   100     25070 while (@coeff && $zero == $coeff[-1]) {
150 1308         8918 pop @coeff;
151             }
152 10439         77599 return bless [\@coeff, $zero, $one, $config], $class;
153             }
154              
155             sub clone {
156 1     1 1 2 my ($this) = @_;
157 1         2 return bless [@{$this}], ref $this;
  1         4  
158             }
159              
160             sub monomial {
161 274     274 1 3053 my ($this, $degree, $coeff) = @_;
162 274         305 my $zero;
163 274         466 _check_int($degree);
164 274 100 100     976 croak 'exponent too large'
165             if defined($max_degree) && $degree > $max_degree;
166 272 100       435 if (ref $this) {
167 261 100       377 if (!defined $coeff) {
168 253         348 $coeff = $this->coeff_one;
169             }
170 261         383 $zero = $this->coeff_zero;
171             }
172             else {
173 11 100       19 if (!defined $coeff) {
174 4         5 $coeff = 1;
175             }
176 11         12 $zero = $coeff - $coeff;
177             }
178 272         491 return $this->new(($zero) x $degree, $coeff);
179             }
180              
181             sub from_roots {
182 5     5 1 1300 my ($this, @roots) = @_;
183 5 100       18 my $one = ref($this)? $this->coeff_one: @roots? $roots[-1] ** 0: 1;
    100          
184 5         14 my $result = $this->new($one);
185 5         10 foreach my $root (@roots) {
186 8         15 $result = $result->mul_root($root);
187             }
188 5         12 return $result;
189             }
190              
191             sub string_config {
192 60     60 1 558 my ($this, $config) = @_;
193 60         89 my $have_arg = 2 <= @_;
194 60 100       95 if (ref $this) {
195 35 100       51 if ($have_arg) {
196 4         9 $this->[_F_CONFIG] = $config;
197             }
198             else {
199 31         52 $config = $this->[_F_CONFIG];
200             }
201             }
202             else {
203 25 100       44 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         28 $config = $global_string_config;
209             }
210             }
211 60         304 return $config;
212             }
213              
214             sub interpolate {
215 92     92 1 2489 my ($this, $xvalues, $yvalues) = @_;
216 92 100 100     316 if (
      100        
217 90         116 !ref($xvalues) || !ref($yvalues) || @{$xvalues} != @{$yvalues}
  90         182  
218             ) {
219 3         261 croak 'usage: $q = $p->interpolate([$x1, $x2, ...], [$y1, $y2, ...])';
220             }
221 89 100       113 return $this->new if !@{$xvalues};
  89         150  
222 87         98 my @alpha = @{$yvalues};
  87         118  
223 87         165 my $result = $this->new($alpha[0]);
224 87         150 my $aux = $result->monomial(0);
225 87         123 my $zero = $result->coeff_zero;
226 87         180 for (my $k=1; $k<=$#alpha; ++$k) {
227 172         303 for (my $j=$#alpha; $j>=$k; --$j) {
228 260         1759 my $dx = $xvalues->[$j] - $xvalues->[$j-$k];
229 260 100       2272 croak 'x values not disjoint' if $zero == $dx;
230 259         1496 $alpha[$j] = ($alpha[$j] - $alpha[$j-1]) / $dx;
231             }
232 171         2808 $aux = $aux->mul_root($xvalues->[$k-1]);
233 171         397 $result += $aux->mul_const($alpha[$k]);
234             }
235 86         203 return $result;
236             }
237              
238             sub coeff {
239 58082     58082 1 79575 my ($this, $degree) = @_;
240 58082 100       79049 if (defined $degree) {
241             return
242 49959 100 100     69870 0 <= $degree && $degree < @{$this->[_F_COEFF]}?
243             $this->[_F_COEFF]->[$degree]:
244             $this->[_F_ZERO];
245             }
246 8123 100       11362 croak 'array context required if called without argument' if !wantarray;
247 8122         8507 return @{$this->[_F_COEFF]};
  8122         15174  
248             }
249              
250             sub coefficients {
251 11     11 1 105 my ($this) = @_;
252 11 100       69 croak 'array context required' if !wantarray;
253 10 100       17 return $this->is_zero? ($this->coeff_zero): $this->coeff;
254             }
255              
256 19165     19165 1 19823 sub degree { return $#{ $_[0]->[_F_COEFF] }; }
  19165         32648  
257 1147     1147 1 1942 sub coeff_zero { return $_[0]->[_F_ZERO]; }
258 4152     4152 1 6932 sub coeff_one { return $_[0]->[_F_ONE]; }
259              
260             sub proper_degree {
261 2     2 1 3 my ($this) = @_;
262 2         6 my $degree = $this->degree;
263 2 100       7 return 0 <= $degree? $degree: undef;
264             }
265              
266             sub number_of_terms {
267 2     2 1 38 my ($this) = @_;
268 2         4 my $zero = $this->coeff_zero;
269 2         15 return scalar grep { $zero != $_ } $this->coeff;
  3         8  
270             }
271              
272             sub evaluate {
273 263     263 1 7947 my ($this, $x) = @_;
274 263         376 my $i = $this->degree;
275 263 100       462 my $result = 0 <= $i? $this->coeff($i): $this->coeff_zero;
276 263         483 while (0 <= --$i) {
277 426         1973 $result = $x * $result + $this->coeff($i);
278             }
279 263         2319 return $result;
280             }
281              
282             sub nest {
283 4     4 1 281 my ($this, $that) = @_;
284 4         7 my $i = $this->degree;
285 4 100       14 my $result = $that->new(0 <= $i? $this->coeff($i): ());
286 4         9 while (0 <= --$i) {
287 6         10 $result = $result->mul($that)->add_const($this->coeff($i));
288             }
289 4         10 return $result;
290             }
291              
292             sub mirror {
293 16     16 1 161 my ($this) = @_;
294 16         19 my $i = 0;
295 16 100       32 return $this->new( map { $i++ & 1? -$_: $_ } $this->coeff );
  47         117  
296             }
297              
298             sub is_zero {
299 1617     1617 1 2506 my ($this) = @_;
300 1617         2066 return $this->degree < 0;
301             }
302              
303             sub is_nonzero {
304 178     178 1 531 my ($this) = @_;
305 178         263 return $this->degree >= 0;
306             }
307              
308             sub is_equal {
309 472     472 1 1368 my ($this, $that) = @_;
310 472         650 my $i = $this->degree;
311 472         654 my $eq = $i == $that->degree;
312 472   100     1304 while ($eq && 0 <= $i) {
313 1020         1381 $eq = $this->coeff($i) == $that->coeff($i);
314 1020         6349 --$i;
315             }
316 472         1199 return $eq;
317             }
318              
319             sub is_unequal {
320 948     948 1 1200 my ($this, $that) = @_;
321 948         1299 my $i = $this->degree;
322 948         1257 my $eq = $i == $that->degree;
323 948   100     1697 while ($eq && 0 <= $i) {
324 39         61 $eq = $this->coeff($i) == $that->coeff($i);
325 39         197 --$i;
326             }
327 948         1915 return !$eq;
328             }
329              
330             sub is_even {
331 6     6 1 60 my ($this) = @_;
332 6         38 return $this->is_equal($this->mirror);
333             }
334              
335             sub is_odd {
336 6     6 1 14 my ($this) = @_;
337 6         13 return $this->is_equal($this->mirror->neg);
338             }
339              
340             sub neg {
341 8     8 1 96 my ($this) = @_;
342 8 100       16 return $this if $this->degree < 0;
343 6         11 return $this->new( map { -$_ } $this->coeff );
  20         38  
344             }
345              
346             sub add {
347 180     180 1 246 my ($this, $that) = @_;
348 180         259 my $this_d = $this->degree;
349 180         271 my $that_d = $that->degree;
350 180 100       322 my $min_d = $this_d <= $that_d? $this_d: $that_d;
351             return $this->new(
352 146         582 (map { $this->coeff($_) + $that->coeff($_) } 0..$min_d),
353 68         98 (map { $this->coeff($_) } $that_d+1 .. $this_d),
354 180         367 (map { $that->coeff($_) } $this_d+1 .. $that_d),
  167         1164  
355             );
356             }
357              
358             sub sub_ {
359 1292     1292 1 1870 my ($this, $that) = @_;
360 1292         1657 my $this_d = $this->degree;
361 1292         1682 my $that_d = $that->degree;
362 1292 100       2140 my $min_d = $this_d <= $that_d? $this_d: $that_d;
363             return $this->new(
364 767         2862 (map { $this->coeff($_) - $that->coeff($_) } 0..$min_d),
365 500         1958 (map { $this->coeff($_) } $that_d+1 .. $this_d),
366 1292         2163 (map { -$that->coeff($_) } $this_d+1 .. $that_d),
  1974         7913  
367             );
368             }
369              
370             sub mul {
371 3332     3332 1 4415 my ($this, $that) = @_;
372 3332         4846 my $this_d = $this->degree;
373 3332 100       5371 return $this if $this_d < 0;
374 3106         4055 my $that_d = $that->degree;
375             return $this->new(
376             map {
377 3106 100       6480 my ($i, $j) = $_ <= $this_d? ($_, 0): ($this_d, $_-$this_d);
  12165 100       20564  
378 12165         15929 my $sum = $this->coeff($i) * $that->coeff($j);
379 12165   100     71362 while ($i > 0 && $j < $that_d) {
380 7153         20079 $sum += $this->coeff(--$i) * $that->coeff(++$j);
381             }
382             $sum
383 12165         61895 } $that_d < 0? (): (0 .. $this_d+$that_d)
384             );
385             }
386              
387             sub _divmod {
388 3392     3392   4180 my ($this, $that) = @_;
389 3392         4700 my @den = $that->coeff;
390 3392 100       6385 @den or croak 'division by zero polynomial';
391 3380         4125 my $hd = pop @den;
392 3380 100       5178 if ($that->is_monic) {
393 2959         18793 undef $hd;
394             }
395 3380         7349 my @rem = $this->coeff;
396 3380         4252 my @quot = ();
397 3380         4560 my $i = $#rem - @den;
398 3380         5519 while (0 <= $i) {
399 7273         38452 my $q = pop @rem;
400 7273 100       10087 if (defined $hd) {
401 910         1710 $q /= $hd;
402             }
403 7273         12271 $quot[$i] = $q;
404 7273         8041 my $j = $i--;
405 7273         9297 foreach my $d (@den) {
406 15640         85812 $rem[$j++] -= $q * $d;
407             }
408             }
409 3380         24774 return (\@quot, \@rem);
410             }
411              
412             sub divmod {
413 561     561 1 1399 my ($this, $that) = @_;
414 561 100       885 croak 'array context required' if !wantarray;
415 560         804 my ($quot, $rem) = _divmod($this, $that);
416 558 100       689 return ($this->new(@{$quot}), @{$quot}? $this->new(@{$rem}): $this);
  558         860  
  558         922  
  555         855  
417             }
418              
419             sub div {
420 122     122 1 393 my ($this, $that) = @_;
421 122         190 my ($quot) = _divmod($this, $that);
422 120         159 return $this->new(@{$quot});
  120         216  
423             }
424              
425             sub mod {
426 2710     2710 1 3513 my ($this, $that) = @_;
427 2710         3808 my ($quot, $rem) = _divmod($this, $that);
428 2702 100       3059 return @{$quot}? $this->new(@{$rem}): $this;
  2702         4598  
  2127         3210  
429             }
430              
431             sub mmod {
432 10     10 1 748 my ($this, $that) = @_;
433 10         22 my @den = $that->coeff;
434 10 100       132 @den or croak 'division by zero polynomial';
435 8         11 my $hd = pop @den;
436 8 100       13 if ($that->is_monic) {
437 1         2 undef $hd;
438             }
439 8         17 my @rem = $this->coeff;
440 8         15 my $i = $#rem - @den;
441 8         15 while (0 <= $i) {
442 9         13 my $q = pop @rem;
443 9 100       41 if (defined $hd) {
444 7         14 foreach my $r (@rem) {
445 9         12 $r *= $hd;
446             }
447             }
448 9         16 my $j = $i--;
449 9         13 foreach my $d (@den) {
450 7         16 $rem[$j++] -= $q * $d;
451             }
452             }
453 8         14 return $this->new(@rem);
454             }
455              
456             sub add_const {
457 8     8 1 208 my ($this, $const) = @_;
458 8 100       14 return $this->new($const) if $this->is_zero;
459 6         8 my $i = 0;
460 6 100       11 return $this->new( map { $i++? $_: $_ + $const } $this->coeff );
  22         43  
461             }
462              
463             sub sub_const {
464 165     165 1 425 my ($this, $const) = @_;
465 165 100       227 return $this->new(-$const) if $this->is_zero;
466 164         246 my $i = 0;
467 164 100       241 return $this->new( map { $i++? $_: $_ - $const } $this->coeff );
  2106         3725  
468             }
469              
470             sub mul_const {
471 357     357 1 635 my ($this, $factor) = @_;
472 357         509 return $this->new( map { $_ * $factor } $this->coeff );
  723         3340  
473             }
474              
475             sub div_const {
476 119     119 1 395 my ($this, $divisor) = @_;
477 119 100       197 croak 'division by zero' if $this->coeff_zero == $divisor;
478 118         691 return $this->new( map { $_ / $divisor } $this->coeff );
  313         1305  
479             }
480              
481             sub mul_root {
482 182     182 1 454 my ($this, $root) = @_;
483 182         286 return $this->shift_up(1)->sub_($this->mul_const($root));
484             }
485              
486             sub _divmod_root {
487 18     18   21 my ($this, $root) = @_;
488 18         31 my $i = $this->degree;
489 18 100       38 my $rem = $this->coeff($i < 0? 0: $i);
490 18         25 my @quot;
491 18         31 while (0 <= --$i) {
492 36         48 $quot[$i] = $rem;
493 36         48 $rem = $root * $rem + $this->coeff($i);
494             }
495 18         35 return (\@quot, $rem);
496             }
497              
498             sub divmod_root {
499 7     7 1 283 my ($this, $root) = @_;
500 7 100       72 croak 'array context required' if !wantarray;
501 6         11 my ($quot, $rem) = _divmod_root($this, $root);
502 6         6 return ($this->new(@{$quot}), $this->new($rem));
  6         11  
503             }
504              
505             sub div_root {
506 12     12 1 887 my ($this, $root, $check) = @_;
507 12         27 my ($quot, $rem) = _divmod_root($this, $root);
508 12 100 100     26 if ($check && $this->coeff_zero != $rem) {
509 3         275 croak 'non-zero remainder';
510             }
511 9         11 return $this->new(@{$quot});
  9         23  
512             }
513              
514             sub is_monic {
515 3397     3397 1 4218 my ($this) = @_;
516 3397         4707 my $degree = $this->degree;
517 3397   100     6584 return 0 <= $degree && $this->coeff_one == $this->coeff($degree);
518             }
519              
520             sub monize {
521 7     7 1 144 my ($this) = @_;
522 7 100 100     15 return $this if $this->is_zero || $this->is_monic;
523 4         34 return $this->div_const($this->coeff($this->degree));
524             }
525              
526             sub pow {
527 20     20 1 35 my ($this, $exp) = @_;
528 20         37 _check_int($exp);
529 18         32 my $degree = $this->degree;
530 18 100       42 return $this->new($this->coeff_one) if 0 == $exp;
531 12 100       29 return $this if 0 > $degree;
532 9 100       33 return $this->new($this->coeff(0) ** $exp) if 0 == $degree;
533 6 100 100     116 croak 'exponent too large'
534             if defined($max_degree) && $degree * $exp > $max_degree;
535 5         8 my @binary = ();
536 5         9 while ($exp > 1) {
537 4         10 push @binary, 1 & $exp;
538 4         7 $exp >>= 1;
539             }
540 5         7 my $result = $this;
541 5         9 while (@binary) {
542 4         9 $result *= $result;
543 4 100       14 $result *= $this if pop @binary;
544             }
545 5         17 return $result;
546             }
547              
548             sub pow_mod {
549 138     138 1 2536 my ($this, $exp, $that) = @_;
550 138         266 _check_int($exp);
551 138         246 $this = $this->mod($that);
552 132         211 my $this_d = $this->degree;
553 132 100       172 return $this->new if 0 == $that->degree;
554 129 100       211 return $this->new($this->coeff_one) if 0 == $exp;
555 124 100       184 return $this if 0 > $this_d;
556 122 100       201 return $this->new($this->coeff(0) ** $exp) if 0 == $this_d;
557 108         123 my @binary = ();
558 108         173 while ($exp > 1) {
559 481         546 push @binary, 1 & $exp;
560 481         657 $exp >>= 1;
561             }
562 108         122 my $result = $this;
563 108         184 while (@binary) {
564 481         816 $result *= $result;
565 481 100       984 $result *= $this if pop @binary;
566 481         862 $result %= $that;
567             }
568 108         224 return $result;
569             }
570              
571             sub exp_mod {
572 99     99 1 702 my ($this, $exp) = @_;
573 99         196 _check_int($exp);
574 99 100       147 return $this->new if 0 == $this->degree;
575 93 100       148 return $this->new($this->coeff_one) if 0 == $exp;
576 91         116 my @binary = ();
577 91         162 while ($exp > 1) {
578 346         415 push @binary, 1 & $exp;
579 346         462 $exp >>= 1;
580             }
581 91         127 my $result = $this->new($this->coeff_zero, $this->coeff_one);
582 91         150 while (@binary) {
583 346         556 $result *= $result;
584 346 100       621 if (pop @binary) {
585 166         192 local $max_degree;
586 166         284 $result <<= 1;
587             }
588 346         595 $result %= $this;
589             }
590 91         207 return $result;
591             }
592              
593             sub shift_up {
594 450     450 1 561 my ($this, $exp) = @_;
595 450         766 _check_int($exp);
596 450 100 100     891 croak 'exponent too large' if
597             defined($max_degree) && $this->degree + $exp > $max_degree;
598 449 100       693 return $this if !$exp;
599 447         688 return $this->new(($this->coeff_zero) x $exp, $this->coeff);
600             }
601              
602             sub shift_down {
603 5     5 1 8 my ($this, $exp) = @_;
604 5         10 _check_int($exp);
605 5 100       11 return $this if !$exp;
606 3         7 return $this->new( map { $this->coeff($_) } $exp .. $this->degree );
  1         3  
607             }
608              
609             sub inflate {
610 12     12 1 598 my ($this, $exp) = @_;
611 12         30 my $degree = $this->degree;
612 12 100       30 return $this if $degree <= 0;
613 6         13 _check_int($exp);
614 6 100 100     75 croak 'exponent too large' if
615             defined($max_degree) && $degree * $exp > $max_degree;
616 5 100       13 return $this->new($this->evaluate($this->coeff_one)) if !$exp;
617 4         8 my @zeroes = ($this->coeff_zero) x ($exp - 1);
618 4 100       9 return $this if !@zeroes;
619 3         6 my ($const, @coeff) = $this->coeff;
620 3         6 return $this->new($const, map {@zeroes, $_} @coeff);
  10         18  
621             }
622              
623             sub slice {
624 49     49 1 411 my ($this, $start, $count) = @_;
625 49         81 _check_int($start, $count);
626 49         61 my $degree = $this->degree;
627 49         59 my $end = $start+$count-1;
628 49 100       76 if ($degree <= $end) {
629 34 100       52 return $this if 0 == $start;
630 32         35 $end = $degree;
631             }
632 47         68 return $this->new( map { $this->coeff($_) } $start .. $end );
  60         97  
633             }
634              
635             sub differentiate {
636 3     3 1 9 my ($this) = @_;
637 3         8 my $n = $this->coeff_zero;
638 3         6 my $one = $this->coeff_one;
639             return $this->new(
640 3         9 map { $this->coeff($_) * ($n += $one) } 1..$this->degree
  12         180  
641             );
642             }
643              
644             sub integrate {
645 7     7 1 240 my ($this, $const) = @_;
646 7         12 my $zero = $this->coeff_zero;
647 7         12 my $one = $this->coeff_one;
648 7         9 my $n = $zero;
649 7 100       13 if (!defined $const) {
650 5         5 $const = $zero;
651             }
652             return $this->new(
653             $const,
654             map {
655 7         16 $n += $one;
  31         239  
656 31         187 my $c = $this->coeff($_);
657 31 100       65 $zero == $c? $c: $c / $n
658             } 0..$this->degree
659             );
660             }
661              
662             sub definite_integral {
663 2     2 1 121 my ($this, $lower, $upper) = @_;
664 2         4 my $ad = $this->integrate;
665 2         5 return $ad->evaluate($upper) - $ad->evaluate($lower);
666             }
667              
668             sub _make_ltz {
669 99     99   140 my ($config, $zero) = @_;
670 99 100       195 return 0 if !$config->{'fold_sign'};
671 45         55 my $sgn = $config->{'sign_of_coeff'};
672             return
673             defined($sgn)?
674 11     11   19 sub { $sgn->($_[0]) < 0 }:
675 45 100   82   162 sub { $_[0] < $zero };
  82         165  
676             }
677              
678             sub as_string {
679 75     75 1 1872 my ($this, $params) = @_;
680             my %config = (
681             @string_defaults,
682 75 100 100     124 %{$params || $this->string_config || (ref $this)->string_config},
  75         533  
683             );
684 75         238 my $max_exp = $this->degree;
685 75 100       134 if ($max_exp < 0) {
686 24         24 $max_exp = 0;
687             }
688 75         88 my $result = q{};
689 75         116 my $zero = $this->coeff_zero;
690 75         128 my $ltz = _make_ltz(\%config, $zero);
691 75         124 my $one = $this->coeff_one;
692 75         91 my $with_variable = $config{'with_variable'};
693 75 100       183 foreach my $exp ($config{'ascending'}? 0..$max_exp: reverse 0..$max_exp) {
694 208         311 my $coeff = $this->coeff($exp);
695              
696             # skip term?
697 208 100 100     730 if (
      100        
      100        
698             $with_variable &&
699             $exp < $max_exp &&
700             $config{'fold_zero'} &&
701             $coeff == $zero
702             ) {
703 43         125 next;
704             }
705              
706             # plus/minus
707 165 100 100     381 if ($ltz && $ltz->($coeff)) {
708 17         69 $coeff = -$coeff;
709 17 100       73 $result .= $config{q[] eq $result? 'leading_minus': 'minus'};
710             }
711             else{
712 148 100       365 $result .= $config{q[] eq $result? 'leading_plus': 'plus'};
713             }
714              
715             # coefficient
716 165 100 100     655 if (
      100        
      100        
      100        
717             !$with_variable ||
718             !$config{'fold_one'} ||
719             0 == $exp && $config{'fold_exp_zero'} ||
720             $one != $coeff
721             ) {
722 113         227 $result .= $config{'convert_coeff'}->($coeff);
723 113 100       348 next if !$with_variable;
724 99 100 100     233 if (0 != $exp || !$config{'fold_exp_zero'}) {
725 44         61 $result .= $config{'times'};
726             }
727             }
728              
729             # variable and exponent
730 151 100 100     471 if (0 != $exp || !$config{'fold_exp_zero'}) {
731 96         110 $result .= $config{'variable'};
732 96 100 100     224 if (1 != $exp || !$config{'fold_exp_one'}) {
733 52         106 $result .= $config{'power'} . $exp;
734             }
735             }
736             }
737             return join q{},
738             $config{'prefix'},
739             $config{'wrap'}->($this, $result),
740 75         156 $config{'suffix'};
741             }
742              
743             sub as_horner_tree {
744 12     12 1 23 my ($this, $params) = @_;
745 12         20 my %config = (@tree_defaults, %{$params});
  12         75  
746 12         31 my $exp = $this->degree;
747 12 100       25 if ($exp < 0) {
748 1         1 $exp = 0;
749             }
750 12         21 my $zero = $this->coeff_zero;
751 12         23 my $ltz = _make_ltz(\%config, $zero);
752 12         24 my $coeff = $this->coeff($exp);
753 12   100     36 my $first_is_neg = $ltz && $ltz->($coeff);
754 12 100       19 if ($first_is_neg) {
755 3         4 $coeff = -$coeff;
756             }
757             my $result =
758             $exp && $this->coeff_one == $coeff? undef:
759 12 100 100     26 $config{'constant'}->($coeff);
760 12         31 my $is_sum = 0;
761 12         17 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
762 12 100       20 my $variable = $var_is_dynamic? undef: $config{'variable'};
763 12         21 while (0 <= --$exp) {
764 18         37 $coeff = $this->coeff($exp);
765 18 100       34 if ($is_sum) {
766 6         18 $result = $config{'group'}->($result);
767             }
768 18 100       37 if ($var_is_dynamic) {
769 6         12 $variable = $config{'variable'}->();
770             }
771 18         43 my $power = $variable;
772 18 100       25 if ($config{'expand_power'}) {
773 8         10 $is_sum = $zero != $coeff;
774             }
775             else {
776 10         11 my $skip = 0;
777 10         14 while ($zero == $coeff) {
778 10         10 ++$skip;
779 10 100       17 last if $skip > $exp;
780 8         13 $coeff = $this->coeff($exp - $skip);
781             }
782 10 100       18 if ($skip) {
783 8         8 $exp -= $skip;
784 8 100       11 ++$skip if 0 <= $exp;
785 8 100       17 $power = $config{'power'}->($variable, $skip) if 1 < $skip;
786             }
787 10         27 $is_sum = 0 <= $exp;
788             }
789             $result =
790 18 100       32 defined($result)? $config{'product'}->($result, $power): $power;
791 18 100       55 if ($first_is_neg) {
792 2         4 $result = $config{'negation'}->($result);
793 2         6 $first_is_neg = 0;
794             }
795 18 100       36 if ($is_sum) {
796 12   100     26 my $is_neg = $ltz && $ltz->($coeff);
797 12 100       19 if ($is_neg) {
798 4         4 $coeff = -$coeff;
799             }
800 12         20 my $const = $config{'constant'}->($coeff);
801 12 100       43 $result = $config{$is_neg? 'difference': 'sum'}->($result, $const);
802             }
803             }
804 12 100       29 if ($first_is_neg) {
805 1         3 $result = $config{'negation'}->($result);
806             }
807 12         60 return $result;
808             }
809              
810             sub as_power_sum_tree {
811 12     12 1 22 my ($this, $params) = @_;
812 12         16 my %config = (@tree_defaults, %{$params});
  12         75  
813 12         28 my $exp = $this->degree;
814 12 100       26 if ($exp < 0) {
815 1         1 $exp = 0;
816             }
817 12         16 my $zero = $this->coeff_zero;
818 12         18 my $ltz = _make_ltz(\%config, $zero);
819 12         21 my $one = $this->coeff_one;
820 12         14 my $result = undef;
821 12         19 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
822 12 100       20 my $variable = $var_is_dynamic? undef: $config{'variable'};
823 12         22 while (0 <= $exp) {
824 34         58 my $coeff = $this->coeff($exp);
825              
826             # skip term?
827 34 100 100     78 next if defined($result) && $zero == $coeff;
828              
829             # variable and exponent
830 22         23 my $term = undef;
831 22 100       28 if (0 != $exp) {
832 12 100       18 if ($var_is_dynamic) {
833 3         6 $variable = $config{'variable'}->();
834             }
835 12         23 $term = $variable;
836 12 100       16 if (1 != $exp) {
837 9 100       13 if ($config{'expand_power'}) {
838 2         3 my $todo = $exp-1;
839 2         3 for (my $tmp=$exp; $tmp>1; --$tmp) {
840 2 100       4 if ($var_is_dynamic) {
841 1         2 $variable = $config{'variable'}->();
842             }
843 2         6 $term = $config{'product'}->($term, $variable);
844             }
845             }
846             else {
847 7         19 $term = $config{'power'}->($term, $exp);
848             }
849             }
850             }
851              
852             # sign and coefficient
853 22   100     60 my $is_neg = $ltz && $ltz->($coeff);
854 22 100       56 if ($is_neg) {
855 7         8 $coeff = -$coeff;
856             }
857 22 100 100     51 if (0 == $exp || $one != $coeff) {
858 16         28 my $const = $config{'constant'}->($coeff);
859 16 100       54 $term = 0 == $exp? $const: $config{'product'}->($const, $term);
860             }
861              
862             # summation
863 22 100       38 if (defined $result) {
864 10 100       21 $result = $config{$is_neg? 'difference': 'sum'}->($result, $term);
865             }
866             else {
867 12 100       25 $result = $is_neg? $config{'negation'}->($term): $term;
868             }
869             }
870             continue {
871 34         76 --$exp;
872             }
873 12         53 return $result;
874             }
875              
876             sub gcd {
877 3     3 1 148 my ($this, $that, $mod) = @_;
878 3 50       9 defined $mod or $mod = 'mod';
879 3         16 my $mod_op = $this->can($mod);
880 3 50       16 $mod_op or croak "no such method: $mod";
881 3         13 my ($this_d, $that_d) = ($this->degree, $that->degree);
882 3 50       9 if ($this_d < $that_d) {
883 3         6 ($this, $that) = ($that, $this);
884 3         7 ($this_d, $that_d) = ($that_d, $this_d);
885             }
886 3         8 while (0 <= $that_d) {
887 6         13 ($this, $that) = ($that, $this->$mod_op($that));
888 6         14 ($this_d, $that_d) = ($that_d, $that->degree);
889 6 50       19 $this_d > $that_d or croak 'bad modulo operator';
890             }
891 3         25 return $this;
892             }
893              
894             sub xgcd {
895 224     224 1 498 my ($this, $that) = @_;
896 224 50       332 croak 'array context required' if !wantarray;
897 224         320 my ($d1, $d2) = ($this->new($this->coeff_one), $this->new);
898 224 50       357 if ($this->degree < $that->degree) {
899 0         0 ($this, $that) = ($that, $this);
900 0         0 ($d1, $d2) = ($d2, $d1);
901             }
902 224         318 my ($m1, $m2) = ($d2, $d1);
903 224         374 while (!$that->is_zero) {
904 550         1046 my ($div, $mod) = $this->divmod($that);
905 550         1005 ($this, $that) = ($that, $mod);
906 550         1019 ($d1, $d2, $m1, $m2) =
907             ($m1, $m2, $d1->sub_($m1->mul($div)), $d2->sub_($m2->mul($div)));
908             }
909 224         592 return ($this, $d1, $d2, $m1, $m2);
910             }
911              
912             sub lcm {
913 0     0 1 0 my $result = shift;
914 0         0 foreach my $that (@_) {
915 0         0 my $gcd = $result->gcd($that);
916 0 0       0 $result *= $that->div($gcd) if !$that->is_equal($gcd);
917             }
918 0         0 return $result;
919             }
920              
921             sub inv_mod {
922 112     112 1 504 my ($this, $that) = @_;
923 112         223 my ($d, $d2) = ($that->xgcd($this))[0, 2];
924 112 50 33     257 croak 'division by zero polynomial' if $that->is_zero || $d2->is_zero;
925 112         197 return $d2->div_const($d->coeff($d->degree));
926             }
927              
928             1;
929             __END__