This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.32
in repository libmath-prime-util-perl.

commit 15de5bb28f9be5fc92a988142dc4e0b546f3ac99
Author: Dana Jacobsen <d...@acm.org>
Date:   Mon Sep 23 18:53:32 2013 -0700

    Speedup for random_nbit_prime
---
 lib/Math/Prime/Util.pm | 100 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 83 insertions(+), 17 deletions(-)

diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index 6a0b325..880a877 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -543,11 +543,11 @@ sub primes {
         my $rbits = length($range->as_bin) - 2;   # bits in range
         my $rwords = int($rbits/32) + (($rbits % 32) ? 1 : 0);
         # Generate more bits so we rarely have to loop.
-        my $rmax = (($zero+2) ** ($rwords*32)) - 1;
+        my $rmax = Math::BigInt->bone->blsft($rwords*32)->bdec();
         my $remainder = $rmax % $range;
         do {
-          $U = $zero;
-          $U = ($U << 32) + $irandf->()  for 1 .. $rwords;
+          $U = $range->copy->from_hex
+            ("0x" . join '', map { sprintf("%08X", $irandf->()) } 1 .. 
$rwords);
         } while $U >= ($rmax - $remainder);
       } elsif ($range <= 4294967295) {
         my $remainder = 4294967295 % $range;
@@ -565,6 +565,39 @@ sub primes {
     };
     return $randf;
   }
+  # Returns a function that gets an nbit random number
+  sub _get_nbit_rand_func {
+    my $irandf = $_Config{'irand'};
+    if (!defined $irandf) {
+      $_BRS = Bytes::Random::Secure->new(NonBlocking=>1) unless defined $_BRS;
+      return sub {
+        my($bits) = @_;
+        return 0 if $bits <= 0;
+        return ($_BRS->irand() >> (32-$bits))
+          if $bits <= 32;
+        return ((($_BRS->irand() << 32) + $_BRS->irand()) >> (64-$bits))
+          if $bits <= 64 && ~0 > 4294967295;
+        my $bytes = int(($bits+7)/8);
+        my $n = Math::BigInt->from_hex('0x' . $_BRS->bytes_hex($bytes));
+        $n->brsft( 8*$bytes - $bits ) unless ($bits % 8) == 0;
+        return $n;
+      };
+    } else {
+      return sub {
+        my($bits) = @_;
+        return 0 if $bits <= 0;
+        return ($irandf->() >> (32-$bits))
+          if $bits <= 32;
+        return ((($irandf->() << 32) + $irandf->()) >> (64-$bits))
+          if $bits <= 64 && ~0 > 4294967295;
+        my $words = int(($bits+31)/32);
+        my $n = Math::BigInt->from_hex
+          ("0x" . join '', map { sprintf("%08X", $irandf->()) } 1 .. $words );
+        $n->brsft( 32*$words - $bits ) unless ($bits % 32) == 0;
+        return $n;
+      };
+    }
+  }
 
   # Sub to call with low and high already primes and verified range.
   my $_random_prime = sub {
@@ -865,13 +898,15 @@ sub primes {
     # slow, then A2 would look more promising.
     #
     if (1 && $bits > 64) {
-      my $irandf = _get_rand_func();
+      my $nrandf = _get_nbit_rand_func();
       my $l = ($_Config{'maxbits'} > 32 && $bits > 79)  ?  63  :  31;
       $l = 49 if $l == 63 && $] < 5.008;  # Fix for broken Perl 5.6
       $l = $bits-2 if $bits-2 < $l;
-      my $arange = (1 << $l) - 1;  # 2^$l-1
-      my $brange = Math::BigInt->new(2)->bpow($bits-$l-2)->bdec();
-      my $b = 2 * $irandf->($brange) + 1;
+
+      my $brand = $nrandf->($bits-$l-2);
+      $brand = Math::BigInt->new("$brand") unless ref($brand) eq 
'Math::BigInt';
+      my $b = $brand->blsft(1)->binc();
+
       # Precalculate some modulii so we can do trial division on native int
       # 9699690 = 2*3*5*7*11*13*17*19, so later operations can be native ints
       my @premod;
@@ -886,7 +921,7 @@ sub primes {
       _make_big_gcds() if $_big_gcd_use < 0;
       my $loop_limit = 1_000_000;
       while ($loop_limit-- > 0) {
-        my $a = (1 << $l) + $irandf->($arange);
+        my $a = (1 << $l) + $nrandf->($l);
         # $a % s == $premod[s]  =>  $p % s == 0  =>  p will be composite
         next if $a %  3 == $premod[ 3] || $a %  5 == $premod[ 5]
              || $a %  7 == $premod[ 7] || $a % 11 == $premod[11]
@@ -912,6 +947,35 @@ sub primes {
       croak "Random function broken?";
     }
 
+    # The Trivial method.  Great uniformity, and fine for small sizes.  It
+    # gets very slow as the bit size increases, but that is why we have the
+    # method above for bigints.
+    if (1) {
+      my $nrandf = _get_nbit_rand_func();
+      if ($bits <= 4) {
+        return (2,3)[$nrandf->(1)] if $bits == 2;
+        return (5,7)[$nrandf->(1)] if $bits == 3;
+        return (11,13)[$nrandf->(1)] if $bits == 4;
+      }
+      my $loop_limit = 2_000_000;
+      if ($bits > $_Config{'maxbits'}) {
+        my $p = Math::BigInt->bone->blsft($bits-1)->binc();
+        while ($loop_limit-- > 0) {
+          my $n = Math::BigInt->new(''.$nrandf->($bits-2))->blsft(1)->badd($p);
+          return $n if is_prob_prime($n);
+        }
+      } else {
+        my $p = (1 << ($bits-1)) + 1;
+        while ($loop_limit-- > 0) {
+          my $n = $p + ($nrandf->($bits-2) << 1);
+          return $n if is_prob_prime($n);
+        }
+      }
+      croak "Random function broken?";
+    }
+
+    # Send through the generic random_prime function.  Decently fast, but
+    # quite a bit slower than the F&T A1 method above.
     if (!defined $_random_nbit_ranges[$bits]) {
       if ($bits > $_Config{'maxbits'}) {
         my $low  = Math::BigInt->new('2')->bpow($bits-1);
@@ -1117,7 +1181,7 @@ sub primes {
     my $k = shift;
     _validate_num($k, 2) || _validate_positive_integer($k, 2);
 
-    if ($_HAVE_GMP && $k <= 512) {
+    if ($_HAVE_GMP && $k <= 450) {
       my $n = random_nbit_prime($k);
       my ($isp, $cert) = is_provable_prime_with_cert($n);
       croak "small nbit prime could not be proven" if $isp != 2;
@@ -4646,8 +4710,9 @@ The C<n-1> proving algorithm in L<Math::Prime::Util::GMP> 
compares well to
 the version including in Pari.  Both are pretty fast to about 60 digits, and
 work reasonably well to 80 or so before starting to take over many minutes per
 number on a fast computer.  Version 0.09 and newer of MPU::GMP contain an
-ECPP implementation that works quite well, though is certainly not state of
-the art.  It averages less than a second for proving 200-digit primes,
+ECPP implementation that, while not state of the art compared to closed source
+solutions, works quite well.
+It averages less than a second for proving 200-digit primes
 including creating a certificate.  Times below 200 digits are faster than
 Pari 2.3.5's APR-CL proof.  For larger inputs the bottleneck is a limited set
 of discriminants, and time becomes more variable.  There is a larger set of
@@ -4658,15 +4723,16 @@ with very large numbers, I recommend 
L<Primo|http://www.ellipsa.eu/>.
 =head2 RANDOM PRIME GENERATION
 
 Seconds per prime for random prime generation on a circa-2009 workstation,
-with L<Math::Prime::Util::GMP> and L<Math::Random::ISAAC::XS> installed.
+with L<Math::BigInt::GMP>, L<Math::Prime::Util::GMP>, and
+L<Math::Random::ISAAC::XS> installed.
 
   bits    random   +testing  rand_prov   Maurer   CPMaurer
   -----  --------  --------  ---------  --------  --------
-     64    0.0001  +0.000003   0.0003     0.0001    0.022
-    128    0.0026  +0.00016    0.012      0.077     0.057
-    256    0.0044  +0.0004     0.059      0.19      0.16
-    512    0.012   +0.0012     0.47       0.50      0.41
-   1024    0.067   +0.0060     1.3        1.2       2.19
+     64    0.0001  +0.000003   0.0002     0.0001    0.022
+    128    0.0020  +0.00016    0.011      0.063     0.057
+    256    0.0034  +0.0004     0.058      0.13      0.16
+    512    0.0097  +0.0012     0.28       0.28      0.41
+   1024    0.060   +0.0060     0.65       0.65      2.19
    2048    0.57    +0.039      4.8        4.8      10.99
    4096    6.24    +0.25      31.9       31.9      79.71
    8192   58.6     +1.61     234.0      234.0     947.3

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git

_______________________________________________
Pkg-perl-cvs-commits mailing list
Pkg-perl-cvs-commits@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-perl-cvs-commits

Reply via email to