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   796184 use 5.006002;
  8         113  
4 8     8   49 use strict;
  8         19  
  8         194  
5 8     8   50 use warnings;
  8         17  
  8         278  
6              
7 8     8   5960 use Math::BigInt::Lib 1.999801;
  8         100254  
  8         46  
8              
9             our @ISA = qw< Math::BigInt::Lib >;
10              
11             our $VERSION = '1.3011';
12              
13 8         56 use Math::Pari qw(PARI pari2pv gdivent bittest
14             gcmp gcmp0 gcmp1 gcd ifact gpui gmul
15             binomial lcm
16 8     8   6578 );
  8         91289  
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 46238     46238   3283148 PARI($_[1] . '')
32             }
33              
34             sub _from_hex {
35 321     321   4301 my $hex = $_[1];
36 321 50       1020 $hex = '0x' . $hex unless substr($hex, 0, 2) eq '0x';
37 321         934 Math::Pari::_hex_cvt($hex);
38             }
39              
40             sub _from_oct {
41 6     6   59 Math::Pari::_hex_cvt('0' . $_[1]);
42             }
43              
44             sub _from_bin {
45 52     52   695 my $bin = $_[1];
46 52 50       165 $bin = '0b' . $bin unless substr($bin, 0, 2) eq '0b';
47 52         174 Math::Pari::_hex_cvt($bin);
48             }
49              
50             sub _as_bin {
51 18     18   703 my $v = unpack('B*', _mp2os($_[1]));
52 18 100       61 return "0b0" if $v eq '';
53 15         82 $v =~ s/^0*/0b/;
54 15         58 $v;
55             }
56              
57             sub _as_oct {
58 15     15   505 my $v = _mp2oct($_[1]);
59 15 100       50 return "00" if $v eq '';
60 12         55 $v =~ s/^0*/0/;
61 12         36 $v;
62             }
63              
64             sub _as_hex {
65 75     75   943 my $v = unpack('H*', _mp2os($_[1]));
66 75 100       203 return "0x0" if $v eq '';
67 62         239 $v =~ s/^0*/0x/;
68 62         172 $v;
69             }
70              
71             sub _to_bin {
72 65     65   600 my $v = unpack('B*', _mp2os($_[1]));
73 65         297 $v =~ s/^0+//;
74 65 100       173 return "0" if $v eq '';
75 55         114 $v;
76             }
77              
78             sub _to_oct {
79 12     12   487 my $v = _mp2oct($_[1]);
80 12         30 $v =~ s/^0+//;
81 12 100       36 return "0" if $v eq '';
82 10         29 $v;
83             }
84              
85             sub _to_hex {
86 10     10   420 my $v = unpack('H*', _mp2os($_[1]));
87 10         32 $v =~ s/^0+//;
88 10 100       29 return "0" if $v eq '';
89 8         22 $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   299 my($p) = @_;
99 168         395 $p = PARI($p);
100 168         982 my $base = PARI(1) << PARI(4*8);
101 168         687 my $res = '';
102 168         702 while ($p != 0) {
103 167         563 my $r = $p % $base;
104 167         714 $p = ($p - $r) / $base;
105 167         774 my $buf = pack 'V', $r;
106 167 100       559 if ($p == 0) {
107 140 100       859 $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         751 $res .= $buf;
113             }
114 168         902 scalar reverse $res;
115             }
116              
117             sub _mp2oct {
118 27     27   61 my($p) = @_;
119 27         108 $p = PARI($p);
120 27         71 my $base = PARI(8);
121 27         49 my $res = '';
122 27         138 while ($p != 0) {
123 181         506 my $r = $p % $base;
124 181         656 $p = ($p - $r) / $base;
125 181         920 $res .= $r;
126             }
127 27         121 scalar reverse $res;
128             }
129              
130 12163     12163   1046182 sub _zero { PARI(0) }
131 735     735   50840 sub _one { PARI(1) }
132 95     95   716 sub _two { PARI(2) }
133 2     2   11 sub _ten { PARI(10) }
134              
135 5     5   41 sub _1ex { gpui(PARI(10), $_[1]) }
136              
137 29232     29232   789405 sub _copy { $_[1] + $zero; }
138              
139 51914     51914   1313258 sub _str { pari2pv($_[1]) }
140              
141 10299     10299   91158 sub _num { 0 + pari2pv($_[1]) }
142              
143 23627     23627   324697 sub _add { $_[1] += $_[2] }
144              
145             sub _sub {
146 23733 100   23733   120669 if ($_[3]) {
147 2632         8416 $_[2] = $_[1] - $_[2];
148 2632         6074 return $_[2];
149             }
150 21101         92433 $_[1] -= $_[2];
151             }
152              
153 11260     11260   334357 sub _mul { $_[1] = gmul($_[1], $_[2]) }
154              
155             sub _div {
156 4964 100   4964   53235 if (wantarray) {
157 874         3766 my $r = $_[1] % $_[2];
158 874         4705 $_[1] = gdivent($_[1], $_[2]);
159 874         3552 return ($_[1], $r);
160             }
161 4090         20442 $_[1] = gdivent($_[1], $_[2]);
162             }
163              
164 691     691   6883 sub _mod { $_[1] %= $_[2]; }
165              
166             sub _nok {
167 16     16   156 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         27 my $twok = $class -> _mul($class -> _two(), $class -> _copy($k));
  16         40  
181 16 100       50 if ($class -> _acmp($twok, $n) > 0) {
182 8         27 $k = $class -> _sub($class -> _copy($n), $k);
183             }
184             }
185              
186 16         166 binomial($n, $k);
187             }
188              
189 2364     2364   22983 sub _inc { ++$_[1]; }
190 68     68   898 sub _dec { --$_[1]; }
191              
192 36     36   1488 sub _and { $_[1] &= $_[2] }
193              
194 53     53   1909 sub _xor { $_[1] ^= $_[2] }
195              
196 51     51   1794 sub _or { $_[1] |= $_[2] }
197              
198 258     258   7353 sub _pow { gpui($_[1], $_[2]) }
199              
200 25     25   683 sub _gcd { gcd($_[1], $_[2]) }
201              
202 11     11   289 sub _lcm { lcm($_[1], $_[2]) }
203              
204 47266     47266   844177 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   1162305 my ($class, $x) = @_;
211 35517 100       93711 return 0 if gcmp0($x); # 0 has no trailing zeros
212              
213 34902         69348 my $u = $class -> _str($x);
214 34902         425082 $u =~ /(0*)\z/;
215 34902         100858 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   277 if ($_[2] < 0) {
229 10         141 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         367 pari2pv(gdivent($_[1], $ten ** $_[2]) % $ten);
234             }
235              
236 123761     123761   2993922 sub _is_zero { gcmp0($_[1]) }
237              
238 2056     2056   20727 sub _is_one { gcmp1($_[1]) }
239              
240 60 100   60   2403 sub _is_two { gcmp($_[1], $two) ? 0 : 1 }
241              
242 2 100   2   38 sub _is_ten { gcmp($_[1], $ten) ? 0 : 1 }
243              
244 23 100   23   1552 sub _is_even { bittest($_[1], 0) ? 0 : 1 }
245              
246 196 100   196   4982 sub _is_odd { bittest($_[1], 0) ? 1 : 0 }
247              
248             sub _acmp {
249 27824   100 27824   373030 my $i = gcmp($_[1], $_[2]) || 0;
250             # work around bug in Pari (on 64bit systems?)
251 27824 100       57996 $i = -1 if $i == 4294967295;
252 27824         53188 $i;
253             }
254              
255             sub _check {
256 1830     1830   743990 my ($class, $x) = @_;
257 1830 50       4776 return "Undefined" unless defined $x;
258 1830 100       4182 return "$x is not a reference to Math::Pari"
259             unless ref($x) eq 'Math::Pari';
260 1829         3991 return 0;
261             }
262              
263             sub _sqrt {
264             # square root of $x
265 242     242   4512 my ($class, $x) = @_;
266 242         1252 my $y = Math::Pari::sqrtint($x);
267 242 50       2497 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   1886 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   1041 my ($c, $num, $exp, $mod) = @_;
280              
281             # in the trivial case,
282 144 100       457 if (gcmp1($mod)) {
283 50         144 $num = PARI(0);
284 50         121 return $num;
285             }
286              
287 94 100       227 if (gcmp1($num)) {
288 29         82 $num = PARI(1);
289 29         73 return $num;
290             }
291              
292 65 100       156 if (gcmp0($num)) {
293 12 100       31 if (gcmp0($exp)) {
294 3         13 return PARI(1);
295             } else {
296 9         41 return PARI(0);
297             }
298             }
299              
300 53         127 my $acc = $c -> _copy($num);
301 53         133 my $t = $c -> _one();
302              
303 53         135 my $expbin = $c -> _to_bin($exp);
304 53         86 my $len = length($expbin);
305 53         115 while (--$len >= 0) {
306 268 100       598 if (substr($expbin, $len, 1) eq '1') { # is_odd
307 141         317 $c -> _mul($t, $acc);
308 141         290 $t = $c -> _mod($t, $mod);
309             }
310 268         634 $c -> _mul($acc, $acc);
311 268         498 $acc = $c -> _mod($acc, $mod);
312             }
313 53         93 $num = $t;
314 53         159 $num;
315             }
316              
317             sub _rsft {
318             # (X, Y, N) = @_; means X >> Y in base N
319              
320 14002 100   14002   66986 if ($_[3] != 2) {
321 13973         133713 return $_[1] = gdivent($_[1], PARI($_[3]) ** $_[2]);
322             }
323 29         175 $_[1] >>= $_[2];
324             }
325              
326             sub _lsft {
327             # (X, Y, N) = @_; means X >> Y in base N
328              
329 9736 100   9736   47028 if ($_[3] != 2) {
330 9721         93962 return $_[1] *= PARI($_[3]) ** $_[2];
331             }
332 15         132 $_[1] <<= $_[2];
333             }
334              
335             sub _fac {
336             # factorial of argument
337 48     48   1723 $_[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__