File Coverage

blib/lib/Math/BigInt/Pari.pm
Criterion Covered Total %
statement 157 162 96.9
branch 60 66 90.9
condition 2 2 100.0
subroutine 57 60 95.0
pod n/a
total 276 290 95.1


line stmt bran cond sub pod time code
1             package Math::BigInt::Pari;
2              
3 8     8   794544 use 5.006002;
  8         110  
4 8     8   45 use strict;
  8         18  
  8         196  
5 8     8   39 use warnings;
  8         18  
  8         294  
6              
7 8     8   6466 use Math::BigInt::Lib 1.999801;
  8         100103  
  8         40  
8              
9             our @ISA = qw< Math::BigInt::Lib >;
10              
11             our $VERSION = '1.3010';
12              
13 8         45 use Math::Pari qw(PARI pari2pv gdivent bittest
14             gcmp gcmp0 gcmp1 gcd ifact gpui gmul
15             binomial lcm
16 8     8   6394 );
  8         91819  
17              
18             # MBI will call this, so catch it and throw it away
19       2     sub import { }
20             sub api_version() { 2; } # we are compatible with MBI v1.83 and up
21              
22             my $zero = PARI(0); # for _copy
23             my $one = PARI(1); # for _inc and _dec
24             my $two = PARI(2); # for _is_two
25             my $ten = PARI(10); # for _digit
26              
27             sub _new {
28             # the . '' is because new($2) will give a magical scalar to us, and PARI
29             # does not like this at all :/
30             # use Devel::Peek; print Dump($_[1]);
31 46237     46237   3260406 PARI($_[1] . '')
32             }
33              
34             sub _from_hex {
35 321     321   4613 my $hex = $_[1];
36 321 50       960 $hex = '0x' . $hex unless substr($hex, 0, 2) eq '0x';
37 321         882 Math::Pari::_hex_cvt($hex);
38             }
39              
40             sub _from_oct {
41 6     6   55 Math::Pari::_hex_cvt('0' . $_[1]);
42             }
43              
44             sub _from_bin {
45 52     52   667 my $bin = $_[1];
46 52 50       164 $bin = '0b' . $bin unless substr($bin, 0, 2) eq '0b';
47 52         168 Math::Pari::_hex_cvt($bin);
48             }
49              
50             sub _as_bin {
51 18     18   703 my $v = unpack('B*', _mp2os($_[1]));
52 18 100       66 return "0b0" if $v eq '';
53 15         68 $v =~ s/^0*/0b/;
54 15         66 $v;
55             }
56              
57             sub _as_oct {
58 15     15   569 my $v = _mp2oct($_[1]);
59 15 100       50 return "00" if $v eq '';
60 12         50 $v =~ s/^0*/0/;
61 12         47 $v;
62             }
63              
64             sub _as_hex {
65 75     75   997 my $v = unpack('H*', _mp2os($_[1]));
66 75 100       217 return "0x0" if $v eq '';
67 62         233 $v =~ s/^0*/0x/;
68 62         167 $v;
69             }
70              
71             sub _to_bin {
72 65     65   620 my $v = unpack('B*', _mp2os($_[1]));
73 65         289 $v =~ s/^0+//;
74 65 100       177 return "0" if $v eq '';
75 55         116 $v;
76             }
77              
78             sub _to_oct {
79 12     12   483 my $v = _mp2oct($_[1]);
80 12         36 $v =~ s/^0+//;
81 12 100       34 return "0" if $v eq '';
82 10         29 $v;
83             }
84              
85             sub _to_hex {
86 10     10   426 my $v = unpack('H*', _mp2os($_[1]));
87 10         35 $v =~ s/^0+//;
88 10 100       32 return "0" if $v eq '';
89 8         23 $v;
90             }
91              
92             sub _to_bytes {
93 0     0   0 my $bytes = _mp2os($_[1]);
94 0 0       0 return $bytes eq '' ? "\x00" : $bytes;
95             }
96              
97             sub _mp2os {
98 168     168   314 my($p) = @_;
99 168         429 $p = PARI($p);
100 168         943 my $base = PARI(1) << PARI(4*8);
101 168         700 my $res = '';
102 168         700 while ($p != 0) {
103 167         623 my $r = $p % $base;
104 167         743 $p = ($p - $r) / $base;
105 167         800 my $buf = pack 'V', $r;
106 167 100       519 if ($p == 0) {
107 140 100       825 $buf = $r >= 16777216 ? $buf
    100          
    100          
108             : $r >= 65536 ? substr($buf, 0, 3)
109             : $r >= 256 ? substr($buf, 0, 2)
110             : substr($buf, 0, 1);
111             }
112 167         728 $res .= $buf;
113             }
114 168         901 scalar reverse $res;
115             }
116              
117             sub _mp2oct {
118 27     27   56 my($p) = @_;
119 27         74 $p = PARI($p);
120 27         65 my $base = PARI(8);
121 27         47 my $res = '';
122 27         123 while ($p != 0) {
123 181         460 my $r = $p % $base;
124 181         670 $p = ($p - $r) / $base;
125 181         900 $res .= $r;
126             }
127 27         112 scalar reverse $res;
128             }
129              
130 12161     12161   1039139 sub _zero { PARI(0) }
131 735     735   50304 sub _one { PARI(1) }
132 95     95   692 sub _two { PARI(2) }
133 2     2   9 sub _ten { PARI(10) }
134              
135 5     5   46 sub _1ex { gpui(PARI(10), $_[1]) }
136              
137 29218     29218   836800 sub _copy { $_[1] + $zero; }
138              
139 51908     51908   1299960 sub _str { pari2pv($_[1]) }
140              
141 10299     10299   89390 sub _num { 0 + pari2pv($_[1]) }
142              
143 23625     23625   317707 sub _add { $_[1] += $_[2] }
144              
145             sub _sub {
146 23730 100   23730   117386 if ($_[3]) {
147 2632         8418 $_[2] = $_[1] - $_[2];
148 2632         5794 return $_[2];
149             }
150 21098         88974 $_[1] -= $_[2];
151             }
152              
153 11254     11254   334201 sub _mul { $_[1] = gmul($_[1], $_[2]) }
154              
155             sub _div {
156 4960 100   4960   53269 if (wantarray) {
157 870         3728 my $r = $_[1] % $_[2];
158 870         4776 $_[1] = gdivent($_[1], $_[2]);
159 870         3709 return ($_[1], $r);
160             }
161 4090         19971 $_[1] = gdivent($_[1], $_[2]);
162             }
163              
164 691     691   6736 sub _mod { $_[1] %= $_[2]; }
165              
166             sub _nok {
167 16     16   152 my ($class, $n, $k) = @_;
168              
169             # Math::Pari doesn't seem to be able to handle the case when n is large and
170             # k is almost as big as n. For instance, the following returns zero (at
171             # least for certain versions and configurations):
172             #
173             # $n = PARI("10000000000000000000");
174             # $k = PARI("9999999999999999999");
175             # print Math::Pari::binomial($n, $k);'
176              
177             # If k > n/2, or, equivalently, 2*k > n, compute nok(n, k) as nok(n, n-k).
178              
179             {
180 16         28 my $twok = $class -> _mul($class -> _two(), $class -> _copy($k));
  16         37  
181 16 100       50 if ($class -> _acmp($twok, $n) > 0) {
182 8         20 $k = $class -> _sub($class -> _copy($n), $k);
183             }
184             }
185              
186 16         152 binomial($n, $k);
187             }
188              
189 2364     2364   23024 sub _inc { ++$_[1]; }
190 68     68   940 sub _dec { --$_[1]; }
191              
192 36     36   1482 sub _and { $_[1] &= $_[2] }
193              
194 53     53   1968 sub _xor { $_[1] ^= $_[2] }
195              
196 51     51   1844 sub _or { $_[1] |= $_[2] }
197              
198 258     258   7345 sub _pow { gpui($_[1], $_[2]) }
199              
200 25     25   691 sub _gcd { gcd($_[1], $_[2]) }
201              
202 11     11   311 sub _lcm { lcm($_[1], $_[2]) }
203              
204 47266     47266   841452 sub _len { length(pari2pv($_[1])) } # costly!
205              
206             # XXX TODO: calc len in base 2 then appr. in base 10
207 0     0   0 sub _alen { length(pari2pv($_[1])) }
208              
209             sub _zeros {
210 35517     35517   1127296 my ($class, $x) = @_;
211 35517 100       93054 return 0 if gcmp0($x); # 0 has no trailing zeros
212              
213 34902         68084 my $u = $class -> _str($x);
214 34902         423392 $u =~ /(0*)\z/;
215 34902         103556 return length($1);
216              
217             #my $s = pari2pv($_[1]);
218             #my $i = length($s);
219             #my $zeros = 0;
220             #while (--$i >= 0) {
221             # substr($s, $i, 1) eq '0' ? $zeros ++ : last;
222             #}
223             #$zeros;
224             }
225              
226             sub _digit {
227             # if $n < 0, we need to count from left and thus can't use the other method:
228 27 100   27   635 if ($_[2] < 0) {
229 10         149 return substr(pari2pv($_[1]), -1 - $_[2], 1);
230             }
231             # else this is faster (except for very short numbers)
232             # shift the number right by $n digits, then extract last digit via % 10
233 17         378 pari2pv(gdivent($_[1], $ten ** $_[2]) % $ten);
234             }
235              
236 123741     123741   2976086 sub _is_zero { gcmp0($_[1]) }
237              
238 2056     2056   20531 sub _is_one { gcmp1($_[1]) }
239              
240 60 100   60   2366 sub _is_two { gcmp($_[1], $two) ? 0 : 1 }
241              
242 2 100   2   28 sub _is_ten { gcmp($_[1], $ten) ? 0 : 1 }
243              
244 23 100   23   1558 sub _is_even { bittest($_[1], 0) ? 0 : 1 }
245              
246 196 100   196   4968 sub _is_odd { bittest($_[1], 0) ? 1 : 0 }
247              
248             sub _acmp {
249 27821   100 27821   369336 my $i = gcmp($_[1], $_[2]) || 0;
250             # work around bug in Pari (on 64bit systems?)
251 27821 100       55822 $i = -1 if $i == 4294967295;
252 27821         51713 $i;
253             }
254              
255             sub _check {
256 1830     1830   736149 my ($class, $x) = @_;
257 1830 50       5285 return "Undefined" unless defined $x;
258 1830 100       4592 return "$x is not a reference to Math::Pari"
259             unless ref($x) eq 'Math::Pari';
260 1829         4245 return 0;
261             }
262              
263             sub _sqrt {
264             # square root of $x
265 242     242   4398 my ($class, $x) = @_;
266 242         1157 my $y = Math::Pari::sqrtint($x);
267 242 50       2450 return $y * $y > $x ? $y - $one : $y; # bug in sqrtint()?
268             }
269              
270             sub _root {
271             # n'th root
272              
273             # Native version:
274 47     47   1915 return $_[1] = int(Math::Pari::sqrtn($_[1] + 0.5, $_[2]));
275             }
276              
277             sub _modpow {
278             # modulus of power ($x ** $y) % $z
279 144     144   968 my ($c, $num, $exp, $mod) = @_;
280              
281             # in the trivial case,
282 144 100       424 if (gcmp1($mod)) {
283 50         127 $num = PARI(0);
284 50         127 return $num;
285             }
286              
287 94 100       239 if (gcmp1($num)) {
288 29         88 $num = PARI(1);
289 29         87 return $num;
290             }
291              
292 65 100       151 if (gcmp0($num)) {
293 12 100       31 if (gcmp0($exp)) {
294 3         18 return PARI(1);
295             } else {
296 9         32 return PARI(0);
297             }
298             }
299              
300 53         126 my $acc = $c -> _copy($num);
301 53         144 my $t = $c -> _one();
302              
303 53         114 my $expbin = $c -> _to_bin($exp);
304 53         84 my $len = length($expbin);
305 53         111 while (--$len >= 0) {
306 268 100       575 if (substr($expbin, $len, 1) eq '1') { # is_odd
307 141         313 $c -> _mul($t, $acc);
308 141         268 $t = $c -> _mod($t, $mod);
309             }
310 268         606 $c -> _mul($acc, $acc);
311 268         493 $acc = $c -> _mod($acc, $mod);
312             }
313 53         89 $num = $t;
314 53         158 $num;
315             }
316              
317             sub _rsft {
318             # (X, Y, N) = @_; means X >> Y in base N
319              
320 14002 100   14002   64425 if ($_[3] != 2) {
321 13973         132886 return $_[1] = gdivent($_[1], PARI($_[3]) ** $_[2]);
322             }
323 29         174 $_[1] >>= $_[2];
324             }
325              
326             sub _lsft {
327             # (X, Y, N) = @_; means X >> Y in base N
328              
329 9737 100   9737   47881 if ($_[3] != 2) {
330 9722         92656 return $_[1] *= PARI($_[3]) ** $_[2];
331             }
332 15         135 $_[1] <<= $_[2];
333             }
334              
335             sub _fac {
336             # factorial of argument
337 48     48   1721 $_[1] = ifact($_[1]);
338             }
339              
340             # _set() - set an already existing object to the given scalar value
341              
342             sub _set {
343 0     0     my ($c, $x, $y) = @_;
344 0           *x = \PARI($y . '');
345             }
346              
347             1;
348              
349             __END__