X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2Fbigint.pl;h=bd1d91f82293343aab7f112bfcddef0cff253344;hb=2d3232d7d747c33b17a2f963786d0f00484dbad2;hp=503c7837c2b262cc01a0e67f6414e53d4479d75e;hpb=5303340c1eb77f5b18e12347ed4a7fa2eb6cd9f7;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/bigint.pl b/lib/bigint.pl index 503c783..bd1d91f 100644 --- a/lib/bigint.pl +++ b/lib/bigint.pl @@ -1,5 +1,13 @@ package bigint; - +# +# This library is no longer being maintained, and is included for backward +# compatibility with Perl 4 programs which may require it. +# +# In particular, this should not be used as an example of modern Perl +# programming techniques. +# +# Suggested alternative: Math::BigInt +# # arbitrary size integer math package # # by Mark Biggar @@ -12,7 +20,7 @@ package bigint; # '+0' canonical zero value # ' -123 123 123' canonical value '-123123123' # '1 23 456 7890' canonical value '+1234567890' -# Output values always always in canonical form +# Output values always in canonical form # # Actual math is done in an internal format consisting of an array # whose first element is the sign (/^[+-]$/) and whose remaining @@ -33,15 +41,25 @@ package bigint; # bgcd(BINT,BINT) return BINT greatest common divisor # bnorm(BINT) return BINT normalization # + +# overcome a floating point problem on certain osnames (posix-bc, os390) +BEGIN { + my $x = 100000.0; + my $use_mult = int($x*1e-5)*1e5 == $x ? 1 : 0; +} + +$zero = 0; + # normalize string form of number. Strip leading zeros. Strip any # white space and add a sign, if missing. # Strings that are not numbers result the value 'NaN'. + sub main'bnorm { #(num_str) return num_str local($_) = @_; s/\s+//g; # strip white space if (s/^([+-]?)0*(\d+)$/$1$2/) { # test if number - substr($_,0,0) = '+' unless $1; # Add missing sign + substr($_,$[,0) = '+' unless $1; # Add missing sign s/^-0/+0/; $_; } else { @@ -53,8 +71,8 @@ sub main'bnorm { #(num_str) return num_str # Assumes normalized value as input. sub internal { #(num_str) return int_num_array local($d) = @_; - ($is,$il) = (substr($d,0,1),length($d)-2); - substr($d,0,1) = ''; + ($is,$il) = (substr($d,$[,1),length($d)-2); + substr($d,$[,1) = ''; ($is, reverse(unpack("a" . ($il%5+1) . ("a5" x ($il/5)), $d))); } @@ -70,7 +88,7 @@ sub external { #(int_num_array) return num_str sub main'bneg { #(num_str) return num_str local($_) = &'bnorm(@_); vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0'; - s/^H/N/; + s/^./N/ unless /^[-+]/; # works both in ASCII and EBCDIC $_; } @@ -87,7 +105,7 @@ sub abs { # post-normalized abs for internal use # Compares 2 values. Returns one of undef, <0, =0, >0. (suitable for sort) sub main'bcmp { #(num_str, num_str) return cond_code - local($x,$y) = (&'bnorm($_[0]),&'bnorm($_[1])); + local($x,$y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); if ($x eq 'NaN') { undef; } elsif ($y eq 'NaN') { @@ -99,17 +117,27 @@ sub main'bcmp { #(num_str, num_str) return cond_code sub cmp { # post-normalized compare for internal use local($cx, $cy) = @_; - $cx cmp $cy - && - ( - ord($cy) <=> ord($cx) - || - ($cx cmp ',') * (length($cy) <=> length($cx) || $cy cmp $cx) - ); + return 0 if ($cx eq $cy); + + local($sx, $sy) = (substr($cx, 0, 1), substr($cy, 0, 1)); + local($ld); + + if ($sx eq '+') { + return 1 if ($sy eq '-' || $cy eq '+0'); + $ld = length($cx) - length($cy); + return $ld if ($ld); + return $cx cmp $cy; + } else { # $sx eq '-' + return -1 if ($sy eq '+'); + $ld = length($cy) - length($cx); + return $ld if ($ld); + return $cy cmp $cx; + } + } sub main'badd { #(num_str, num_str) return num_str - local(*x, *y); ($x, $y) = (&'bnorm($_[0]),&'bnorm($_[1])); + local(*x, *y); ($x, $y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); if ($x eq 'NaN') { 'NaN'; } elsif ($y eq 'NaN') { @@ -132,25 +160,21 @@ sub main'badd { #(num_str, num_str) return num_str } sub main'bsub { #(num_str, num_str) return num_str - &'badd($_[0],&'bneg($_[1])); + &'badd($_[$[],&'bneg($_[$[+1])); } # GCD -- Euclids algorithm Knuth Vol 2 pg 296 sub main'bgcd { #(num_str, num_str) return num_str - local($x,$y) = (&'bnorm($_[0]),&'bnorm($_[1])); - if ($x eq 'NaN') { + local($x,$y) = (&'bnorm($_[$[]),&'bnorm($_[$[+1])); + if ($x eq 'NaN' || $y eq 'NaN') { 'NaN'; - } - elsif ($y eq 'NaN') { - 'NaN'; - } - else { + } else { ($x, $y) = ($y,&'bmod($x,$y)) while $y ne '+0'; $x; } } -# routine to add two base 100000 numbers +# routine to add two base 1e5 numbers # stolen from Knuth Vol 2 Algorithm A pg 231 # there are separate routines to add and sub as per Kunth pg 233 sub add { #(int_num_array, int_num_array) return int_num_array @@ -158,29 +182,29 @@ sub add { #(int_num_array, int_num_array) return int_num_array $car = 0; for $x (@x) { last unless @y || $car; - $x -= 100000 if $car = (($x += shift @y + $car) >= 100000); + $x -= 1e5 if $car = (($x += shift(@y) + $car) >= 1e5) ? 1 : 0; } for $y (@y) { last unless $car; - $y -= 100000 if $car = (($y += $car) >= 100000); + $y -= 1e5 if $car = (($y += $car) >= 1e5) ? 1 : 0; } (@x, @y, $car); } -# subtract base 100000 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y +# subtract base 1e5 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y sub sub { #(int_num_array, int_num_array) return int_num_array local(*sx, *sy) = @_; $bar = 0; for $sx (@sx) { last unless @y || $bar; - $sx += 100000 if $bar = (($sx -= shift @sy + $bar) < 0); + $sx += 1e5 if $bar = (($sx -= shift(@sy) + $bar) < 0); } @sx; } # multiply two numbers -- stolen from Knuth Vol 2 pg 233 sub main'bmul { #(num_str, num_str) return num_str - local(*x, *y); ($x, $y) = (&'bnorm($_[0]), &'bnorm($_[1])); + local(*x, *y); ($x, $y) = (&'bnorm($_[$[]), &'bnorm($_[$[+1])); if ($x eq 'NaN') { 'NaN'; } elsif ($y eq 'NaN') { @@ -191,11 +215,17 @@ sub main'bmul { #(num_str, num_str) return num_str local($signr) = (shift @x ne shift @y) ? '-' : '+'; @prod = (); for $x (@x) { - ($car, $cty) = (0, 0); + ($car, $cty) = (0, $[); for $y (@y) { $prod = $x * $y + $prod[$cty] + $car; - $prod[$cty++] = - $prod - ($car = int($prod * (1/100000))) * 100000; + if ($use_mult) { + $prod[$cty++] = + $prod - ($car = int($prod * 1e-5)) * 1e5; + } + else { + $prod[$cty++] = + $prod - ($car = int($prod / 1e5)) * 1e5; + } } $prod[$cty] += $car if $car; $x = shift @prod; @@ -206,49 +236,64 @@ sub main'bmul { #(num_str, num_str) return num_str # modulus sub main'bmod { #(num_str, num_str) return num_str - (&'bdiv(@_))[1]; + (&'bdiv(@_))[$[+1]; } sub main'bdiv { #(dividend: num_str, divisor: num_str) return num_str - local (*x, *y); ($x, $y) = (&'bnorm($_[0]), &'bnorm($_[1])); + local (*x, *y); ($x, $y) = (&'bnorm($_[$[]), &'bnorm($_[$[+1])); return wantarray ? ('NaN','NaN') : 'NaN' if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0'); return wantarray ? ('+0',$x) : '+0' if (&cmp(&abs($x),&abs($y)) < 0); @x = &internal($x); @y = &internal($y); - $srem = $y[0]; + $srem = $y[$[]; $sr = (shift @x ne shift @y) ? '-' : '+'; $car = $bar = $prd = 0; - if (($dd = int(100000/($y[$#y]+1))) != 1) { + if (($dd = int(1e5/($y[$#y]+1))) != 1) { for $x (@x) { $x = $x * $dd + $car; - $x -= ($car = int($x * (1/100000))) * 100000; + if ($use_mult) { + $x -= ($car = int($x * 1e-5)) * 1e5; + } + else { + $x -= ($car = int($x / 1e5)) * 1e5; + } } push(@x, $car); $car = 0; for $y (@y) { $y = $y * $dd + $car; - $y -= ($car = int($y * (1/100000))) * 100000; + if ($use_mult) { + $y -= ($car = int($y * 1e-5)) * 1e5; + } + else { + $y -= ($car = int($y / 1e5)) * 1e5; + } } } else { push(@x, 0); } - @q = (); ($v2,$v1) = @y[$#y-1,$#y]; + @q = (); ($v2,$v1) = @y[-2,-1]; while ($#x > $#y) { - ($u2,$u1,$u0) = @x[($#x-2)..$#x]; - $q = (($u0 == $v1) ? 99999 : int(($u0*100000+$u1)/$v1)); - --$q while ($v2*$q > ($u0*100000+$u1-$q*$v1)*100000+$u2); + ($u2,$u1,$u0) = @x[-3..-1]; + $q = (($u0 == $v1) ? 99999 : int(($u0*1e5+$u1)/$v1)); + --$q while ($v2*$q > ($u0*1e5+$u1-$q*$v1)*1e5+$u2); if ($q) { ($car, $bar) = (0,0); - for ($y = 0, $x = $#x-$#y-1; $y <= $#y; ++$y,++$x) { + for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) { $prd = $q * $y[$y] + $car; - $prd -= ($car = int($prd * (1/100000))) * 100000; - $x[$x] += 100000 if ($bar = (($x[$x] -= $prd + $bar) < 0)); + if ($use_mult) { + $prd -= ($car = int($prd * 1e-5)) * 1e5; + } + else { + $prd -= ($car = int($prd / 1e5)) * 1e5; + } + $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0)); } if ($x[$#x] < $car + $bar) { $car = 0; --$q; - for ($y = 0, $x = $#x-$#y-1; $y <= $#y; ++$y,++$x) { - $x[$x] -= 100000 - if ($car = (($x[$x] += $y[$y] + $car) > 100000)); + for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) { + $x[$x] -= 1e5 + if ($car = (($x[$x] += $y[$y] + $car) > 1e5)); } } } @@ -259,7 +304,7 @@ sub main'bdiv { #(dividend: num_str, divisor: num_str) return num_str if ($dd != 1) { $car = 0; for $x (reverse @x) { - $prd = $car * 100000 + $x; + $prd = $car * 1e5 + $x; $car = $prd - ($tmp = int($prd / $dd)) * $dd; unshift(@d, $tmp); } @@ -267,7 +312,7 @@ sub main'bdiv { #(dividend: num_str, divisor: num_str) return num_str else { @d = @x; } - (&external($sr, @q), &external($srem, @d, 0)); + (&external($sr, @q), &external($srem, @d, $zero)); } else { &external($sr, @q); }