[libmath-prime-util-perl] 36/59: More random prime work

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:44:58 UTC 2015


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

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

commit 40cb80d31de2db86cc5c9a96c70b07c2471ad065
Author: Dana Jacobsen <dana at acm.org>
Date:   Sun Jul 8 03:47:19 2012 -0600

    More random prime work
---
 lib/Math/Prime/Util.pm | 100 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 68 insertions(+), 32 deletions(-)

diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index 5d8408a..21daaf7 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -287,13 +287,13 @@ sub primes {
 #
 # The random_maurer_prime function uses Maurer's algorithm of course.
 #
-# The current code is pretty fast for native types, but very slow for bigints.
-#      37uS for   24-bit
-#     0.25s for   64-bit
+# The current code is reasonably fast for native, but very slow for bigints.
+#      70uS for   24-bit
+#     0.01s for   64-bit
 #     0.2s  for  128-bit
-#     1.3s  for  256-bit
-#     9s    for  512-bit
-#   3m      for 1024-bit
+#     1s    for  256-bit
+#    10s    for  512-bit
+#   1m      for 1024-bit
 #  ~4m      for 2048-bit
 # ~80m      for 4096-bit
 #
@@ -330,7 +330,7 @@ sub primes {
     my $max = int($range) + 1;
     my $offset = 0;
     while ($max > 1) {
-      if ($max <= $rand_max_val) { $offset += $irandf->($max); last; }
+      if ($max <= 31) { $offset += $irandf->($max); last; }
       my $part = $max >> 1;
       $part++ if ($max & 1) && $get_rand_bit->();
       $offset += $part if $get_rand_bit->();
@@ -343,13 +343,15 @@ sub primes {
   # ranges the two edges are selected with slightly higher priority because
   # we're approximating 1/r using powers of 2.  The error rapidly reduces
   # as r increases.  By calling out to irandf when max is small enough we can
-  # make it basically go away.
+  # mitigate it.
   #
   # The other implementation choice I can think of is to call irandf a bunch
   # of times to get a random number R >= r.  Let m = int(R/r).  If R < m*r
   # then return R % m.  Repeat otherwise.  This description isn't quite right
   # in that we want to generate R with at least as many random bits as r, not
   # necessarily greater, and m is related to the bits in each.
+  #
+  # Lastly I could do a recursive partition.
 
 
   # Sub to call with low and high already primes and verified range.
@@ -401,19 +403,36 @@ sub primes {
 
     # Since we have an arbitrary range and not a power of two, I don't see how
     # Fouque's algorithm A1 could be used (where we generate lower bits and
-    # generate random sets of upper).  What I'm doing is pulling out 2^31 lower
-    # bits, then randomly select all the uppers.  We iterate adding in the lower
-    # bits.
-
-    my $srange = $oddrange - $rand_max_val - 1;
-    my $offset = $get_rand_range->($srange);
-    my $primelow = $low + 2 * $offset;
+    # generate random sets of upper).  Similarly trying to simply generate
+    # upper bits is full of ways to trip up and get non-uniform results.
+    #
+    # What I'm doing here is:
+    #
+    #   1) divide the range into semi-evenly sized partitions, where each part
+    #      is as close to $rand_max_val as we can.
+    #   2) randomly select one of the partitions.
+    #   3) iterate choosing random values within the partition.
+
+    my $nbins   = int( ($oddrange + $rand_max_val - 1) / $rand_max_val );
+    my $binsize = int( ($oddrange + $nbins - 1) / $nbins );
+    my $nparts  = int( $oddrange / $binsize );
+    
+    my $rpart = $get_rand_range->($nparts);
+    #my $rpart = (($irandf->($rand_max_val) << 31) + $irandf->($rand_max_val)) % $nparts;
+
+    my $primelow = $low + 2 * $binsize * $rpart;
+    my $partsize = ($rpart < $nparts) ? $binsize
+                                      : $oddrange - ($nparts * $binsize);
+    $partsize = int($partsize->bstr) if ref($partsize) eq 'Math::BigInt';
+    #warn "range $oddrange  = $nparts * $binsize + ", $oddrange - ($nparts * $binsize), "\n";
+    #warn "  chose part $rpart size $partsize\n";
+    #warn "  primelow is $low + 2 * $binsize * $rpart = $primelow\n";
 
     # Generate random numbers in the interval until one is prime.
     my $loop_limit = 2000 * 1000;  # To protect against broken rand
     while (1) {
-      $prime = $primelow + ( 2 * $irandf->($rand_max_val) );
-      die "$prime > $high" if $prime > $high;
+      $prime = $primelow + ( 2 * $irandf->($partsize) );
+      croak "random prime failure, $prime > $high" if $prime > $high;
       croak "Random function broken?" if $loop_limit-- < 0;
       if (ref($prime) eq 'Math::BigInt') {
         next if $prime > 53 && Math::BigInt::bgcd($prime, "16294579238595022365") != 1;
@@ -551,15 +570,21 @@ sub primes {
       #warn "$n passes final gcd\n";
 
       # Or via a different method, where we check q >= n**1/3 and also do
-      # some tests on x & y from 2R = xq+y.  Crypt::Primes does the q test
-      # but doesn't seem to do the x/y and perfect square portions.
+      # some tests on x & y from 2R = xq+y (see Lemma 2 from Maurer's paper).
+      # Crypt::Primes does the q test but doesn't seem to do the x/y and
+      # perfect square portions.
       #   next if ($q <= $n->copy->bpow(1/3));
-      #   next if ....
+      #   my $x = (2*$R)->bdiv($q)->bfloor;
+      #   my $y = 2*$R - $x*$q;
+      #   my $z = $y*$y - 4*$x;
+      #   next if $z == 0;
+      #   next if $z is a perfect square
+      # Menezes seems to imply only the q test needs to be done.
 
       # Finally, verify with a BPSW test on the result.  This will either,
       #  1) save us from accidently outputing a non-prime due to some mistake
       #  2) make history by finding the first known BPSW pseudo-prime
-      die "Maurer prime $n failed BPSW" unless is_prob_prime($n);
+      croak "Maurer prime $n failed BPSW" unless is_prob_prime($n);
       #warn "     and passed BPSW.\n";
 
       return $n;
@@ -1489,7 +1514,6 @@ the obvious Monte Carlo method is used, where random numbers in the range are
 selected until one is prime.  For even larger ranges, a method similar to that
 of Fouque and Tibouchi (2011) algorithm A1 is used.
 
-
 Perl's L<rand> function is normally called, but if the sub C<main::rand>
 exists, it will be used instead.  When called with no arguments it should
 return a float value between 0 and 1-epsilon, with 31 bits of randomness.
@@ -1501,9 +1525,10 @@ Examples:
   # Use a custom random function
   sub rand { ... }
 
-If you want cryptographically secure primes, I suggest looking at
-L<Crypt::Primes> for now.  At minimum you should use a better source of
-random numbers, such as L<Crypt::Random>.
+If you want cryptographically secure primes, at minimum a better source of
+random numbers should be used, e.g. L<Crypt::Random>.  Until this module
+has more testing, I would point the user to L<Crypt::Primes> for production
+use.
 
 
 =head2 random_ndigit_prime
@@ -1531,13 +1556,24 @@ This the trivial algorithm to select primes from a range.  This gives a uniform
 distribution, however it is quite slow for bigints, where the C<is_prime>
 function is a limiter.
 
-The differences between this function and what is used by L<Crypt::Primes>
-include: (1) this function generates probable primes (albeit using BPSW) while
-the latter is provable primes; (2) this function is really fast for native
-bit sizes, but ridiculously slow in its current implementation when run on
-very large numbers of bits -- L<Crypt::Primes> is quite fast for large bits;
-(3) this function requires no external libraries while the latter requires
-L<Math::Pari>; (4) the latter has some useful options for cryptography.
+
+=head2 random_maurer_prime
+
+  use bigint;  my $bigprime = random_maurer_prime(512);
+
+Construct an n-bit provable prime, using the algorithm of Ueli Maurer (1995).
+This is the same algorithm used by L<Crypt::Primes>.
+
+The differences between this function and that in L<Crypt::Primes> include
+(1) this function is really fast for native bit sizes, but ridiculously slow
+in its current implementation when run on very large numbers of bits; (2) no
+external libraries are used for this module, while C::P uses L<Math::Pari>;
+(3) C::P uses a modified version of final acceptance criteria
+(C<q E<LT> n**(1/3)> without the rest of Lemma 2), while this module uses the
+original set followed by a BPSW probable prime test to give some extra
+assurance against code error; (4) C::P  has some useful options for
+cryptography, and (5) C::P is hardcoded to use Crypt::Random, while this
+function will use whatever you set C<rand> to (this is both good and bad).
 
 
 =head2 moebius

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



More information about the Pkg-perl-cvs-commits mailing list