SYN SYN
[p5sagit/p5-mst-13.2.git] / lib / Math / BigInt.pm
CommitLineData
a0d0e21e 1package Math::BigInt;
22d4bb9c 2$VERSION='0.01';
a0d0e21e 3
a5f75d66 4use overload
748a9306 5'+' => sub {new Math::BigInt &badd},
6'-' => sub {new Math::BigInt
a0d0e21e 7 $_[2]? bsub($_[1],${$_[0]}) : bsub(${$_[0]},$_[1])},
5d7098d5 8'<=>' => sub {$_[2]? bcmp($_[1],${$_[0]}) : bcmp(${$_[0]},$_[1])},
9'cmp' => sub {$_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
748a9306 10'*' => sub {new Math::BigInt &bmul},
11'/' => sub {new Math::BigInt
a0d0e21e 12 $_[2]? scalar bdiv($_[1],${$_[0]}) :
13 scalar bdiv(${$_[0]},$_[1])},
748a9306 14'%' => sub {new Math::BigInt
a0d0e21e 15 $_[2]? bmod($_[1],${$_[0]}) : bmod(${$_[0]},$_[1])},
748a9306 16'**' => sub {new Math::BigInt
a0d0e21e 17 $_[2]? bpow($_[1],${$_[0]}) : bpow(${$_[0]},$_[1])},
748a9306 18'neg' => sub {new Math::BigInt &bneg},
19'abs' => sub {new Math::BigInt &babs},
e16b8f49 20'<<' => sub {new Math::BigInt
21 $_[2]? blsft($_[1],${$_[0]}) : blsft(${$_[0]},$_[1])},
22'>>' => sub {new Math::BigInt
23 $_[2]? brsft($_[1],${$_[0]}) : brsft(${$_[0]},$_[1])},
24'&' => sub {new Math::BigInt &band},
25'|' => sub {new Math::BigInt &bior},
26'^' => sub {new Math::BigInt &bxor},
27'~' => sub {new Math::BigInt &bnot},
a0d0e21e 28
29qw(
30"" stringify
310+ numify) # Order of arguments unsignificant
a5f75d66 32;
a0d0e21e 33
748a9306 34$NaNOK=1;
35
a0d0e21e 36sub new {
a5f75d66 37 my($class) = shift;
38 my($foo) = bnorm(shift);
748a9306 39 die "Not a number initialized to Math::BigInt" if !$NaNOK && $foo eq "NaN";
a5f75d66 40 bless \$foo, $class;
a0d0e21e 41}
42sub stringify { "${$_[0]}" }
43sub numify { 0 + "${$_[0]}" } # Not needed, additional overhead
44 # comparing to direct compilation based on
45 # stringify
b3ac6de7 46sub import {
47 shift;
48 return unless @_;
49 die "unknown import: @_" unless @_ == 1 and $_[0] eq ':constant';
50 overload::constant integer => sub {Math::BigInt->new(shift)};
51}
a0d0e21e 52
a0d0e21e 53$zero = 0;
54
a5f75d66 55
a0d0e21e 56# normalize string form of number. Strip leading zeros. Strip any
57# white space and add a sign, if missing.
58# Strings that are not numbers result the value 'NaN'.
59
60sub bnorm { #(num_str) return num_str
61 local($_) = @_;
62 s/\s+//g; # strip white space
63 if (s/^([+-]?)0*(\d+)$/$1$2/) { # test if number
64 substr($_,$[,0) = '+' unless $1; # Add missing sign
65 s/^-0/+0/;
66 $_;
67 } else {
68 'NaN';
69 }
70}
71
72# Convert a number from string format to internal base 100000 format.
73# Assumes normalized value as input.
74sub internal { #(num_str) return int_num_array
75 local($d) = @_;
76 ($is,$il) = (substr($d,$[,1),length($d)-2);
77 substr($d,$[,1) = '';
78 ($is, reverse(unpack("a" . ($il%5+1) . ("a5" x ($il/5)), $d)));
79}
80
81# Convert a number from internal base 100000 format to string format.
82# This routine scribbles all over input array.
83sub external { #(int_num_array) return num_str
84 $es = shift;
85 grep($_ > 9999 || ($_ = substr('0000'.$_,-5)), @_); # zero pad
86 &bnorm(join('', $es, reverse(@_))); # reverse concat and normalize
87}
88
89# Negate input value.
90sub bneg { #(num_str) return num_str
91 local($_) = &bnorm(@_);
e3c7ef20 92 return $_ if $_ eq '+0' or $_ eq 'NaN';
93 vec($_,0,8) ^= ord('+') ^ ord('-');
a0d0e21e 94 $_;
95}
96
97# Returns the absolute value of the input.
98sub babs { #(num_str) return num_str
99 &abs(&bnorm(@_));
100}
101
102sub abs { # post-normalized abs for internal use
103 local($_) = @_;
104 s/^-/+/;
105 $_;
106}
a5f75d66 107
a0d0e21e 108# Compares 2 values. Returns one of undef, <0, =0, >0. (suitable for sort)
109sub bcmp { #(num_str, num_str) return cond_code
110 local($x,$y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
111 if ($x eq 'NaN') {
112 undef;
113 } elsif ($y eq 'NaN') {
114 undef;
115 } else {
e3c7ef20 116 &cmp($x,$y) <=> 0;
a0d0e21e 117 }
118}
119
120sub cmp { # post-normalized compare for internal use
121 local($cx, $cy) = @_;
1e2e1ae8 122
123 return 0 if ($cx eq $cy);
124
125 local($sx, $sy) = (substr($cx, 0, 1), substr($cy, 0, 1));
126 local($ld);
127
128 if ($sx eq '+') {
129 return 1 if ($sy eq '-' || $cy eq '+0');
130 $ld = length($cx) - length($cy);
131 return $ld if ($ld);
132 return $cx cmp $cy;
133 } else { # $sx eq '-'
134 return -1 if ($sy eq '+');
135 $ld = length($cy) - length($cx);
136 return $ld if ($ld);
137 return $cy cmp $cx;
138 }
a0d0e21e 139}
140
141sub badd { #(num_str, num_str) return num_str
142 local(*x, *y); ($x, $y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
143 if ($x eq 'NaN') {
144 'NaN';
145 } elsif ($y eq 'NaN') {
146 'NaN';
147 } else {
148 @x = &internal($x); # convert to internal form
149 @y = &internal($y);
150 local($sx, $sy) = (shift @x, shift @y); # get signs
151 if ($sx eq $sy) {
152 &external($sx, &add(*x, *y)); # if same sign add
153 } else {
154 ($x, $y) = (&abs($x),&abs($y)); # make abs
155 if (&cmp($y,$x) > 0) {
156 &external($sy, &sub(*y, *x));
157 } else {
158 &external($sx, &sub(*x, *y));
159 }
160 }
161 }
162}
163
164sub bsub { #(num_str, num_str) return num_str
165 &badd($_[$[],&bneg($_[$[+1]));
166}
167
168# GCD -- Euclids algorithm Knuth Vol 2 pg 296
169sub bgcd { #(num_str, num_str) return num_str
170 local($x,$y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
171 if ($x eq 'NaN' || $y eq 'NaN') {
172 'NaN';
173 } else {
174 ($x, $y) = ($y,&bmod($x,$y)) while $y ne '+0';
175 $x;
176 }
177}
a5f75d66 178
a0d0e21e 179# routine to add two base 1e5 numbers
180# stolen from Knuth Vol 2 Algorithm A pg 231
181# there are separate routines to add and sub as per Kunth pg 233
182sub add { #(int_num_array, int_num_array) return int_num_array
183 local(*x, *y) = @_;
184 $car = 0;
185 for $x (@x) {
186 last unless @y || $car;
20408e3c 187 $x -= 1e5 if $car = (($x += (@y ? shift(@y) : 0) + $car) >= 1e5) ? 1 : 0;
a0d0e21e 188 }
189 for $y (@y) {
190 last unless $car;
55497cff 191 $y -= 1e5 if $car = (($y += $car) >= 1e5) ? 1 : 0;
a0d0e21e 192 }
193 (@x, @y, $car);
194}
195
196# subtract base 1e5 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
197sub sub { #(int_num_array, int_num_array) return int_num_array
198 local(*sx, *sy) = @_;
199 $bar = 0;
200 for $sx (@sx) {
20408e3c 201 last unless @sy || $bar;
202 $sx += 1e5 if $bar = (($sx -= (@sy ? shift(@sy) : 0) + $bar) < 0);
a0d0e21e 203 }
204 @sx;
205}
206
207# multiply two numbers -- stolen from Knuth Vol 2 pg 233
208sub bmul { #(num_str, num_str) return num_str
209 local(*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
210 if ($x eq 'NaN') {
211 'NaN';
212 } elsif ($y eq 'NaN') {
213 'NaN';
214 } else {
215 @x = &internal($x);
216 @y = &internal($y);
217 &external(&mul(*x,*y));
218 }
219}
220
221# multiply two numbers in internal representation
222# destroys the arguments, supposes that two arguments are different
223sub mul { #(*int_num_array, *int_num_array) return int_num_array
224 local(*x, *y) = (shift, shift);
225 local($signr) = (shift @x ne shift @y) ? '-' : '+';
226 @prod = ();
227 for $x (@x) {
228 ($car, $cty) = (0, $[);
229 for $y (@y) {
8b6a6e55 230 $prod = $x * $y + ($prod[$cty] || 0) + $car;
a0d0e21e 231 $prod[$cty++] =
232 $prod - ($car = int($prod * 1e-5)) * 1e5;
233 }
234 $prod[$cty] += $car if $car;
235 $x = shift @prod;
236 }
237 ($signr, @x, @prod);
238}
239
240# modulus
241sub bmod { #(num_str, num_str) return num_str
242 (&bdiv(@_))[$[+1];
243}
a5f75d66 244
a0d0e21e 245sub bdiv { #(dividend: num_str, divisor: num_str) return num_str
246 local (*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
247 return wantarray ? ('NaN','NaN') : 'NaN'
248 if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0');
249 return wantarray ? ('+0',$x) : '+0' if (&cmp(&abs($x),&abs($y)) < 0);
250 @x = &internal($x); @y = &internal($y);
251 $srem = $y[$[];
252 $sr = (shift @x ne shift @y) ? '-' : '+';
253 $car = $bar = $prd = 0;
254 if (($dd = int(1e5/($y[$#y]+1))) != 1) {
255 for $x (@x) {
256 $x = $x * $dd + $car;
257 $x -= ($car = int($x * 1e-5)) * 1e5;
258 }
259 push(@x, $car); $car = 0;
260 for $y (@y) {
261 $y = $y * $dd + $car;
262 $y -= ($car = int($y * 1e-5)) * 1e5;
263 }
264 }
265 else {
266 push(@x, 0);
267 }
5d7098d5 268 @q = (); ($v2,$v1) = @y[-2,-1];
0a6a0d52 269 $v2 = 0 unless $v2;
a0d0e21e 270 while ($#x > $#y) {
5d7098d5 271 ($u2,$u1,$u0) = @x[-3..-1];
0a6a0d52 272 $u2 = 0 unless $u2;
a0d0e21e 273 $q = (($u0 == $v1) ? 99999 : int(($u0*1e5+$u1)/$v1));
274 --$q while ($v2*$q > ($u0*1e5+$u1-$q*$v1)*1e5+$u2);
275 if ($q) {
276 ($car, $bar) = (0,0);
277 for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) {
278 $prd = $q * $y[$y] + $car;
279 $prd -= ($car = int($prd * 1e-5)) * 1e5;
280 $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0));
281 }
282 if ($x[$#x] < $car + $bar) {
283 $car = 0; --$q;
284 for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) {
285 $x[$x] -= 1e5
286 if ($car = (($x[$x] += $y[$y] + $car) > 1e5));
287 }
288 }
289 }
290 pop(@x); unshift(@q, $q);
291 }
292 if (wantarray) {
293 @d = ();
294 if ($dd != 1) {
295 $car = 0;
296 for $x (reverse @x) {
297 $prd = $car * 1e5 + $x;
298 $car = $prd - ($tmp = int($prd / $dd)) * $dd;
299 unshift(@d, $tmp);
300 }
301 }
302 else {
303 @d = @x;
304 }
305 (&external($sr, @q), &external($srem, @d, $zero));
306 } else {
307 &external($sr, @q);
308 }
309}
310
311# compute power of two numbers -- stolen from Knuth Vol 2 pg 233
312sub bpow { #(num_str, num_str) return num_str
313 local(*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
314 if ($x eq 'NaN') {
315 'NaN';
316 } elsif ($y eq 'NaN') {
317 'NaN';
318 } elsif ($x eq '+1') {
319 '+1';
320 } elsif ($x eq '-1') {
321 &bmod($x,2) ? '-1': '+1';
322 } elsif ($y =~ /^-/) {
323 'NaN';
324 } elsif ($x eq '+0' && $y eq '+0') {
325 'NaN';
326 } else {
327 @x = &internal($x);
328 local(@pow2)=@x;
329 local(@pow)=&internal("+1");
330 local($y1,$res,@tmp1,@tmp2)=(1); # need tmp to send to mul
331 while ($y ne '+0') {
332 ($y,$res)=&bdiv($y,2);
333 if ($res ne '+0') {@tmp=@pow2; @pow=&mul(*pow,*tmp);}
334 if ($y ne '+0') {@tmp=@pow2;@pow2=&mul(*pow2,*tmp);}
335 }
336 &external(@pow);
337 }
338}
339
e16b8f49 340# compute x << y, y >= 0
341sub blsft { #(num_str, num_str) return num_str
342 &bmul($_[$[], &bpow(2, $_[$[+1]));
343}
344
345# compute x >> y, y >= 0
346sub brsft { #(num_str, num_str) return num_str
347 &bdiv($_[$[], &bpow(2, $_[$[+1]));
348}
349
350# compute x & y
351sub band { #(num_str, num_str) return num_str
352 local($x,$y,$r,$m,$xr,$yr) = (&bnorm($_[$[]),&bnorm($_[$[+1]),0,1);
353 if ($x eq 'NaN' || $y eq 'NaN') {
354 'NaN';
355 } else {
356 while ($x ne '+0' && $y ne '+0') {
357 ($x, $xr) = &bdiv($x, 0x10000);
358 ($y, $yr) = &bdiv($y, 0x10000);
359 $r = &badd(&bmul(int $xr & $yr, $m), $r);
360 $m = &bmul($m, 0x10000);
361 }
362 $r;
363 }
364}
365
366# compute x | y
367sub bior { #(num_str, num_str) return num_str
368 local($x,$y,$r,$m,$xr,$yr) = (&bnorm($_[$[]),&bnorm($_[$[+1]),0,1);
369 if ($x eq 'NaN' || $y eq 'NaN') {
370 'NaN';
371 } else {
372 while ($x ne '+0' || $y ne '+0') {
373 ($x, $xr) = &bdiv($x, 0x10000);
374 ($y, $yr) = &bdiv($y, 0x10000);
375 $r = &badd(&bmul(int $xr | $yr, $m), $r);
376 $m = &bmul($m, 0x10000);
377 }
378 $r;
379 }
380}
381
382# compute x ^ y
383sub bxor { #(num_str, num_str) return num_str
384 local($x,$y,$r,$m,$xr,$yr) = (&bnorm($_[$[]),&bnorm($_[$[+1]),0,1);
385 if ($x eq 'NaN' || $y eq 'NaN') {
386 'NaN';
387 } else {
388 while ($x ne '+0' || $y ne '+0') {
389 ($x, $xr) = &bdiv($x, 0x10000);
390 ($y, $yr) = &bdiv($y, 0x10000);
391 $r = &badd(&bmul(int $xr ^ $yr, $m), $r);
392 $m = &bmul($m, 0x10000);
393 }
394 $r;
395 }
396}
397
398# represent ~x as twos-complement number
399sub bnot { #(num_str) return num_str
400 &bsub(-1,$_[$[]);
401}
402
a0d0e21e 4031;
a5f75d66 404__END__
405
406=head1 NAME
407
408Math::BigInt - Arbitrary size integer math package
409
410=head1 SYNOPSIS
411
412 use Math::BigInt;
413 $i = Math::BigInt->new($string);
414
415 $i->bneg return BINT negation
416 $i->babs return BINT absolute value
417 $i->bcmp(BINT) return CODE compare numbers (undef,<0,=0,>0)
418 $i->badd(BINT) return BINT addition
419 $i->bsub(BINT) return BINT subtraction
420 $i->bmul(BINT) return BINT multiplication
421 $i->bdiv(BINT) return (BINT,BINT) division (quo,rem) just quo if scalar
422 $i->bmod(BINT) return BINT modulus
423 $i->bgcd(BINT) return BINT greatest common divisor
424 $i->bnorm return BINT normalization
e16b8f49 425 $i->blsft(BINT) return BINT left shift
426 $i->brsft(BINT) return (BINT,BINT) right shift (quo,rem) just quo if scalar
427 $i->band(BINT) return BINT bit-wise and
428 $i->bior(BINT) return BINT bit-wise inclusive or
429 $i->bxor(BINT) return BINT bit-wise exclusive or
430 $i->bnot return BINT bit-wise not
a5f75d66 431
432=head1 DESCRIPTION
433
434All basic math operations are overloaded if you declare your big
435integers as
436
437 $i = new Math::BigInt '123 456 789 123 456 789';
438
439
440=over 2
441
442=item Canonical notation
443
444Big integer value are strings of the form C</^[+-]\d+$/> with leading
445zeros suppressed.
446
447=item Input
448
449Input values to these routines may be strings of the form
450C</^\s*[+-]?[\d\s]+$/>.
451
452=item Output
453
454Output values always always in canonical form
455
456=back
457
458Actual math is done in an internal format consisting of an array
459whose first element is the sign (/^[+-]$/) and whose remaining
460elements are base 100000 digits with the least significant digit first.
461The string 'NaN' is used to represent the result when input arguments
462are not numbers, as well as the result of dividing by zero.
463
464=head1 EXAMPLES
465
466 '+0' canonical zero value
467 ' -123 123 123' canonical value '-123123123'
468 '1 23 456 7890' canonical value '+1234567890'
469
470
b3ac6de7 471=head1 Autocreating constants
472
473After C<use Math::BigInt ':constant'> all the integer decimal constants
e3c7ef20 474in the given scope are converted to C<Math::BigInt>. This conversion
b3ac6de7 475happens at compile time.
476
477In particular
478
479 perl -MMath::BigInt=:constant -e 'print 2**100'
480
8dcee03e 481print the integer value of C<2**100>. Note that without conversion of
482constants the expression 2**100 will be calculated as floating point number.
b3ac6de7 483
a5f75d66 484=head1 BUGS
485
486The current version of this module is a preliminary version of the
487real thing that is currently (as of perl5.002) under development.
488
489=head1 AUTHOR
490
491Mark Biggar, overloaded interface by Ilya Zakharevich.
492
493=cut