1*0Sstevel@tonic-gatepackage bigint; 2*0Sstevel@tonic-gate# 3*0Sstevel@tonic-gate# This library is no longer being maintained, and is included for backward 4*0Sstevel@tonic-gate# compatibility with Perl 4 programs which may require it. 5*0Sstevel@tonic-gate# 6*0Sstevel@tonic-gate# In particular, this should not be used as an example of modern Perl 7*0Sstevel@tonic-gate# programming techniques. 8*0Sstevel@tonic-gate# 9*0Sstevel@tonic-gate# Suggested alternative: Math::BigInt 10*0Sstevel@tonic-gate# 11*0Sstevel@tonic-gate# arbitrary size integer math package 12*0Sstevel@tonic-gate# 13*0Sstevel@tonic-gate# by Mark Biggar 14*0Sstevel@tonic-gate# 15*0Sstevel@tonic-gate# Canonical Big integer value are strings of the form 16*0Sstevel@tonic-gate# /^[+-]\d+$/ with leading zeros suppressed 17*0Sstevel@tonic-gate# Input values to these routines may be strings of the form 18*0Sstevel@tonic-gate# /^\s*[+-]?[\d\s]+$/. 19*0Sstevel@tonic-gate# Examples: 20*0Sstevel@tonic-gate# '+0' canonical zero value 21*0Sstevel@tonic-gate# ' -123 123 123' canonical value '-123123123' 22*0Sstevel@tonic-gate# '1 23 456 7890' canonical value '+1234567890' 23*0Sstevel@tonic-gate# Output values always in canonical form 24*0Sstevel@tonic-gate# 25*0Sstevel@tonic-gate# Actual math is done in an internal format consisting of an array 26*0Sstevel@tonic-gate# whose first element is the sign (/^[+-]$/) and whose remaining 27*0Sstevel@tonic-gate# elements are base 100000 digits with the least significant digit first. 28*0Sstevel@tonic-gate# The string 'NaN' is used to represent the result when input arguments 29*0Sstevel@tonic-gate# are not numbers, as well as the result of dividing by zero 30*0Sstevel@tonic-gate# 31*0Sstevel@tonic-gate# routines provided are: 32*0Sstevel@tonic-gate# 33*0Sstevel@tonic-gate# bneg(BINT) return BINT negation 34*0Sstevel@tonic-gate# babs(BINT) return BINT absolute value 35*0Sstevel@tonic-gate# bcmp(BINT,BINT) return CODE compare numbers (undef,<0,=0,>0) 36*0Sstevel@tonic-gate# badd(BINT,BINT) return BINT addition 37*0Sstevel@tonic-gate# bsub(BINT,BINT) return BINT subtraction 38*0Sstevel@tonic-gate# bmul(BINT,BINT) return BINT multiplication 39*0Sstevel@tonic-gate# bdiv(BINT,BINT) return (BINT,BINT) division (quo,rem) just quo if scalar 40*0Sstevel@tonic-gate# bmod(BINT,BINT) return BINT modulus 41*0Sstevel@tonic-gate# bgcd(BINT,BINT) return BINT greatest common divisor 42*0Sstevel@tonic-gate# bnorm(BINT) return BINT normalization 43*0Sstevel@tonic-gate# 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate# overcome a floating point problem on certain osnames (posix-bc, os390) 46*0Sstevel@tonic-gateBEGIN { 47*0Sstevel@tonic-gate my $x = 100000.0; 48*0Sstevel@tonic-gate my $use_mult = int($x*1e-5)*1e5 == $x ? 1 : 0; 49*0Sstevel@tonic-gate} 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate$zero = 0; 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gate# normalize string form of number. Strip leading zeros. Strip any 55*0Sstevel@tonic-gate# white space and add a sign, if missing. 56*0Sstevel@tonic-gate# Strings that are not numbers result the value 'NaN'. 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gatesub main'bnorm { #(num_str) return num_str 59*0Sstevel@tonic-gate local($_) = @_; 60*0Sstevel@tonic-gate s/\s+//g; # strip white space 61*0Sstevel@tonic-gate if (s/^([+-]?)0*(\d+)$/$1$2/) { # test if number 62*0Sstevel@tonic-gate substr($_,$[,0) = '+' unless $1; # Add missing sign 63*0Sstevel@tonic-gate s/^-0/+0/; 64*0Sstevel@tonic-gate $_; 65*0Sstevel@tonic-gate } else { 66*0Sstevel@tonic-gate 'NaN'; 67*0Sstevel@tonic-gate } 68*0Sstevel@tonic-gate} 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate# Convert a number from string format to internal base 100000 format. 71*0Sstevel@tonic-gate# Assumes normalized value as input. 72*0Sstevel@tonic-gatesub internal { #(num_str) return int_num_array 73*0Sstevel@tonic-gate local($d) = @_; 74*0Sstevel@tonic-gate ($is,$il) = (substr($d,$[,1),length($d)-2); 75*0Sstevel@tonic-gate substr($d,$[,1) = ''; 76*0Sstevel@tonic-gate ($is, reverse(unpack("a" . ($il%5+1) . ("a5" x ($il/5)), $d))); 77*0Sstevel@tonic-gate} 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate# Convert a number from internal base 100000 format to string format. 80*0Sstevel@tonic-gate# This routine scribbles all over input array. 81*0Sstevel@tonic-gatesub external { #(int_num_array) return num_str 82*0Sstevel@tonic-gate $es = shift; 83*0Sstevel@tonic-gate grep($_ > 9999 || ($_ = substr('0000'.$_,-5)), @_); # zero pad 84*0Sstevel@tonic-gate &'bnorm(join('', $es, reverse(@_))); # reverse concat and normalize 85*0Sstevel@tonic-gate} 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate# Negate input value. 88*0Sstevel@tonic-gatesub main'bneg { #(num_str) return num_str 89*0Sstevel@tonic-gate local($_) = &'bnorm(@_); 90*0Sstevel@tonic-gate vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0'; 91*0Sstevel@tonic-gate s/^./N/ unless /^[-+]/; # works both in ASCII and EBCDIC 92*0Sstevel@tonic-gate $_; 93*0Sstevel@tonic-gate} 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate# Returns the absolute value of the input. 96*0Sstevel@tonic-gatesub main'babs { #(num_str) return num_str 97*0Sstevel@tonic-gate &abs(&'bnorm(@_)); 98*0Sstevel@tonic-gate} 99*0Sstevel@tonic-gate 100*0Sstevel@tonic-gatesub abs { # post-normalized abs for internal use 101*0Sstevel@tonic-gate local($_) = @_; 102*0Sstevel@tonic-gate s/^-/+/; 103*0Sstevel@tonic-gate $_; 104*0Sstevel@tonic-gate} 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gate# Compares 2 values. Returns one of undef, <0, =0, >0. (suitable for sort) 107*0Sstevel@tonic-gatesub main'bcmp { #(num_str, num_str) return cond_code 108*0Sstevel@tonic-gate local($x,$y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); 109*0Sstevel@tonic-gate if ($x eq 'NaN') { 110*0Sstevel@tonic-gate undef; 111*0Sstevel@tonic-gate } elsif ($y eq 'NaN') { 112*0Sstevel@tonic-gate undef; 113*0Sstevel@tonic-gate } else { 114*0Sstevel@tonic-gate &cmp($x,$y); 115*0Sstevel@tonic-gate } 116*0Sstevel@tonic-gate} 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gatesub cmp { # post-normalized compare for internal use 119*0Sstevel@tonic-gate local($cx, $cy) = @_; 120*0Sstevel@tonic-gate return 0 if ($cx eq $cy); 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate local($sx, $sy) = (substr($cx, 0, 1), substr($cy, 0, 1)); 123*0Sstevel@tonic-gate local($ld); 124*0Sstevel@tonic-gate 125*0Sstevel@tonic-gate if ($sx eq '+') { 126*0Sstevel@tonic-gate return 1 if ($sy eq '-' || $cy eq '+0'); 127*0Sstevel@tonic-gate $ld = length($cx) - length($cy); 128*0Sstevel@tonic-gate return $ld if ($ld); 129*0Sstevel@tonic-gate return $cx cmp $cy; 130*0Sstevel@tonic-gate } else { # $sx eq '-' 131*0Sstevel@tonic-gate return -1 if ($sy eq '+'); 132*0Sstevel@tonic-gate $ld = length($cy) - length($cx); 133*0Sstevel@tonic-gate return $ld if ($ld); 134*0Sstevel@tonic-gate return $cy cmp $cx; 135*0Sstevel@tonic-gate } 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate} 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gatesub main'badd { #(num_str, num_str) return num_str 140*0Sstevel@tonic-gate local(*x, *y); ($x, $y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); 141*0Sstevel@tonic-gate if ($x eq 'NaN') { 142*0Sstevel@tonic-gate 'NaN'; 143*0Sstevel@tonic-gate } elsif ($y eq 'NaN') { 144*0Sstevel@tonic-gate 'NaN'; 145*0Sstevel@tonic-gate } else { 146*0Sstevel@tonic-gate @x = &internal($x); # convert to internal form 147*0Sstevel@tonic-gate @y = &internal($y); 148*0Sstevel@tonic-gate local($sx, $sy) = (shift @x, shift @y); # get signs 149*0Sstevel@tonic-gate if ($sx eq $sy) { 150*0Sstevel@tonic-gate &external($sx, &add(*x, *y)); # if same sign add 151*0Sstevel@tonic-gate } else { 152*0Sstevel@tonic-gate ($x, $y) = (&abs($x),&abs($y)); # make abs 153*0Sstevel@tonic-gate if (&cmp($y,$x) > 0) { 154*0Sstevel@tonic-gate &external($sy, &sub(*y, *x)); 155*0Sstevel@tonic-gate } else { 156*0Sstevel@tonic-gate &external($sx, &sub(*x, *y)); 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate } 159*0Sstevel@tonic-gate } 160*0Sstevel@tonic-gate} 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gatesub main'bsub { #(num_str, num_str) return num_str 163*0Sstevel@tonic-gate &'badd($_[$[],&'bneg($_[$[+1])); 164*0Sstevel@tonic-gate} 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate# GCD -- Euclids algorithm Knuth Vol 2 pg 296 167*0Sstevel@tonic-gatesub main'bgcd { #(num_str, num_str) return num_str 168*0Sstevel@tonic-gate local($x,$y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); 169*0Sstevel@tonic-gate if ($x eq 'NaN' || $y eq 'NaN') { 170*0Sstevel@tonic-gate 'NaN'; 171*0Sstevel@tonic-gate } else { 172*0Sstevel@tonic-gate ($x, $y) = ($y,&'bmod($x,$y)) while $y ne '+0'; 173*0Sstevel@tonic-gate $x; 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate} 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate# routine to add two base 1e5 numbers 178*0Sstevel@tonic-gate# stolen from Knuth Vol 2 Algorithm A pg 231 179*0Sstevel@tonic-gate# there are separate routines to add and sub as per Kunth pg 233 180*0Sstevel@tonic-gatesub add { #(int_num_array, int_num_array) return int_num_array 181*0Sstevel@tonic-gate local(*x, *y) = @_; 182*0Sstevel@tonic-gate $car = 0; 183*0Sstevel@tonic-gate for $x (@x) { 184*0Sstevel@tonic-gate last unless @y || $car; 185*0Sstevel@tonic-gate $x -= 1e5 if $car = (($x += shift(@y) + $car) >= 1e5) ? 1 : 0; 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate for $y (@y) { 188*0Sstevel@tonic-gate last unless $car; 189*0Sstevel@tonic-gate $y -= 1e5 if $car = (($y += $car) >= 1e5) ? 1 : 0; 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate (@x, @y, $car); 192*0Sstevel@tonic-gate} 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate# subtract base 1e5 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y 195*0Sstevel@tonic-gatesub sub { #(int_num_array, int_num_array) return int_num_array 196*0Sstevel@tonic-gate local(*sx, *sy) = @_; 197*0Sstevel@tonic-gate $bar = 0; 198*0Sstevel@tonic-gate for $sx (@sx) { 199*0Sstevel@tonic-gate last unless @y || $bar; 200*0Sstevel@tonic-gate $sx += 1e5 if $bar = (($sx -= shift(@sy) + $bar) < 0); 201*0Sstevel@tonic-gate } 202*0Sstevel@tonic-gate @sx; 203*0Sstevel@tonic-gate} 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate# multiply two numbers -- stolen from Knuth Vol 2 pg 233 206*0Sstevel@tonic-gatesub main'bmul { #(num_str, num_str) return num_str 207*0Sstevel@tonic-gate local(*x, *y); ($x, $y) = (&'bnorm($_[$[]), &'bnorm($_[$[+1])); 208*0Sstevel@tonic-gate if ($x eq 'NaN') { 209*0Sstevel@tonic-gate 'NaN'; 210*0Sstevel@tonic-gate } elsif ($y eq 'NaN') { 211*0Sstevel@tonic-gate 'NaN'; 212*0Sstevel@tonic-gate } else { 213*0Sstevel@tonic-gate @x = &internal($x); 214*0Sstevel@tonic-gate @y = &internal($y); 215*0Sstevel@tonic-gate local($signr) = (shift @x ne shift @y) ? '-' : '+'; 216*0Sstevel@tonic-gate @prod = (); 217*0Sstevel@tonic-gate for $x (@x) { 218*0Sstevel@tonic-gate ($car, $cty) = (0, $[); 219*0Sstevel@tonic-gate for $y (@y) { 220*0Sstevel@tonic-gate $prod = $x * $y + $prod[$cty] + $car; 221*0Sstevel@tonic-gate if ($use_mult) { 222*0Sstevel@tonic-gate $prod[$cty++] = 223*0Sstevel@tonic-gate $prod - ($car = int($prod * 1e-5)) * 1e5; 224*0Sstevel@tonic-gate } 225*0Sstevel@tonic-gate else { 226*0Sstevel@tonic-gate $prod[$cty++] = 227*0Sstevel@tonic-gate $prod - ($car = int($prod / 1e5)) * 1e5; 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate } 230*0Sstevel@tonic-gate $prod[$cty] += $car if $car; 231*0Sstevel@tonic-gate $x = shift @prod; 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate &external($signr, @x, @prod); 234*0Sstevel@tonic-gate } 235*0Sstevel@tonic-gate} 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate# modulus 238*0Sstevel@tonic-gatesub main'bmod { #(num_str, num_str) return num_str 239*0Sstevel@tonic-gate (&'bdiv(@_))[$[+1]; 240*0Sstevel@tonic-gate} 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gatesub main'bdiv { #(dividend: num_str, divisor: num_str) return num_str 243*0Sstevel@tonic-gate local (*x, *y); ($x, $y) = (&'bnorm($_[$[]), &'bnorm($_[$[+1])); 244*0Sstevel@tonic-gate return wantarray ? ('NaN','NaN') : 'NaN' 245*0Sstevel@tonic-gate if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0'); 246*0Sstevel@tonic-gate return wantarray ? ('+0',$x) : '+0' if (&cmp(&abs($x),&abs($y)) < 0); 247*0Sstevel@tonic-gate @x = &internal($x); @y = &internal($y); 248*0Sstevel@tonic-gate $srem = $y[$[]; 249*0Sstevel@tonic-gate $sr = (shift @x ne shift @y) ? '-' : '+'; 250*0Sstevel@tonic-gate $car = $bar = $prd = 0; 251*0Sstevel@tonic-gate if (($dd = int(1e5/($y[$#y]+1))) != 1) { 252*0Sstevel@tonic-gate for $x (@x) { 253*0Sstevel@tonic-gate $x = $x * $dd + $car; 254*0Sstevel@tonic-gate if ($use_mult) { 255*0Sstevel@tonic-gate $x -= ($car = int($x * 1e-5)) * 1e5; 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate else { 258*0Sstevel@tonic-gate $x -= ($car = int($x / 1e5)) * 1e5; 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate } 261*0Sstevel@tonic-gate push(@x, $car); $car = 0; 262*0Sstevel@tonic-gate for $y (@y) { 263*0Sstevel@tonic-gate $y = $y * $dd + $car; 264*0Sstevel@tonic-gate if ($use_mult) { 265*0Sstevel@tonic-gate $y -= ($car = int($y * 1e-5)) * 1e5; 266*0Sstevel@tonic-gate } 267*0Sstevel@tonic-gate else { 268*0Sstevel@tonic-gate $y -= ($car = int($y / 1e5)) * 1e5; 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate else { 273*0Sstevel@tonic-gate push(@x, 0); 274*0Sstevel@tonic-gate } 275*0Sstevel@tonic-gate @q = (); ($v2,$v1) = @y[-2,-1]; 276*0Sstevel@tonic-gate while ($#x > $#y) { 277*0Sstevel@tonic-gate ($u2,$u1,$u0) = @x[-3..-1]; 278*0Sstevel@tonic-gate $q = (($u0 == $v1) ? 99999 : int(($u0*1e5+$u1)/$v1)); 279*0Sstevel@tonic-gate --$q while ($v2*$q > ($u0*1e5+$u1-$q*$v1)*1e5+$u2); 280*0Sstevel@tonic-gate if ($q) { 281*0Sstevel@tonic-gate ($car, $bar) = (0,0); 282*0Sstevel@tonic-gate for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) { 283*0Sstevel@tonic-gate $prd = $q * $y[$y] + $car; 284*0Sstevel@tonic-gate if ($use_mult) { 285*0Sstevel@tonic-gate $prd -= ($car = int($prd * 1e-5)) * 1e5; 286*0Sstevel@tonic-gate } 287*0Sstevel@tonic-gate else { 288*0Sstevel@tonic-gate $prd -= ($car = int($prd / 1e5)) * 1e5; 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0)); 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate if ($x[$#x] < $car + $bar) { 293*0Sstevel@tonic-gate $car = 0; --$q; 294*0Sstevel@tonic-gate for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) { 295*0Sstevel@tonic-gate $x[$x] -= 1e5 296*0Sstevel@tonic-gate if ($car = (($x[$x] += $y[$y] + $car) > 1e5)); 297*0Sstevel@tonic-gate } 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate } 300*0Sstevel@tonic-gate pop(@x); unshift(@q, $q); 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate if (wantarray) { 303*0Sstevel@tonic-gate @d = (); 304*0Sstevel@tonic-gate if ($dd != 1) { 305*0Sstevel@tonic-gate $car = 0; 306*0Sstevel@tonic-gate for $x (reverse @x) { 307*0Sstevel@tonic-gate $prd = $car * 1e5 + $x; 308*0Sstevel@tonic-gate $car = $prd - ($tmp = int($prd / $dd)) * $dd; 309*0Sstevel@tonic-gate unshift(@d, $tmp); 310*0Sstevel@tonic-gate } 311*0Sstevel@tonic-gate } 312*0Sstevel@tonic-gate else { 313*0Sstevel@tonic-gate @d = @x; 314*0Sstevel@tonic-gate } 315*0Sstevel@tonic-gate (&external($sr, @q), &external($srem, @d, $zero)); 316*0Sstevel@tonic-gate } else { 317*0Sstevel@tonic-gate &external($sr, @q); 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate} 320*0Sstevel@tonic-gate1; 321