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   255039 use 5.006;
  14         138  
4 14     14   80 use strict;
  14         26  
  14         333  
5 14     14   79 use warnings;
  14         27  
  14         471  
6 14     14   78 use Carp qw(croak);
  14         25  
  14         2379  
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   108 use constant _F_COEFF => 0; # coefficients arrayref, ascending degree
  14         35  
  14         1588  
34 14     14   103 use constant _F_ZERO => 1; # zero element of coefficient space
  14         30  
  14         797  
35 14     14   115 use constant _F_ONE => 2; # unit element of coefficient space
  14         29  
  14         723  
36 14     14   80 use constant _F_CONFIG => 3; # default stringification configuration
  14         24  
  14         755  
37 14     14   96 use constant _NFIELDS => 4;
  14         28  
  14         6758  
38              
39             # ----- static data -----
40              
41             our $VERSION = '1.019';
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   189 my ($method) = @_;
97             return sub {
98 5754     5754   19122 my ($this, $that, $reversed) = @_;
99 5754 100 100     12287 if (!ref($that) || !eval { $that->isa('Math::Polynomial') }) {
  5748         20281  
100 7         27 $that = $this->new($that);
101             }
102 5754 100       11336 if ($reversed) {
103 2         16 ($this, $that) = ($that, $this);
104             }
105 5754         12541 return $this->$method($that);
106 98         546 };
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   87 my ($method) = @_;
114             return sub {
115 294     294   3433 my ($this, $that, $reversed) = @_;
116 294 100       694 croak 'wrong operand type' if $reversed;
117 293         709 return $this->$method($that);
118 42         133 };
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       1612 eval {
126 14     14   119 use warnings FATAL => 'all';
  14         47  
  14         93498  
127 1099         3322 $arg == abs int $arg
128             } or croak 'non-negative integer argument expected';
129             }
130 1048         1555 return;
131             }
132              
133             # ----- methods -----
134              
135             sub new {
136 10469     10469 1 46436 my ($this, @coeff) = @_;
137 10469         17118 my $class = ref $this;
138 10469         14363 my ($zero, $one, $config);
139 10469 100       16282 if ($class) {
140 10224         13013 (undef, $zero, $one, $config) = @{$this};
  10224         18890  
141             }
142             else {
143 245 100       460 my $sample = @coeff? $coeff[-1]: 1;
144 245         640 $zero = $sample - $sample;
145 245         2091 $one = $sample ** 0;
146 245         2310 $config = undef;
147 245         445 $class = $this;
148             }
149 10469   100     29754 while (@coeff && $zero == $coeff[-1]) {
150 1328         10630 pop @coeff;
151             }
152 10469         90220 return bless [\@coeff, $zero, $one, $config], $class;
153             }
154              
155             sub clone {
156 1     1 1 4 my ($this) = @_;
157 1         2 return bless [@{$this}], ref $this;
  1         4  
158             }
159              
160             sub monomial {
161 274     274 1 3655 my ($this, $degree, $coeff) = @_;
162 274         365 my $zero;
163 274         580 _check_int($degree);
164 274 100 100     1236 croak 'exponent too large'
165             if defined($max_degree) && $degree > $max_degree;
166 272 100       518 if (ref $this) {
167 261 100       489 if (!defined $coeff) {
168 253         436 $coeff = $this->coeff_one;
169             }
170 261         456 $zero = $this->coeff_zero;
171             }
172             else {
173 11 100       23 if (!defined $coeff) {
174 4         6 $coeff = 1;
175             }
176 11         17 $zero = $coeff - $coeff;
177             }
178 272         596 return $this->new(($zero) x $degree, $coeff);
179             }
180              
181             sub from_roots {
182 5     5 1 1544 my ($this, @roots) = @_;
183 5 100       20 my $one = ref($this)? $this->coeff_one: @roots? $roots[-1] ** 0: 1;
    100          
184 5         20 my $result = $this->new($one);
185 5         13 foreach my $root (@roots) {
186 8         20 $result = $result->mul_root($root);
187             }
188 5         16 return $result;
189             }
190              
191             sub string_config {
192 60     60 1 683 my ($this, $config) = @_;
193 60         109 my $have_arg = 2 <= @_;
194 60 100       122 if (ref $this) {
195 35 100       72 if ($have_arg) {
196 4         9 $this->[_F_CONFIG] = $config;
197             }
198             else {
199 31         60 $config = $this->[_F_CONFIG];
200             }
201             }
202             else {
203 25 100       46 if ($have_arg) {
204             # note: do not leave ultimate fallback configuration undefined
205 6   100     66 $global_string_config = $config || {};
206             }
207             else {
208 19         31 $config = $global_string_config;
209             }
210             }
211 60         382 return $config;
212             }
213              
214             sub interpolate {
215 92     92 1 2722 my ($this, $xvalues, $yvalues) = @_;
216 92 100 100     363 if (
      100        
217 90         151 !ref($xvalues) || !ref($yvalues) || @{$xvalues} != @{$yvalues}
  90         219  
218             ) {
219 3         292 croak 'usage: $q = $p->interpolate([$x1, $x2, ...], [$y1, $y2, ...])';
220             }
221 89 100       136 return $this->new if !@{$xvalues};
  89         169  
222 87         135 my @alpha = @{$yvalues};
  87         143  
223 87         186 my $result = $this->new($alpha[0]);
224 87         176 my $aux = $result->monomial(0);
225 87         176 my $zero = $result->coeff_zero;
226 87         191 for (my $k=1; $k<=$#alpha; ++$k) {
227 172         394 for (my $j=$#alpha; $j>=$k; --$j) {
228 260         2192 my $dx = $xvalues->[$j] - $xvalues->[$j-$k];
229 260 100       2737 croak 'x values not disjoint' if $zero == $dx;
230 259         1811 $alpha[$j] = ($alpha[$j] - $alpha[$j-1]) / $dx;
231             }
232 171         4069 $aux = $aux->mul_root($xvalues->[$k-1]);
233 171         489 $result += $aux->mul_const($alpha[$k]);
234             }
235 86         236 return $result;
236             }
237              
238             sub coeff {
239 58156     58156 1 97294 my ($this, $degree) = @_;
240 58156 100       97286 if (defined $degree) {
241             return
242 50011 100 100     86747 0 <= $degree && $degree < @{$this->[_F_COEFF]}?
243             $this->[_F_COEFF]->[$degree]:
244             $this->[_F_ZERO];
245             }
246 8145 100       13788 croak 'array context required if called without argument' if !wantarray;
247 8144         10032 return @{$this->[_F_COEFF]};
  8144         18400  
248             }
249              
250             sub coefficients {
251 11     11 1 109 my ($this) = @_;
252 11 100       85 croak 'array context required' if !wantarray;
253 10 100       19 return $this->is_zero? ($this->coeff_zero): $this->coeff;
254             }
255              
256 19220     19220 1 24933 sub degree { return $#{ $_[0]->[_F_COEFF] }; }
  19220         38556  
257 1147     1147 1 2360 sub coeff_zero { return $_[0]->[_F_ZERO]; }
258 4159     4159 1 8506 sub coeff_one { return $_[0]->[_F_ONE]; }
259              
260             sub proper_degree {
261 2     2 1 5 my ($this) = @_;
262 2         5 my $degree = $this->degree;
263 2 100       10 return 0 <= $degree? $degree: undef;
264             }
265              
266             sub number_of_terms {
267 2     2 1 49 my ($this) = @_;
268 2         4 my $zero = $this->coeff_zero;
269 2         10 return scalar grep { $zero != $_ } $this->coeff;
  3         10  
270             }
271              
272             sub evaluate {
273 263     263 1 9450 my ($this, $x) = @_;
274 263         464 my $i = $this->degree;
275 263 100       575 my $result = 0 <= $i? $this->coeff($i): $this->coeff_zero;
276 263         533 while (0 <= --$i) {
277 426         2434 $result = $x * $result + $this->coeff($i);
278             }
279 263         2761 return $result;
280             }
281              
282             sub nest {
283 4     4 1 313 my ($this, $that) = @_;
284 4         10 my $i = $this->degree;
285 4 100       15 my $result = $that->new(0 <= $i? $this->coeff($i): ());
286 4         10 while (0 <= --$i) {
287 6         12 $result = $result->mul($that)->add_const($this->coeff($i));
288             }
289 4         13 return $result;
290             }
291              
292             sub unnest {
293 5     5 1 140 my ($this, $that) = @_;
294 5 100       12 return undef if $that->degree <= 0;
295 3         4 my @coeff = ();
296 3         6 my $q = $this;
297 3         9 while ($q) {
298 7         17 ($q, my $r) = $q->divmod($that);
299 7 100       15 return undef if $r->degree > 0;
300 6         13 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         23 my $i = 0;
308 16 100       35 return $this->new( map { $i++ & 1? -$_: $_ } $this->coeff );
  47         141  
309             }
310              
311             sub is_zero {
312 1617     1617 1 2905 my ($this) = @_;
313 1617         2560 return $this->degree < 0;
314             }
315              
316             sub is_nonzero {
317 196     196 1 664 my ($this) = @_;
318 196         365 return $this->degree >= 0;
319             }
320              
321             sub is_equal {
322 472     472 1 1676 my ($this, $that) = @_;
323 472         821 my $i = $this->degree;
324 472         847 my $eq = $i == $that->degree;
325 472   100     1475 while ($eq && 0 <= $i) {
326 1020         1677 $eq = $this->coeff($i) == $that->coeff($i);
327 1020         7390 --$i;
328             }
329 472         1374 return $eq;
330             }
331              
332             sub is_unequal {
333 948     948 1 1487 my ($this, $that) = @_;
334 948         1632 my $i = $this->degree;
335 948         1595 my $eq = $i == $that->degree;
336 948   100     2114 while ($eq && 0 <= $i) {
337 39         80 $eq = $this->coeff($i) == $that->coeff($i);
338 39         222 --$i;
339             }
340 948         2272 return !$eq;
341             }
342              
343             sub is_even {
344 6     6 1 87 my ($this) = @_;
345 6         52 return $this->is_equal($this->mirror);
346             }
347              
348             sub is_odd {
349 6     6 1 14 my ($this) = @_;
350 6         17 return $this->is_equal($this->mirror->neg);
351             }
352              
353             sub neg {
354 8     8 1 115 my ($this) = @_;
355 8 100       19 return $this if $this->degree < 0;
356 6         17 return $this->new( map { -$_ } $this->coeff );
  20         46  
357             }
358              
359             sub add {
360 180     180 1 306 my ($this, $that) = @_;
361 180         308 my $this_d = $this->degree;
362 180         323 my $that_d = $that->degree;
363 180 100       369 my $min_d = $this_d <= $that_d? $this_d: $that_d;
364             return $this->new(
365 146         725 (map { $this->coeff($_) + $that->coeff($_) } 0..$min_d),
366 68         127 (map { $this->coeff($_) } $that_d+1 .. $this_d),
367 180         397 (map { $that->coeff($_) } $this_d+1 .. $that_d),
  167         1351  
368             );
369             }
370              
371             sub sub_ {
372 1299     1299 1 2153 my ($this, $that) = @_;
373 1299         2222 my $this_d = $this->degree;
374 1299         2147 my $that_d = $that->degree;
375 1299 100       2439 my $min_d = $this_d <= $that_d? $this_d: $that_d;
376             return $this->new(
377 781         3452 (map { $this->coeff($_) - $that->coeff($_) } 0..$min_d),
378 500         2477 (map { $this->coeff($_) } $that_d+1 .. $this_d),
379 1299         2606 (map { -$that->coeff($_) } $this_d+1 .. $that_d),
  1974         9196  
380             );
381             }
382              
383             sub mul {
384 3332     3332 1 5596 my ($this, $that) = @_;
385 3332         5619 my $this_d = $this->degree;
386 3332 100       6652 return $this if $this_d < 0;
387 3106         4757 my $that_d = $that->degree;
388             return $this->new(
389             map {
390 3106 100       7952 my ($i, $j) = $_ <= $this_d? ($_, 0): ($this_d, $_-$this_d);
  12165 100       23932  
391 12165         19248 my $sum = $this->coeff($i) * $that->coeff($j);
392 12165   100     83067 while ($i > 0 && $j < $that_d) {
393 7153         23188 $sum += $this->coeff(--$i) * $that->coeff(++$j);
394             }
395             $sum
396 12165         74680 } $that_d < 0? (): (0 .. $this_d+$that_d)
397             );
398             }
399              
400             sub _divmod {
401 3399     3399   5286 my ($this, $that) = @_;
402 3399         5697 my @den = $that->coeff;
403 3399 100       7954 @den or croak 'division by zero polynomial';
404 3387         5015 my $hd = pop @den;
405 3387 100       5937 if ($that->is_monic) {
406 2959         21873 undef $hd;
407             }
408 3387         8574 my @rem = $this->coeff;
409 3387         5928 my @quot = ();
410 3387         5207 my $i = $#rem - @den;
411 3387         6584 while (0 <= $i) {
412 7285         45408 my $q = pop @rem;
413 7285 100       12397 if (defined $hd) {
414 922         1608 $q /= $hd;
415             }
416 7285         14047 $quot[$i] = $q;
417 7285         9411 my $j = $i--;
418 7285         11493 foreach my $d (@den) {
419 15664         103882 $rem[$j++] -= $q * $d;
420             }
421             }
422 3387         29230 return (\@quot, \@rem);
423             }
424              
425             sub divmod {
426 568     568 1 1794 my ($this, $that) = @_;
427 568 100       1088 croak 'array context required' if !wantarray;
428 567         1035 my ($quot, $rem) = _divmod($this, $that);
429 565 100       864 return ($this->new(@{$quot}), @{$quot}? $this->new(@{$rem}): $this);
  565         1059  
  565         1084  
  560         990  
430             }
431              
432             sub div {
433 122     122 1 455 my ($this, $that) = @_;
434 122         241 my ($quot) = _divmod($this, $that);
435 120         253 return $this->new(@{$quot});
  120         256  
436             }
437              
438             sub mod {
439 2710     2710 1 4433 my ($this, $that) = @_;
440 2710         4647 my ($quot, $rem) = _divmod($this, $that);
441 2702 100       4165 return @{$quot}? $this->new(@{$rem}): $this;
  2702         5760  
  2127         3793  
442             }
443              
444             sub mmod {
445 10     10 1 897 my ($this, $that) = @_;
446 10         24 my @den = $that->coeff;
447 10 100       188 @den or croak 'division by zero polynomial';
448 8         16 my $hd = pop @den;
449 8 100       17 if ($that->is_monic) {
450 1         3 undef $hd;
451             }
452 8         17 my @rem = $this->coeff;
453 8         13 my $i = $#rem - @den;
454 8         19 while (0 <= $i) {
455 9         14 my $q = pop @rem;
456 9 100       48 if (defined $hd) {
457 7         21 foreach my $r (@rem) {
458 9         17 $r *= $hd;
459             }
460             }
461 9         13 my $j = $i--;
462 9         14 foreach my $d (@den) {
463 7         21 $rem[$j++] -= $q * $d;
464             }
465             }
466 8         15 return $this->new(@rem);
467             }
468              
469             sub add_const {
470 8     8 1 281 my ($this, $const) = @_;
471 8 100       16 return $this->new($const) if $this->is_zero;
472 6         12 my $i = 0;
473 6 100       12 return $this->new( map { $i++? $_: $_ + $const } $this->coeff );
  22         52  
474             }
475              
476             sub sub_const {
477 165     165 1 516 my ($this, $const) = @_;
478 165 100       287 return $this->new(-$const) if $this->is_zero;
479 164         308 my $i = 0;
480 164 100       274 return $this->new( map { $i++? $_: $_ - $const } $this->coeff );
  2106         4618  
481             }
482              
483             sub mul_const {
484 357     357 1 844 my ($this, $factor) = @_;
485 357         592 return $this->new( map { $_ * $factor } $this->coeff );
  723         4128  
486             }
487              
488             sub div_const {
489 119     119 1 521 my ($this, $divisor) = @_;
490 119 100       222 croak 'division by zero' if $this->coeff_zero == $divisor;
491 118         850 return $this->new( map { $_ / $divisor } $this->coeff );
  313         1579  
492             }
493              
494             sub mul_root {
495 182     182 1 520 my ($this, $root) = @_;
496 182         402 return $this->shift_up(1)->sub_($this->mul_const($root));
497             }
498              
499             sub _divmod_root {
500 18     18   35 my ($this, $root) = @_;
501 18         38 my $i = $this->degree;
502 18 100       50 my $rem = $this->coeff($i < 0? 0: $i);
503 18         26 my @quot;
504 18         43 while (0 <= --$i) {
505 36         53 $quot[$i] = $rem;
506 36         62 $rem = $root * $rem + $this->coeff($i);
507             }
508 18         45 return (\@quot, $rem);
509             }
510              
511             sub divmod_root {
512 7     7 1 350 my ($this, $root) = @_;
513 7 100       89 croak 'array context required' if !wantarray;
514 6         19 my ($quot, $rem) = _divmod_root($this, $root);
515 6         7 return ($this->new(@{$quot}), $this->new($rem));
  6         12  
516             }
517              
518             sub div_root {
519 12     12 1 933 my ($this, $root, $check) = @_;
520 12         29 my ($quot, $rem) = _divmod_root($this, $root);
521 12 100 100     33 if ($check && $this->coeff_zero != $rem) {
522 3         319 croak 'non-zero remainder';
523             }
524 9         14 return $this->new(@{$quot});
  9         19  
525             }
526              
527             sub is_monic {
528 3404     3404 1 5131 my ($this) = @_;
529 3404         5472 my $degree = $this->degree;
530 3404   100     8499 return 0 <= $degree && $this->coeff_one == $this->coeff($degree);
531             }
532              
533             sub monize {
534 7     7 1 186 my ($this) = @_;
535 7 100 100     19 return $this if $this->is_zero || $this->is_monic;
536 4         33 return $this->div_const($this->coeff($this->degree));
537             }
538              
539             sub pow {
540 20     20 1 39 my ($this, $exp) = @_;
541 20         63 _check_int($exp);
542 18         44 my $degree = $this->degree;
543 18 100       68 return $this->new($this->coeff_one) if 0 == $exp;
544 12 100       28 return $this if 0 > $degree;
545 9 100       39 return $this->new($this->coeff(0) ** $exp) if 0 == $degree;
546 6 100 100     144 croak 'exponent too large'
547             if defined($max_degree) && $degree * $exp > $max_degree;
548 5         10 my @binary = ();
549 5         13 while ($exp > 1) {
550 4         16 push @binary, 1 & $exp;
551 4         9 $exp >>= 1;
552             }
553 5         9 my $result = $this;
554 5         11 while (@binary) {
555 4         9 $result *= $result;
556 4 100       20 $result *= $this if pop @binary;
557             }
558 5         22 return $result;
559             }
560              
561             sub pow_mod {
562 138     138 1 3064 my ($this, $exp, $that) = @_;
563 138         335 _check_int($exp);
564 138         277 $this = $this->mod($that);
565 132         247 my $this_d = $this->degree;
566 132 100       231 return $this->new if 0 == $that->degree;
567 129 100       271 return $this->new($this->coeff_one) if 0 == $exp;
568 124 100       265 return $this if 0 > $this_d;
569 122 100       286 return $this->new($this->coeff(0) ** $exp) if 0 == $this_d;
570 108         178 my @binary = ();
571 108         277 while ($exp > 1) {
572 481         681 push @binary, 1 & $exp;
573 481         827 $exp >>= 1;
574             }
575 108         147 my $result = $this;
576 108         216 while (@binary) {
577 481         979 $result *= $result;
578 481 100       1261 $result *= $this if pop @binary;
579 481         970 $result %= $that;
580             }
581 108         291 return $result;
582             }
583              
584             sub exp_mod {
585 99     99 1 902 my ($this, $exp) = @_;
586 99         211 _check_int($exp);
587 99 100       185 return $this->new if 0 == $this->degree;
588 93 100       200 return $this->new($this->coeff_one) if 0 == $exp;
589 91         143 my @binary = ();
590 91         184 while ($exp > 1) {
591 346         488 push @binary, 1 & $exp;
592 346         607 $exp >>= 1;
593             }
594 91         158 my $result = $this->new($this->coeff_zero, $this->coeff_one);
595 91         188 while (@binary) {
596 346         749 $result *= $result;
597 346 100       776 if (pop @binary) {
598 166         243 local $max_degree;
599 166         368 $result <<= 1;
600             }
601 346         755 $result %= $this;
602             }
603 91         238 return $result;
604             }
605              
606             sub shift_up {
607 450     450 1 718 my ($this, $exp) = @_;
608 450         968 _check_int($exp);
609 450 100 100     1006 croak 'exponent too large' if
610             defined($max_degree) && $this->degree + $exp > $max_degree;
611 449 100       1557 return $this if !$exp;
612 447         883 return $this->new(($this->coeff_zero) x $exp, $this->coeff);
613             }
614              
615             sub shift_down {
616 5     5 1 10 my ($this, $exp) = @_;
617 5         13 _check_int($exp);
618 5 100       15 return $this if !$exp;
619 3         17 return $this->new( map { $this->coeff($_) } $exp .. $this->degree );
  1         4  
620             }
621              
622             sub inflate {
623 12     12 1 724 my ($this, $exp) = @_;
624 12         25 my $degree = $this->degree;
625 12 100       34 return $this if $degree <= 0;
626 6         15 _check_int($exp);
627 6 100 100     89 croak 'exponent too large' if
628             defined($max_degree) && $degree * $exp > $max_degree;
629 5 100       13 return $this->new($this->evaluate($this->coeff_one)) if !$exp;
630 4         9 my @zeroes = ($this->coeff_zero) x ($exp - 1);
631 4 100       10 return $this if !@zeroes;
632 3         6 my ($const, @coeff) = $this->coeff;
633 3         7 return $this->new($const, map {@zeroes, $_} @coeff);
  10         32  
634             }
635              
636             sub deflate {
637 9     9 1 290 my ($this, $exp) = @_;
638 9         21 _check_int($exp);
639 9 100       22 return undef if !$exp;
640 7 100       17 return $this if $exp <= 1;
641 4         9 my $degree = $this->degree;
642 4 100       9 return $this if $degree <= 0;
643             my @coeff =
644             map {
645 2         7 my $c = $this->coeff($_);
  11         20  
646 11 100       31 !($_ % $exp)? $c: !$c? (): return undef
    100          
647             } 0 .. $degree;
648 1         3 return $this->new(@coeff);
649             }
650              
651             sub slice {
652 49     49 1 483 my ($this, $start, $count) = @_;
653 49         98 _check_int($start, $count);
654 49         75 my $degree = $this->degree;
655 49         80 my $end = $start+$count-1;
656 49 100       81 if ($degree <= $end) {
657 34 100       60 return $this if 0 == $start;
658 32         43 $end = $degree;
659             }
660 47         81 return $this->new( map { $this->coeff($_) } $start .. $end );
  60         93  
661             }
662              
663             sub differentiate {
664 3     3 1 12 my ($this) = @_;
665 3         7 my $n = $this->coeff_zero;
666 3         5 my $one = $this->coeff_one;
667             return $this->new(
668 3         10 map { $this->coeff($_) * ($n += $one) } 1..$this->degree
  12         215  
669             );
670             }
671              
672             sub integrate {
673 7     7 1 278 my ($this, $const) = @_;
674 7         13 my $zero = $this->coeff_zero;
675 7         13 my $one = $this->coeff_one;
676 7         11 my $n = $zero;
677 7 100       15 if (!defined $const) {
678 5         10 $const = $zero;
679             }
680             return $this->new(
681             $const,
682             map {
683 7         15 $n += $one;
  31         280  
684 31         230 my $c = $this->coeff($_);
685 31 100       75 $zero == $c? $c: $c / $n
686             } 0..$this->degree
687             );
688             }
689              
690             sub definite_integral {
691 2     2 1 146 my ($this, $lower, $upper) = @_;
692 2         5 my $ad = $this->integrate;
693 2         6 return $ad->evaluate($upper) - $ad->evaluate($lower);
694             }
695              
696             sub _make_ltz {
697 99     99   171 my ($config, $zero) = @_;
698 99 100       233 return 0 if !$config->{'fold_sign'};
699 45         66 my $sgn = $config->{'sign_of_coeff'};
700             return
701             defined($sgn)?
702 11     11   25 sub { $sgn->($_[0]) < 0 }:
703 45 100   82   206 sub { $_[0] < $zero };
  82         219  
704             }
705              
706             sub as_string {
707 75     75 1 2491 my ($this, $params) = @_;
708             my %config = (
709             @string_defaults,
710 75 100 100     149 %{$params || $this->string_config || (ref $this)->string_config},
  75         677  
711             );
712 75         302 my $max_exp = $this->degree;
713 75 100       170 if ($max_exp < 0) {
714 24         30 $max_exp = 0;
715             }
716 75         124 my $result = q{};
717 75         128 my $zero = $this->coeff_zero;
718 75         161 my $ltz = _make_ltz(\%config, $zero);
719 75         152 my $one = $this->coeff_one;
720 75         124 my $with_variable = $config{'with_variable'};
721 75 100       249 foreach my $exp ($config{'ascending'}? 0..$max_exp: reverse 0..$max_exp) {
722 208         371 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         145 next;
732             }
733              
734             # plus/minus
735 165 100 100     488 if ($ltz && $ltz->($coeff)) {
736 17         113 $coeff = -$coeff;
737 17 100       69 $result .= $config{q[] eq $result? 'leading_minus': 'minus'};
738             }
739             else{
740 148 100       445 $result .= $config{q[] eq $result? 'leading_plus': 'plus'};
741             }
742              
743             # coefficient
744 165 100 100     872 if (
      100        
      100        
      100        
745             !$with_variable ||
746             !$config{'fold_one'} ||
747             0 == $exp && $config{'fold_exp_zero'} ||
748             $one != $coeff
749             ) {
750 113         252 $result .= $config{'convert_coeff'}->($coeff);
751 113 100       455 next if !$with_variable;
752 99 100 100     289 if (0 != $exp || !$config{'fold_exp_zero'}) {
753 44         74 $result .= $config{'times'};
754             }
755             }
756              
757             # variable and exponent
758 151 100 100     539 if (0 != $exp || !$config{'fold_exp_zero'}) {
759 96         141 $result .= $config{'variable'};
760 96 100 100     255 if (1 != $exp || !$config{'fold_exp_one'}) {
761 52         118 $result .= $config{'power'} . $exp;
762             }
763             }
764             }
765             return join q{},
766             $config{'prefix'},
767             $config{'wrap'}->($this, $result),
768 75         181 $config{'suffix'};
769             }
770              
771             sub as_horner_tree {
772 12     12 1 29 my ($this, $params) = @_;
773 12         22 my %config = (@tree_defaults, %{$params});
  12         103  
774 12         36 my $exp = $this->degree;
775 12 100       34 if ($exp < 0) {
776 1         2 $exp = 0;
777             }
778 12         20 my $zero = $this->coeff_zero;
779 12         26 my $ltz = _make_ltz(\%config, $zero);
780 12         29 my $coeff = $this->coeff($exp);
781 12   100     30 my $first_is_neg = $ltz && $ltz->($coeff);
782 12 100       24 if ($first_is_neg) {
783 3         7 $coeff = -$coeff;
784             }
785             my $result =
786             $exp && $this->coeff_one == $coeff? undef:
787 12 100 100     33 $config{'constant'}->($coeff);
788 12         36 my $is_sum = 0;
789 12         24 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
790 12 100       22 my $variable = $var_is_dynamic? undef: $config{'variable'};
791 12         25 while (0 <= --$exp) {
792 18         46 $coeff = $this->coeff($exp);
793 18 100       37 if ($is_sum) {
794 6         12 $result = $config{'group'}->($result);
795             }
796 18 100       45 if ($var_is_dynamic) {
797 6         11 $variable = $config{'variable'}->();
798             }
799 18         43 my $power = $variable;
800 18 100       30 if ($config{'expand_power'}) {
801 8         13 $is_sum = $zero != $coeff;
802             }
803             else {
804 10         16 my $skip = 0;
805 10         28 while ($zero == $coeff) {
806 10         14 ++$skip;
807 10 100       20 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       25 ++$skip if 0 <= $exp;
813 8 100       22 $power = $config{'power'}->($variable, $skip) if 1 < $skip;
814             }
815 10         38 $is_sum = 0 <= $exp;
816             }
817             $result =
818 18 100       39 defined($result)? $config{'product'}->($result, $power): $power;
819 18 100       75 if ($first_is_neg) {
820 2         5 $result = $config{'negation'}->($result);
821 2         9 $first_is_neg = 0;
822             }
823 18 100       35 if ($is_sum) {
824 12   100     31 my $is_neg = $ltz && $ltz->($coeff);
825 12 100       23 if ($is_neg) {
826 4         7 $coeff = -$coeff;
827             }
828 12         25 my $const = $config{'constant'}->($coeff);
829 12 100       54 $result = $config{$is_neg? 'difference': 'sum'}->($result, $const);
830             }
831             }
832 12 100       56 if ($first_is_neg) {
833 1         3 $result = $config{'negation'}->($result);
834             }
835 12         94 return $result;
836             }
837              
838             sub as_power_sum_tree {
839 12     12 1 31 my ($this, $params) = @_;
840 12         18 my %config = (@tree_defaults, %{$params});
  12         102  
841 12         33 my $exp = $this->degree;
842 12 100       31 if ($exp < 0) {
843 1         2 $exp = 0;
844             }
845 12         22 my $zero = $this->coeff_zero;
846 12         34 my $ltz = _make_ltz(\%config, $zero);
847 12         24 my $one = $this->coeff_one;
848 12         19 my $result = undef;
849 12         23 my $var_is_dynamic = 'CODE' eq ref $config{'variable'};
850 12 100       23 my $variable = $var_is_dynamic? undef: $config{'variable'};
851 12         58 while (0 <= $exp) {
852 34         63 my $coeff = $this->coeff($exp);
853              
854             # skip term?
855 34 100 100     95 next if defined($result) && $zero == $coeff;
856              
857             # variable and exponent
858 22         28 my $term = undef;
859 22 100       41 if (0 != $exp) {
860 12 100       21 if ($var_is_dynamic) {
861 3         9 $variable = $config{'variable'}->();
862             }
863 12         36 $term = $variable;
864 12 100       22 if (1 != $exp) {
865 9 100       27 if ($config{'expand_power'}) {
866 2         4 my $todo = $exp-1;
867 2         5 for (my $tmp=$exp; $tmp>1; --$tmp) {
868 2 100       4 if ($var_is_dynamic) {
869 1         3 $variable = $config{'variable'}->();
870             }
871 2         8 $term = $config{'product'}->($term, $variable);
872             }
873             }
874             else {
875 7         15 $term = $config{'power'}->($term, $exp);
876             }
877             }
878             }
879              
880             # sign and coefficient
881 22   100     87 my $is_neg = $ltz && $ltz->($coeff);
882 22 100       71 if ($is_neg) {
883 7         10 $coeff = -$coeff;
884             }
885 22 100 100     63 if (0 == $exp || $one != $coeff) {
886 16         34 my $const = $config{'constant'}->($coeff);
887 16 100       66 $term = 0 == $exp? $const: $config{'product'}->($const, $term);
888             }
889              
890             # summation
891 22 100       51 if (defined $result) {
892 10 100       27 $result = $config{$is_neg? 'difference': 'sum'}->($result, $term);
893             }
894             else {
895 12 100       29 $result = $is_neg? $config{'negation'}->($term): $term;
896             }
897             }
898             continue {
899 34         97 --$exp;
900             }
901 12         72 return $result;
902             }
903              
904             sub gcd {
905 3     3 1 187 my ($this, $that, $mod) = @_;
906 3 50       13 defined $mod or $mod = 'mod';
907 3         22 my $mod_op = $this->can($mod);
908 3 50       9 $mod_op or croak "no such method: $mod";
909 3         9 my ($this_d, $that_d) = ($this->degree, $that->degree);
910 3 50       11 if ($this_d < $that_d) {
911 3         9 ($this, $that) = ($that, $this);
912 3         10 ($this_d, $that_d) = ($that_d, $this_d);
913             }
914 3         11 while (0 <= $that_d) {
915 6         17 ($this, $that) = ($that, $this->$mod_op($that));
916 6         20 ($this_d, $that_d) = ($that_d, $that->degree);
917 6 50       27 $this_d > $that_d or croak 'bad modulo operator';
918             }
919 3         25 return $this;
920             }
921              
922             sub xgcd {
923 224     224 1 644 my ($this, $that) = @_;
924 224 50       433 croak 'array context required' if !wantarray;
925 224         431 my ($d1, $d2) = ($this->new($this->coeff_one), $this->new);
926 224 50       446 if ($this->degree < $that->degree) {
927 0         0 ($this, $that) = ($that, $this);
928 0         0 ($d1, $d2) = ($d2, $d1);
929             }
930 224         382 my ($m1, $m2) = ($d2, $d1);
931 224         406 while (!$that->is_zero) {
932 550         1373 my ($div, $mod) = $this->divmod($that);
933 550         987 ($this, $that) = ($that, $mod);
934 550         1141 ($d1, $d2, $m1, $m2) =
935             ($m1, $m2, $d1->sub_($m1->mul($div)), $d2->sub_($m2->mul($div)));
936             }
937 224         712 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 640 my ($this, $that) = @_;
951 112         278 my ($d, $d2) = ($that->xgcd($this))[0, 2];
952 112 50 33     289 croak 'division by zero polynomial' if $that->is_zero || $d2->is_zero;
953 112         255 return $d2->div_const($d->coeff($d->degree));
954             }
955              
956             1;
957             __END__