This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to Math::BigInt pre-rel 1.66 as of
authorJarkko Hietaniemi <jhi@iki.fi>
Wed, 3 Sep 2003 20:27:56 +0000 (20:27 +0000)
committerJarkko Hietaniemi <jhi@iki.fi>
Wed, 3 Sep 2003 20:27:56 +0000 (20:27 +0000)
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

12 files changed:
lib/Math/BigInt.pm
lib/Math/BigInt/Calc.pm
lib/Math/BigInt/t/bare_mbf.t
lib/Math/BigInt/t/bare_mbi.t
lib/Math/BigInt/t/bigfltpm.inc
lib/Math/BigInt/t/bigfltpm.t
lib/Math/BigInt/t/bigintpm.inc
lib/Math/BigInt/t/bigintpm.t
lib/Math/BigInt/t/mbi_rand.t
lib/Math/BigInt/t/sub_mbf.t
lib/Math/BigInt/t/sub_mbi.t
lib/Math/BigInt/t/with_sub.t

index ad3e12a..6c1c36d 100644 (file)
@@ -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</^[+-]\d+$/> 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</^\s*[+-]?[\d]+\.?[\d]*E?[+-]?[\d]*$/>, or
-hexadecimal C</^\s*[+-]?[0-9a-f]+$/i>, or binary C</^\s*[+-]?[01]+$/>.
+       $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<bnorm()> 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<Input> for more info on accepted input formats.
+
 =head2 bnan
 
        $x = Math::BigInt->bnan();
index a3091c7..c09e07a 100644 (file)
@@ -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)
     {
index d11daf7..1c4a97a 100644 (file)
@@ -27,7 +27,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1760;
+  plan tests => 1768;
   }
 
 use Math::BigFloat lib => 'BareCalc';
index 6106466..0c27c3e 100644 (file)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 2648;
+  plan tests => 2668;
   }
 
 use Math::BigInt lib => 'BareCalc';
index 2cb5543..712caa6 100644 (file)
@@ -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
index 000856b..0d73a7d 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1760
+  plan tests => 1768
        + 2;            # own tests
   }
 
index 3852c1c..caf722c 100644 (file)
@@ -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
index 2522f83..0bc4ac4 100755 (executable)
@@ -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;
index fa8e966..a7bd929 100644 (file)
@@ -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);
   }
 
index c812191..d2c19c2 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n"; 
   
-  plan tests => 1760
+  plan tests => 1768
     + 6;       # + our own tests
   }
 
index 39e47d5..1979173 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 2648
+  plan tests => 2668
     + 5;       # +5 own tests
   }
 
index 2b6d871..c4319aa 100644 (file)
@@ -28,7 +28,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1760
+  plan tests => 1768
        + 1;
   }