File Coverage

blib/lib/Math/BigInt/Pari.pm
Criterion Covered Total %
statement 155 161 96.2
branch 59 66 89.3
condition 2 2 100.0
subroutine 57 60 95.0
pod n/a
total 273 289 94.4


line stmt bran cond sub pod time code
1             package Math::BigInt::Pari;
2              
3 8     8   1400738 use 5.006002;
  8         54  
4 8     8   52 use strict;
  8         12  
  8         256  
5 8     8   47 use warnings;
  8         38  
  8         699  
6              
7 8     8   7647 use Math::BigInt::Lib 1.999801;
  8         136981  
  8         81  
8              
9             our @ISA = qw< Math::BigInt::Lib >;
10              
11             our $VERSION = '1.3016';
12              
13 8         57 use Math::Pari qw(PARI pari2pv gdivent bittest
14             gcmp gcmp0 gcmp1 gcd ifact gpui gmul
15             binomial lcm
16 8     8   8637 );
  8         131129  
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 48991     48991   7090259 PARI($_[1] . '')
32             }
33              
34             sub _from_hex {
35 321     321   4692 my $hex = $_[1];
36 321 50       1239 $hex = '0x' . $hex unless substr($hex, 0, 2) eq '0x';
37 321         959 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 68     68   1283 my $bin = $_[1];
46 68 50       312 $bin = '0b' . $bin unless substr($bin, 0, 2) eq '0b';
47 68         321 Math::Pari::_hex_cvt($bin);
48             }
49              
50             sub _as_bin {
51 17     17   887 my $v = unpack('B*', _mp2os($_[1]));
52 17 100       64 return "0b0" if $v eq '';
53 14         73 $v =~ s/^0*/0b/;
54 14         53 $v;
55             }
56              
57             sub _as_oct {
58 15     15   1108 my $v = _mp2oct($_[1]);
59 15 100       57 return "00" if $v eq '';
60 12         70 $v =~ s/^0*/0/;
61 12         37 $v;
62             }
63              
64             sub _as_hex {
65 75     75   1183 my $v = unpack('H*', _mp2os($_[1]));
66 75 100       254 return "0x0" if $v eq '';
67 62         276 $v =~ s/^0*/0x/;
68 62         183 $v;
69             }
70              
71             sub _to_bin {
72 83     83   2259 my $v = unpack('B*', _mp2os($_[1]));
73 83         530 $v =~ s/^0+//;
74 83 100       278 return "0" if $v eq '';
75 73         189 $v;
76             }
77              
78             sub _to_oct {
79 12     12   869 my $v = _mp2oct($_[1]);
80 12         32 $v =~ s/^0+//;
81 12 100       46 return "0" if $v eq '';
82 10         31 $v;
83             }
84              
85             sub _to_hex {
86 10     10   475 my $v = unpack('H*', _mp2os($_[1]));
87 10         33 $v =~ s/^0+//;
88 10 100       27 return "0" if $v eq '';
89 8         45 $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 185     185   537 my($p) = @_;
99 185         552 $p = PARI($p);
100 185         1399 my $base = PARI(1) << PARI(4*8);
101 185         939 my $res = '';
102 185         983 while ($p != 0) {
103 184         810 my $r = $p % $base;
104 184         959 $p = ($p - $r) / $base;
105 184         1063 my $buf = pack 'V', $r;
106 184 100       670 if ($p == 0) {
107 157 100       1113 $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 184         931 $res .= $buf;
113             }
114 185         1246 scalar reverse $res;
115             }
116              
117             sub _mp2oct {
118 27     27   74 my($p) = @_;
119 27         98 $p = PARI($p);
120 27         82 my $base = PARI(8);
121 27         63 my $res = '';
122 27         248 while ($p != 0) {
123 181         468 my $r = $p % $base;
124 181         793 $p = ($p - $r) / $base;
125 181         995 $res .= $r;
126             }
127 27         173 scalar reverse $res;
128             }
129              
130 11230     11230   1624176 sub _zero { PARI(0) }
131 767     767   91616 sub _one { PARI(1) }
132 119     119   1072 sub _two { PARI(2) }
133 2     2   13 sub _ten { PARI(10) }
134              
135 5     5   49 sub _1ex { gpui(PARI(10), $_[1]) }
136              
137 35113     35113   1313847 sub _copy { $_[1] + $zero; }
138              
139 53310     53310   1923185 sub _str { pari2pv($_[1]) }
140              
141 10058     10058   109143 sub _num { 0 + pari2pv($_[1]) }
142              
143 24444     24444   351089 sub _add { $_[1] += $_[2] }
144              
145             sub _sub {
146 24731 100   24731   129842 if ($_[3]) {
147 2507         9657 $_[2] = $_[1] - $_[2];
148 2507         5587 return $_[2];
149             }
150 22224         112172 $_[1] -= $_[2];
151             }
152              
153 11257     11257   719180 sub _mul { $_[1] = gmul($_[1], $_[2]) }
154              
155             sub _div {
156 5095 100   5095   50606 if (wantarray) {
157 874         3868 my $r = $_[1] % $_[2];
158 874         4755 $_[1] = gdivent($_[1], $_[2]);
159 874         3183 return ($_[1], $r);
160             }
161 4221         22248 $_[1] = gdivent($_[1], $_[2]);
162             }
163              
164 722     722   7617 sub _mod { $_[1] %= $_[2]; }
165              
166             sub _nok {
167 16     16   84 my ($class, $n, $k) = @_;
168              
169             # Math::Pari::binomial() doesn't seem to be able to handle input larger
170             # than 9223372036854775807, i.e., 2**63-1. For instance, the following
171             # returns zero, not one (at least for certain versions and configurations):
172             #
173             # $n = PARI("9223372036854775808");
174             # $k = PARI("9223372036854775808");
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             #my $imax = $class -> _new("9223372036854775807");
180              
181             #if ($class -> _acmp($n, $imax) > 0 ||
182             # $class -> _acmp($k, $imax) > 0)
183             #{
184             # return $class -> SUPER::_nok($n, $k);
185             #}
186              
187 16         44 my $twok = $class -> _mul($class -> _two(), $class -> _copy($k));
188 16 50       97 if ($class -> _acmp($twok, $n) > 0) {
189 0         0 $k = $class -> _sub($class -> _copy($n), $k);
190             }
191              
192 16         224 binomial($n, $k);
193             }
194              
195 2344     2344   26894 sub _inc { ++$_[1]; }
196 84     84   1626 sub _dec { --$_[1]; }
197              
198 36     36   1898 sub _and { $_[1] &= $_[2] }
199              
200 53     53   2052 sub _xor { $_[1] ^= $_[2] }
201              
202 51     51   2659 sub _or { $_[1] |= $_[2] }
203              
204 276     276   14014 sub _pow { gpui($_[1], $_[2]) }
205              
206 25     25   545 sub _gcd { gcd($_[1], $_[2]) }
207              
208 8     8   281 sub _lcm { lcm($_[1], $_[2]) }
209              
210 49233     49233   1220950 sub _len { length(pari2pv($_[1])) } # costly!
211              
212             # XXX TODO: calc len in base 2 then appr. in base 10
213 0     0   0 sub _alen { length(pari2pv($_[1])) }
214              
215             sub _zeros {
216 36156     36156   1385039 my ($class, $x) = @_;
217 36156 100       101955 return 0 if gcmp0($x); # 0 has no trailing zeros
218              
219 35539         76122 my $u = $class -> _str($x);
220 35539         549626 $u =~ /(0*)\z/;
221 35539         111294 return length($1);
222              
223             #my $s = pari2pv($_[1]);
224             #my $i = length($s);
225             #my $zeros = 0;
226             #while (--$i >= 0) {
227             # substr($s, $i, 1) eq '0' ? $zeros ++ : last;
228             #}
229             #$zeros;
230             }
231              
232             sub _digit {
233             # if $n < 0, we need to count from left and thus can't use the other method:
234 27 100   27   370 if ($_[2] < 0) {
235 10         153 return substr(pari2pv($_[1]), -1 - $_[2], 1);
236             }
237             # else this is faster (except for very short numbers)
238             # shift the number right by $n digits, then extract last digit via % 10
239 17         503 pari2pv(gdivent($_[1], $ten ** $_[2]) % $ten);
240             }
241              
242 117117     117117   4287067 sub _is_zero { gcmp0($_[1]) }
243              
244 1312     1312   34915 sub _is_one { gcmp1($_[1]) }
245              
246 58 100   58   4813 sub _is_two { gcmp($_[1], $two) ? 0 : 1 }
247              
248 2 100   2   17 sub _is_ten { gcmp($_[1], $ten) ? 0 : 1 }
249              
250 23 100   23   3505 sub _is_even { bittest($_[1], 0) ? 0 : 1 }
251              
252 207 100   207   8718 sub _is_odd { bittest($_[1], 0) ? 1 : 0 }
253              
254             sub _acmp {
255 30892   100 30892   465990 my $i = gcmp($_[1], $_[2]) || 0;
256             # work around bug in Pari (on 64bit systems?)
257 30892 100       63893 $i = -1 if $i == 4294967295;
258 30892         61896 $i;
259             }
260              
261             sub _check {
262 1838     1838   994151 my ($class, $x) = @_;
263 1838 50       6357 return "Undefined" unless defined $x;
264 1838 100       5492 return "$x is not a reference to Math::Pari"
265             unless ref($x) eq 'Math::Pari';
266 1837         5058 return 0;
267             }
268              
269             sub _sqrt {
270             # square root of $x
271 139     139   1819 my ($class, $x) = @_;
272 139         1045 my $y = Math::Pari::sqrtint($x);
273 139 50       1817 return $y * $y > $x ? $y - $one : $y; # bug in sqrtint()?
274             }
275              
276             sub _root {
277             # n'th root
278              
279             # Native version:
280 47     47   2477 return $_[1] = int(Math::Pari::sqrtn($_[1] + 0.5, $_[2]));
281             }
282              
283             sub _modpow {
284             # modulus of power ($x ** $y) % $z
285 144     144   1411 my ($c, $num, $exp, $mod) = @_;
286              
287             # in the trivial case,
288 144 100       612 if (gcmp1($mod)) {
289 50         163 $num = PARI(0);
290 50         143 return $num;
291             }
292              
293 94 100       342 if (gcmp1($num)) {
294 29         110 $num = PARI(1);
295 29         84 return $num;
296             }
297              
298 65 100       385 if (gcmp0($num)) {
299 12 100       97 if (gcmp0($exp)) {
300 3         20 return PARI(1);
301             } else {
302 9         51 return PARI(0);
303             }
304             }
305              
306 53         221 my $acc = $c -> _copy($num);
307 53         209 my $t = $c -> _one();
308              
309 53         234 my $expbin = $c -> _to_bin($exp);
310 53         157 my $len = length($expbin);
311 53         294 while (--$len >= 0) {
312 268 100       535 if (substr($expbin, $len, 1) eq '1') { # is_odd
313 141         319 $c -> _mul($t, $acc);
314 141         221 $t = $c -> _mod($t, $mod);
315             }
316 268         556 $c -> _mul($acc, $acc);
317 268         440 $acc = $c -> _mod($acc, $mod);
318             }
319 53         124 $num = $t;
320 53         226 $num;
321             }
322              
323             sub _rsft {
324             # (X, Y, N) = @_; means X >> Y in base N
325              
326 14692 100   14692   75596 if ($_[3] != 2) {
327 14677         159239 return $_[1] = gdivent($_[1], PARI($_[3]) ** $_[2]);
328             }
329 15         167 $_[1] >>= $_[2];
330             }
331              
332             sub _lsft {
333             # (X, Y, N) = @_; means X >> Y in base N
334              
335 9190 100   9190   51188 if ($_[3] != 2) {
336 9172         101519 return $_[1] *= PARI($_[3]) ** $_[2];
337             }
338 18         203 $_[1] <<= $_[2];
339             }
340              
341             sub _fac {
342             # factorial of argument
343 46     46   792 $_[1] = ifact($_[1]);
344             }
345              
346             # _set() - set an already existing object to the given scalar value
347              
348             sub _set {
349 0     0     my ($c, $x, $y) = @_;
350 0           *x = \PARI($y . '');
351             }
352              
353             1;
354              
355             __END__