From: Jarkko Hietaniemi Date: Wed, 3 Sep 2003 20:27:56 +0000 (+0000) Subject: Upgrade to Math::BigInt pre-rel 1.66 as of X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=aef458a0891c6e9b68a63c1a2c34e99bc6e508d8;p=p5sagit%2Fp5-mst-13.2.git Upgrade to Math::BigInt pre-rel 1.66 as of http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2003-09/msg00242.html (the tar.gz link doesn't have 'v1.66', it has '1.66') so that the smoke builds can start chewing it. p4raw-id: //depot/perl@21025 --- diff --git a/lib/Math/BigInt.pm b/lib/Math/BigInt.pm index ad3e12a..6c1c36d 100644 --- a/lib/Math/BigInt.pm +++ b/lib/Math/BigInt.pm @@ -18,7 +18,7 @@ package Math::BigInt; my $class = "Math::BigInt"; require 5.005; -$VERSION = '1.65'; +$VERSION = '1.66'; use Exporter; @ISA = qw( Exporter ); @EXPORT_OK = qw( objectify _swap bgcd blcm); @@ -1751,7 +1751,7 @@ sub bpow # (BINT or num_str, BINT or num_str) return BINT # compute power of two numbers -- stolen from Knuth Vol 2 pg 233 # modifies first argument - + # set up parameters my ($self,$x,$y,@r) = (ref($_[0]),@_); # objectify is costly, so avoid it @@ -2742,7 +2742,7 @@ sub _split # some possible inputs: # 2.1234 # 0.12 # 1 # 1E1 # 2.134E1 # 434E-10 # 1.02009E-2 - # .2 # 1_2_3.4_5_6 # 1.4E1_2_3 # 1e3 # +.2 + # .2 # 1_2_3.4_5_6 # 1.4E1_2_3 # 1e3 # +.2 # 0e999 #return if $$x =~ /[Ee].*[Ee]/; # more than one E => error @@ -2768,6 +2768,8 @@ sub _split $mis = $1||'+'; $miv = $2; return unless ($mf =~ /^(\d*?)0*$/); # strip trailing zeros $mfv = $1; + # handle the 0e999 case here + $ev = 0 if $miv eq '0' && $mfv eq ''; return (\$mis,\$miv,\$mfv,\$es,\$ev); } } @@ -3041,32 +3043,29 @@ exactly what you expect. =over 2 -=item Canonical notation - -Big integer values are strings of the form C with leading -zeros suppressed. +=item Input - '-0' canonical value '-0', normalized '0' - ' -123_123_123' canonical value '-123123123' - '1_23_456_7890' canonical value '1234567890' +Input values to these routines may be any string, that looks like a number +and results in an integer, including hexadecimal and binary numbers. -=item Input +Scalars holding numbers may also be passed, but note that non-integer numbers +may already have lost precision due to the conversation to float. Quote +your input if you want BigInt to see all the digits. -Input values to these routines may be either Math::BigInt objects or -strings of the form C, or -hexadecimal C, or binary C. + $x = Math::BigInt->new(12345678890123456789); # bad + $x = Math::BigInt->new('12345678901234567890'); # good You can include one underscore between any two digits. This means integer values like 1.01E2 or even 1000E-2 are also accepted. -Non integer values result in NaN. +Non-integer values result in NaN. -Math::BigInt::new() defaults to 0, while Math::BigInt::new('') results -in 'NaN'. +Currently, Math::BigInt::new() defaults to 0, while Math::BigInt::new('') +results in 'NaN'. -bnorm() on a BigInt object is now effectively a no-op, since the numbers +C on a BigInt object is now effectively a no-op, since the numbers are always stored in normalized form. On a string, it creates a BigInt -object. +object from the input. =item Output @@ -3228,10 +3227,12 @@ result). $x = Math::BigInt->new($str,$A,$P,$R); -Creates a new BigInt object from a string or another BigInt object. The +Creates a new BigInt object from a scalar or another BigInt object. The input is accepted as decimal, hex (with leading '0x') or binary (with leading '0b'). +See L for more info on accepted input formats. + =head2 bnan $x = Math::BigInt->bnan(); diff --git a/lib/Math/BigInt/Calc.pm b/lib/Math/BigInt/Calc.pm index a3091c7..c09e07a 100644 --- a/lib/Math/BigInt/Calc.pm +++ b/lib/Math/BigInt/Calc.pm @@ -8,7 +8,7 @@ require Exporter; use vars qw/@ISA $VERSION/; @ISA = qw(Exporter); -$VERSION = '0.35'; +$VERSION = '0.36'; # Package to store unsigned big integers in decimal and do math with them @@ -513,8 +513,18 @@ sub _div_use_mul { # ref to array, ref to array, modify first array and return remainder if # in list context + + # see comments in _div_use_div() for more explanations + my ($c,$x,$yorg) = @_; + + # the general div algorithmn here is about O(N*N) and thus quite slow, so + # we first check for some special cases and use shortcuts to handle them. + # This works, because we store the numbers in a chunked format where each + # element contains 5..7 digits (depending on system). + + # if both numbers have only one element: if (@$x == 1 && @$yorg == 1) { # shortcut, $yorg and $x are two small numbers @@ -530,6 +540,8 @@ sub _div_use_mul return $x; } } + + # if x has more than one, but y has only one element: if (@$yorg == 1) { my $rem; @@ -549,6 +561,69 @@ sub _div_use_mul return $x; } + # now x and y have more than one element + + # check whether y has more elements than x, if yet, the result will be 0 + if (@$yorg > @$x) + { + my $rem; + $rem = [@$x] if wantarray; # make copy + splice (@$x,1); # keep ref to original array + $x->[0] = 0; # set to 0 + return ($x,$rem) if wantarray; # including remainder? + return $x; # only x, which is [0] now + } + # check whether the numbers have the same number of elements, in that case + # the result will fit into one element and can be computed efficiently + if (@$yorg == @$x) + { + my $rem; + # if $yorg has more digits than $x (it's leading element is longer than + # the one from $x), the result will also be 0: + if (length(int($yorg->[-1])) > length(int($x->[-1]))) + { + $rem = [@$x] if wantarray; # make copy + splice (@$x,1); # keep ref to org array + $x->[0] = 0; # set to 0 + return ($x,$rem) if wantarray; # including remainder? + return $x; + } + # now calculate $x / $yorg + if (length(int($yorg->[-1])) == length(int($x->[-1]))) + { + # same length, so make full compare, and if equal, return 1 + # hm, same lengths, but same contents? So we need to check all parts: + my $a = 0; my $j = scalar @$x - 1; + # manual way (abort if unequal, good for early ne) + while ($j >= 0) + { + last if ($a = $x->[$j] - $yorg->[$j]); $j--; + } + # $a contains the result of the compare between X and Y + # a < 0: x < y, a == 0 => x == y, a > 0: x > y + if ($a <= 0) + { + if (wantarray) + { + $rem = [ 0 ]; # a = 0 => x == y => rem 1 + $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x + } + splice(@$x,1); # keep single element + $x->[0] = 0; # if $a < 0 + if ($a == 0) + { + # $x == $y + $x->[0] = 1; + } + return ($x,$rem) if wantarray; + return $x; + } + # $x >= $y, proceed normally + } + } + + # all other cases: + my $y = [ @$yorg ]; # always make copy to preserve my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0); @@ -580,7 +655,7 @@ sub _div_use_mul $u2 = 0 unless $u2; #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" # if $v1 == 0; - $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1)); + $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1)); --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2); if ($q) { @@ -597,11 +672,12 @@ sub _div_use_mul for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) { $x->[$xi] -= $MBASE - if ($car = (($x->[$xi] += $y->[$yi] + $car) > $MBASE)); + if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $MBASE)); } } } - pop(@$x); unshift(@q, $q); + pop(@$x); + unshift(@q, $q); } if (wantarray) { @@ -688,7 +764,7 @@ sub _div_use_div splice (@$x,1); # keep ref to original array $x->[0] = 0; # set to 0 return ($x,$rem) if wantarray; # including remainder? - return $x; + return $x; # only x, which is [0] now } # check whether the numbers have the same number of elements, in that case # the result will fit into one element and can be computed efficiently @@ -709,18 +785,23 @@ sub _div_use_div if (length(int($yorg->[-1])) == length(int($x->[-1]))) { # same length, so make full compare, and if equal, return 1 - # hm, same lengths, but same contents? So we need to check all parts: + # hm, same lengths, but same contents? So we need to check all parts: my $a = 0; my $j = scalar @$x - 1; # manual way (abort if unequal, good for early ne) while ($j >= 0) { last if ($a = $x->[$j] - $yorg->[$j]); $j--; } + # $a contains the result of the compare between X and Y # a < 0: x < y, a == 0 => x == y, a > 0: x > y if ($a <= 0) { - $rem = [@$x] if wantarray; - splice(@$x,1); + if (wantarray) + { + $rem = [ 0 ]; # a = 0 => x == y => rem 1 + $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x + } + splice(@$x,1); # keep single element $x->[0] = 0; # if $a < 0 if ($a == 0) { @@ -730,9 +811,8 @@ sub _div_use_div return ($x,$rem) if wantarray; return $x; } - # $x >= $y, proceed normally + # $x >= $y, so proceed normally } - } # all other cases: @@ -760,6 +840,10 @@ sub _div_use_div { push(@$x, 0); } + + # @q will accumulate the final result, $q contains the current computed + # part of the final result + @q = (); ($v2,$v1) = @$y[-2,-1]; $v2 = 0 unless $v2; while ($#$x > $#$y) @@ -768,7 +852,7 @@ sub _div_use_div $u2 = 0 unless $u2; #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" # if $v1 == 0; - $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1)); + $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1)); --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2); if ($q) { @@ -785,7 +869,7 @@ sub _div_use_div for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) { $x->[$xi] -= $MBASE - if ($car = (($x->[$xi] += $y->[$yi] + $car) > $MBASE)); + if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $MBASE)); } } } @@ -1013,6 +1097,7 @@ sub _mod my ($xo,$rem) = _div($c,$x,$yo); return $rem; } + my $y = $yo->[0]; # both are single element arrays if (scalar @$x == 1) @@ -1021,7 +1106,7 @@ sub _mod return $x; } - # @y is single element, but @x has more than one + # @y is a single element, but @x has more than one element my $b = $BASE % $y; if ($b == 0) { diff --git a/lib/Math/BigInt/t/bare_mbf.t b/lib/Math/BigInt/t/bare_mbf.t index d11daf7..1c4a97a 100644 --- a/lib/Math/BigInt/t/bare_mbf.t +++ b/lib/Math/BigInt/t/bare_mbf.t @@ -27,7 +27,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 1760; + plan tests => 1768; } use Math::BigFloat lib => 'BareCalc'; diff --git a/lib/Math/BigInt/t/bare_mbi.t b/lib/Math/BigInt/t/bare_mbi.t index 6106466..0c27c3e 100644 --- a/lib/Math/BigInt/t/bare_mbi.t +++ b/lib/Math/BigInt/t/bare_mbi.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 2648; + plan tests => 2668; } use Math::BigInt lib => 'BareCalc'; diff --git a/lib/Math/BigInt/t/bigfltpm.inc b/lib/Math/BigInt/t/bigfltpm.inc index 2cb5543..712caa6 100644 --- a/lib/Math/BigInt/t/bigfltpm.inc +++ b/lib/Math/BigInt/t/bigfltpm.inc @@ -431,6 +431,10 @@ abc:NaN 11111b:NaN +1z:NaN -1z:NaN +0e999:0 +0e-999:0 +-0e999:0 +-0e-999:0 0:0 +0:0 +00:0 diff --git a/lib/Math/BigInt/t/bigfltpm.t b/lib/Math/BigInt/t/bigfltpm.t index 000856b..0d73a7d 100755 --- a/lib/Math/BigInt/t/bigfltpm.t +++ b/lib/Math/BigInt/t/bigfltpm.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 1760 + plan tests => 1768 + 2; # own tests } diff --git a/lib/Math/BigInt/t/bigintpm.inc b/lib/Math/BigInt/t/bigintpm.inc index 3852c1c..caf722c 100644 --- a/lib/Math/BigInt/t/bigintpm.inc +++ b/lib/Math/BigInt/t/bigintpm.inc @@ -769,6 +769,10 @@ NaN:inf: -inf:NaN: NaN:-inf: &bnorm +0e999:0 +0e-999:0 +-0e999:0 +-0e-999:0 123:123 # binary input 0babc:NaN @@ -1473,6 +1477,11 @@ inf:0:inf 1234567890999999999:9876543210:124999998 1234567890000000000:9876543210:124999998 96969696969696969696969696969678787878626262626262626262626262:484848484848484848484848486666666666666689898989898989898989:199 +# bug up to v0.35 in Calc (--$q one too many) +84696969696969696956565656566184292929292929292847474747436308080808080808086765396464646464646465:13131313131313131313131313131394949494949494949494949494943535353535353535353535:6449999999999999999 +84696969696969696943434343434871161616161616161452525252486813131313131313143230042929292929292930:13131313131313131313131313131394949494949494949494949494943535353535353535353535:6449999999999999998 +84696969696969696969696969697497424242424242424242424242385803030303030303030300750000000000000000:13131313131313131313131313131394949494949494949494949494943535353535353535353535:6450000000000000000 +84696969696969696930303030303558030303030303030057575757537318181818181818199694689393939393939395:13131313131313131313131313131394949494949494949494949494943535353535353535353535:6449999999999999997 &bmodinv # format: number:modulus:result # bmodinv Data errors @@ -1618,6 +1627,10 @@ abc:1:abc:NaN 123456789123456789:113:39 # bug in bmod() not modifying the variable in place -629:5033:4404 +# bug in bmod() in Calc in the _div_use_div() shortcut code path, +# when X == X and X was big +111111111111111111111111111111:111111111111111111111111111111:0 +12345678901234567890:12345678901234567890:0 &bgcd abc:abc:NaN abc:+0:NaN diff --git a/lib/Math/BigInt/t/bigintpm.t b/lib/Math/BigInt/t/bigintpm.t index 2522f83..0bc4ac4 100755 --- a/lib/Math/BigInt/t/bigintpm.t +++ b/lib/Math/BigInt/t/bigintpm.t @@ -10,7 +10,7 @@ BEGIN my $location = $0; $location =~ s/bigintpm.t//; unshift @INC, $location; # to locate the testing files chdir 't' if -d 't'; - plan tests => 2648; + plan tests => 2668; } use Math::BigInt; diff --git a/lib/Math/BigInt/t/mbi_rand.t b/lib/Math/BigInt/t/mbi_rand.t index fa8e966..a7bd929 100644 --- a/lib/Math/BigInt/t/mbi_rand.t +++ b/lib/Math/BigInt/t/mbi_rand.t @@ -23,9 +23,9 @@ my $c = 'Math::BigInt'; my $length = 128; # If you get a failure here, please re-run the test with the printed seed -# value as input: perl t/mbi_rand.t seed +# value as input "perl t/mbi_rand.t seed" and send me the output -my $seed = ($#ARGV == 0) ? $ARGV[0] : int(rand(65537)); +my $seed = ($#ARGV == 0) ? $ARGV[0] : int(rand(1165537)); print "# seed: $seed\n"; srand($seed); my ($A,$B,$As,$Bs,$ADB,$AMB,$la,$lb); @@ -35,12 +35,14 @@ for (my $i = 0; $i < $count; $i++) # length of A and B $la = int(rand($length)+1); $lb = int(rand($length)+1); $As = ''; $Bs = ''; + # we create the numbers from "patterns", e.g. get a random number and a # random count and string them together. This means things like # "100000999999999999911122222222" are much more likely. If we just strung # together digits, we would end up with "1272398823211223" etc. while (length($As) < $la) { $As .= int(rand(100)) x int(rand(16)); } while (length($Bs) < $lb) { $Bs .= int(rand(100)) x int(rand(16)); } + $As =~ s/^0+//; $Bs =~ s/^0+//; $As = $As || '0'; $Bs = $Bs || '0'; # print "# As $As\n# Bs $Bs\n"; @@ -50,23 +52,29 @@ for (my $i = 0; $i < $count; $i++) { for (1..4) { ok (1,1); } next; } + # check that int(A/B)*B + A % B == A holds for all inputs + # $X = ($A/$B)*$B + 2 * ($A % $B) - ($A % $B); + ($ADB,$AMB) = $A->copy()->bdiv($B); - print "# ". join(' ',Math::BigInt::Calc->_base_len()),"\n". +# print "# ($A / $B, $A % $B ) = $ADB $AMB\n"; + + print "# seed $seed, ". join(' ',Math::BigInt::Calc->_base_len()),"\n". "# tried $ADB * $B + $two*$AMB - $AMB\n" unless ok ($ADB*$B+$two*$AMB-$AMB,$As); - ok ($ADB*$B/$B,$ADB); + print "\$ADB * \$B / \$B = ", $ADB * $B / $B, " != $ADB (\$B=$B)\n" + unless ok ($ADB*$B/$B,$ADB); # swap 'em and try this, too # $X = ($B/$A)*$A + $B % $A; ($ADB,$AMB) = $B->copy()->bdiv($A); #print "check: $ADB $AMB"; - print "# ". join(' ',Math::BigInt::Calc->_base_len()),"\n". + print "# seed $seed, ". join(' ',Math::BigInt::Calc->_base_len()),"\n". "# tried $ADB * $A + $two*$AMB - $AMB\n" unless ok ($ADB*$A+$two*$AMB-$AMB,$Bs); - #print "$ADB * $A = ",$ADB * $A,"\n"; - #print " +$two * $AMB = ",$ADB * $A + $two * $AMB,"\n"; - #print " -$AMB = ",$ADB * $A + $two * $AMB - $AMB,"\n"; - ok ($ADB*$A/$A,$ADB); +# print " +$two * $AMB = ",$ADB * $A + $two * $AMB,"\n"; +# print " -$AMB = ",$ADB * $A + $two * $AMB - $AMB,"\n"; + print "\$ADB * \$A / \$A = ", $ADB * $A / $A, " != $ADB (\$A=$A)\n" + unless ok ($ADB*$A/$A,$ADB); } diff --git a/lib/Math/BigInt/t/sub_mbf.t b/lib/Math/BigInt/t/sub_mbf.t index c812191..d2c19c2 100755 --- a/lib/Math/BigInt/t/sub_mbf.t +++ b/lib/Math/BigInt/t/sub_mbf.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 1760 + plan tests => 1768 + 6; # + our own tests } diff --git a/lib/Math/BigInt/t/sub_mbi.t b/lib/Math/BigInt/t/sub_mbi.t index 39e47d5..1979173 100755 --- a/lib/Math/BigInt/t/sub_mbi.t +++ b/lib/Math/BigInt/t/sub_mbi.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 2648 + plan tests => 2668 + 5; # +5 own tests } diff --git a/lib/Math/BigInt/t/with_sub.t b/lib/Math/BigInt/t/with_sub.t index 2b6d871..c4319aa 100644 --- a/lib/Math/BigInt/t/with_sub.t +++ b/lib/Math/BigInt/t/with_sub.t @@ -28,7 +28,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 1760 + plan tests => 1768 + 1; }