This is an automated email from the git hooks/post-receive script. ppm-guest pushed a commit to annotated tag v0.15 in repository libmath-prime-util-perl.
commit 315d5e28616275e21684b11dd93fcd00d222c8c6 Author: Dana Jacobsen <d...@acm.org> Date: Sun Dec 9 06:24:54 2012 -0800 Major changes to random primes. Return BigInts for big results on primorial and random primes. --- Changes | 6 +- lib/Math/Prime/Util.pm | 629 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 439 insertions(+), 196 deletions(-) diff --git a/Changes b/Changes index 659b210..19d6948 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,6 @@ Revision history for Perl extension Math::Prime::Util. -0.15 ?? December 2012 +0.15 9 December 2012 - Lots of internal changes to Ei, li, Zeta, and R functions: - Native Zeta and R have slightly more accurate results. @@ -17,6 +17,10 @@ Revision history for Perl extension Math::Prime::Util. - Fix some issues with random nbit and maurer primes. + - The random prime and primorial functions now will return a Math::BigInt + object if the result is greater than the native size. This includes + loading up the Math::BigInt library if necessary. + 0.14 29 November 2012 - Compilation and test issues: diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm index 588eafe..3105dfe 100644 --- a/lib/Math/Prime/Util.pm +++ b/lib/Math/Prime/Util.pm @@ -5,7 +5,7 @@ use Carp qw/croak confess carp/; BEGIN { $Math::Prime::Util::AUTHORITY = 'cpan:DANAJ'; - $Math::Prime::Util::VERSION = '0.14'; + $Math::Prime::Util::VERSION = '0.15'; } # parent is cleaner, and in the Perl 5.10.1 / 5.12.0 core, but not earlier. @@ -31,6 +31,8 @@ our @EXPORT_OK = qw( ); our %EXPORT_TAGS = (all => [ @EXPORT_OK ]); +my %_Config; + # Similar to how boolean handles its option sub import { my @options = grep $_ ne '-nobigint', @_; @@ -40,6 +42,7 @@ sub import { } sub _import_nobigint { + $_Config{'nobigint'} = 1; undef *factor; *factor = \&_XS_factor; undef *is_prime; *is_prime = \&_XS_is_prime; undef *is_prob_prime; *is_prob_prime = \&_XS_is_prob_prime; @@ -51,8 +54,6 @@ sub _import_nobigint { undef *miller_rabin; *miller_rabin = \&_XS_miller_rabin; } -my %_Config; - BEGIN { # Load PP code. Nothing exported. @@ -85,6 +86,7 @@ BEGIN { *pminus1_factor = \&Math::Prime::Util::PP::pminus1_factor; }; + $_Config{'nobigint'} = 0; $_Config{'gmp'} = 0; # See if they have the GMP module and haven't requested it not to be used. if (!defined $ENV{MPU_NO_GMP} || $ENV{MPU_NO_GMP} != 1) { @@ -92,6 +94,11 @@ BEGIN { Math::Prime::Util::GMP->import(); 1; }; } + + use Config; + $_Config{'system_randbits'} = $Config{'randbits'}; + no Config; + } END { _prime_memfreeall; @@ -265,6 +272,8 @@ sub primes { croak "Internal error: large value without bigint loaded." unless defined $Math::BigInt::VERSION; @$sref = map { Math::BigInt->new("$_") } @$sref; + } else { + @$sref = map { int($_) } @$sref; } return $sref; } @@ -353,27 +362,50 @@ sub primes { # The probability for a prime is proportional to its gap, which is # really a bad distribution. # -# For standard random primes, the implementation is very similar to Fouque's -# Algorithm 1. For ranges of 32-bits or less, the distribution is uniform. -# For larger ranges it is very close (See Foque/Tibouchi). +# In this code, for ranges within randbits (typically 48 on UNIX system rand, +# 31 for user-provided rand, and 16 for most Win32 systems), the results +# are completely uniform. For larger ranges it is close. +# +# The random_maurer_prime function uses Maurer's FastPrime algorithm. +# +# These functions are quite fast for native size inputs, and reasonably fast +# for bigints. Some factors that make a significant difference: +# - Is Math::Prime::Util::GMP installed? +# - Using Math::BigInt::GMP or Math::BigInt::Pari? Very important. +# - Which platform? Typically x86_64 is best optimized. +# - If using system rand, is RANDBITS large? +# - What RNG? # -# The random_maurer_prime function uses Maurer's algorithm of course. +# random_nbit_prime random_maurer_prime +# n-bits no GMP w/ MPU::GMP no GMP w/ MPU::GMP +# ---------- -------- ----------- -------- ----------- +# 24-bit 14uS same same same +# 64-bit 70uS same same same +# 128-bit 0.06s 0.006s 0.06s 0.07s +# 256-bit 0.1s 0.012s 0.17s 0.16s +# 512-bit 0.2s 0.028s 0.46s 0.47s +# 1024-bit 0.6s 0.12s 1.2s 1.1s +# 2048-bit 2.3s 1.0s 5.2s 4.3s +# 4096-bit 17.5s 12s 23s 23s # -# The current code is reasonably fast for native, but slow for bigints. Using -# the M::P::U::GMP module helps immensely. Performance does differ though -- -# my 32-bit machine is ~5x slower than this 64-bit machine for this. +# Writing these entirely in GMP has a problem, which is that we want to use +# a user-supplied rand function, which means a lot of callbacks. One +# possibility is to, if they do not supply a rand function, use the GMP MT +# function with an appropriate seed. # -# n-bits no GMP with MPU::GMP -# ---------- ---------- -------------- -# 24-bit 15uS (native) -# 64-bit 60uS (native) -# 128-bit 0.2s 0.01s -# 256-bit 1s 0.02s -# 512-bit 10s 0.03s -# 1024-bit 1m 0.1s -# 2048-bit ~4m 0.6s -# 4096-bit ~80m 7s -# 8192-bit ---- 80s +# It will generate primes with more bits, but it slows down a lot. The +# time variation becomes quite extreme once bit sizes get over 6000 or so. +# +# Random timings for 1M calls: +# 0.054 system rand +# 0.24 Math::Random::MT::Auto +# 2.27 Math::Random::Secure (with Math::Random::ISAAC::XS) +# 6.73 Math::Random::Secure +# 7.31 *Bytes::Random::Secure (with Math::Random::ISAAC::XS) +# 16.2 *Bytes::Random::Secure +# 180.0 *Crypt::Random (probably blocked on /dev/random) +# * BRS and CR were hindered on this test by being used in a sub, and neither +# are being used to their full potential of returning big random chunks. # # To verify distribution: # perl -Iblib/lib -Iblib/arch -MMath::Prime::Util=:all -E 'my %freq; $n=1000000; $freq{random_nbit_prime(6)}++ for (1..$n); printf("%4d %6.3f%%\n", $_, 100.0*$freq{$_}/$n) for sort {$a<=>$b} keys %freq;' @@ -381,13 +413,34 @@ sub primes { { # Note: I was using rand($range), but Math::Random::MT ignores the argument - # instead of following its documentation. + # instead of following its documentation. (Fixed in v1.16) my $irandf = sub { return int( (defined &::rand) ? ::rand()*$_[0] : rand()*$_[0] ); }; - # TODO: Look at RANDBITS if using system rand - my $rand_max_bits = 31; - my $rand_max_val = 1 << $rand_max_bits; + my $irandmaxbitsf = sub { + # 31 for user-defined, randbits for system rand + my $rand_max_bits = (defined &::rand) ? 31 : $_Config{'system_randbits'}; + # but always make sure we can make an integer from it. + $rand_max_bits = $_Config{'maxbits'}-1 if $rand_max_bits >= $_Config{'maxbits'}; + return $rand_max_bits; + }; + + # These are much faster than straightforward trial division when n is big. + # You'll want to first do a test up to and including 23. + my @_big_gcd; + my $_big_gcd_top = 20046; + my $_big_gcd_use = -1; + sub _make_big_gcds { + croak "Internal error: make_big_gcds needs Math::BigInt!" unless defined $Math::BigInt::VERSION; + my $p0 = primorial(Math::BigInt->new( 520)); + my $p1 = primorial(Math::BigInt->new(2052)); + my $p2 = primorial(Math::BigInt->new(6028)); + my $p3 = primorial(Math::BigInt->new($_big_gcd_top)); + $_big_gcd[0] = $p0 / 223092870; + $_big_gcd[1] = $p1 / $p0; + $_big_gcd[2] = $p2 / $p1; + $_big_gcd[3] = $p3 / $p2; + } # Returns a uniform number between [0,$range] inclusive. The straightforward # method of getting a number of rand bits equal to the number of bits in the @@ -403,6 +456,7 @@ sub primes { my $t = $range; while ($t) { $rbits++; $t >>= 1; } } + my $rand_max_bits = $irandmaxbitsf->(); while (1) { my $rbitsleft = $rbits; my $U = $range - $range; # zero in possible bigint @@ -420,17 +474,18 @@ sub primes { my($low,$high) = @_; my $prime; - # { my $bsize = 100; my @bins; my $counts = 10000000; - # for my $c (1..$counts) { $bins[ $get_rand_range->($bsize) ]++; } - # for my $b (0..$bsize) {printf("%4d %8.5f%%\n", $b, $bins[$b]/$counts);} + #{ my $bsize = 100; my @bins; my $counts = 10000000; + # for my $c (1..$counts) { $bins[ $get_rand_range->($bsize) ]++; } + # for my $b (0..$bsize) {printf("%4d %8.5f%%\n", $b, $bins[$b]/$counts);} } # low and high are both primes, and low < high. - if ($high < 65536 && $high <= $_XS_MAXVAL) { - # nice deterministic solution, but gets very costly with large values. - my $li = _XS_prime_count($low); - my $hi = _XS_prime_count($high); - my $irange = $hi - $li + 1; + # This is fast for small values, low memory, perfectly uniform, and consumes + # the absolute minimum amount of randomness needed. But it isn't feasible + # with large values. + if ($high <= 131072 && $high <= $_XS_MAXVAL) { + my $li = _XS_prime_count(2, $low); + my $irange = _XS_prime_count($low, $high); my $rand = $irandf->($irange); return _XS_nth_prime($li + $rand); } @@ -442,6 +497,9 @@ sub primes { #my $range = $high - $low + 1; my $oddrange = int(($high - $low) / 2) + 1; + # Find out the parameters of the random function + my $rand_max_val = 1 << $irandmaxbitsf->(); + # If $low is large (e.g. >10 digits) and $range is small (say ~10k), it # would be fastest to call primes in the range and randomly pick one. I'm # not implementing it now because it seems like a rare case. @@ -451,14 +509,22 @@ sub primes { # Our range is small enough we can just call rand once and be happy. # Generate random numbers in the interval until one is prime. my $loop_limit = 2000 * 1000; # To protect against broken rand - while (1) { - $prime = $low + 2 * $irandf->($oddrange); - croak "Random function broken?" if $loop_limit-- < 0; - next if $prime > 11 && (!($prime % 3) || !($prime % 5) || !($prime % 7) || !($prime % 11)); - return 2 if $prime == 1; # Remember the special case for 2. - last if is_prob_prime($prime); + + if ($low > 11) { + while ($loop_limit-- > 0) { + $prime = $low + 2 * $irandf->($oddrange); + next if !($prime % 3) || !($prime % 5) || !($prime % 7) || !($prime % 11); + return $prime if is_prob_prime($prime); + } + } else { + while ($loop_limit-- > 0) { + $prime = $low + 2 * $irandf->($oddrange); + next if $prime > 11 && (!($prime % 3) || !($prime % 5) || !($prime % 7) || !($prime % 11)); + return 2 if $prime == 1; # Remember the special case for 2. + return $prime if is_prob_prime($prime); + } } - return $prime; + croak "Random function broken?"; } # We have an ocean of range, and a teaspoon to hold randomness. @@ -474,6 +540,38 @@ sub primes { # 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. + # + # The downside is that we're skewing a _lot_ farther from uniformity than + # we'd like. Imagine we started at 0 with 1e18 partitions of size 100k each. + # Probability of '5' being returned = + # 1.04e-22 = 1e-18 (chose first partition) * 1/9592 (chose '5') + # Probability of '100003' being returned = + # 1.19e-22 = 1e-18 (chose second partition) * 1/8392 (chose '100003') + # Probability of '99999999999999999999977' being returned = + # 5.20e-22 = 1e-18 (chose last partition) * 1/1922 (chose '99...77') + # So the primes in the last partition will show up 5x more often. + # The partitions are selected uniformly, and the primes within are selected + # uniformly, but the number of primes in each bucket is _not_ uniform. + # Their individual probability of being selected is the probability of the + # partition (uniform) times the probability of being selected inside the + # partition (uniform with respect to all other primes in the same + # partition, but each partition is different and skewed). + # + # When selecting n-bit or n-digit primes, this effect is _much_ smaller, as + # the skew becomes approx lg(2^n) / lg(2^(n-1)) which is pretty close to 1. + # Note that we really want big partitions to even out any local skews, which + # worries me on systems with randbits of 16. In fact, we should probably + # just get two numbers on those systems. + # + # Another idea I'd like to try sometime is: + # pclo = prime_count_lower(low); + # pchi = prime_count_upper(high); + # do { + # $nth = random selection between pclo and pchi + # $prguess = nth_prime_approx($nth); + # } while ($prguess >= low) && ($prguess <= high); + # monte carlo select a prime in $prguess-2**24 to $prguess+2**24 + # which accounts for the prime distribution. my($binsize, $nparts); if (ref($oddrange) eq 'Math::BigInt') { @@ -485,10 +583,13 @@ sub primes { ($binsize,$rem) = $oddrange->copy->bdiv($nbins); $binsize++ if $rem > 0; $nparts = $oddrange->copy->bdiv($binsize); + $low = $high->copy->bzero->badd($low) if ref($low) ne 'Math::BigInt'; } else { - my $nbins = int( ($oddrange + $rand_max_val - 1) / $rand_max_val ); - $binsize = int( ($oddrange + $nbins - 1) / $nbins ); - $nparts = int( $oddrange / $binsize ); + my $nbins = int($oddrange / $rand_max_val); + $nbins++ if $nbins * $rand_max_val != $oddrange; + $binsize = int($oddrange / $nbins); + $binsize++ if $binsize*$nbins != $oddrange; + $nparts = int($oddrange/$binsize); } $nparts-- if ($nparts * $binsize) == $oddrange; @@ -505,19 +606,69 @@ sub primes { # 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->($partsize) ); + + # Simply things for non-bigints. + if (ref($low) ne 'Math::BigInt') { + while ($loop_limit-- > 0) { + my $rand = $irandf->($partsize); + $prime = $primelow + $rand + $rand; + croak "random prime failure, $prime > $high" if $prime > $high; + if ($prime <= 23) { + $prime = 2 if $prime == 1; # special case for low = 2 + next unless (0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1)[$prime]; + return $prime; + } + next if !($prime % 3) || !($prime % 5) || !($prime % 7) || !($prime % 11); + # It looks promising. Check it. + next unless is_prob_prime($prime); + return $prime; + } + croak "Random function broken?"; + } + + # By checking a wheel 30 mod, we can skip anything that would be a multiple + # of 2, 3, or 5, without even having to create the bigint prime. + my @w30 = (1,0,5,4,3,2,1,0,3,2,1,0,1,0,3,2,1,0,1,0,3,2,1,0,5,4,3,2,1,0); + my $primelow30 = $primelow % 30; + $primelow30 = int($primelow30->bstr) if ref($primelow30) eq 'Math::BigInt'; + + # Big GCD's are hugely fast with GMP or Pari, but super slow with Calc. + if ($_big_gcd_use < 0) { + $_big_gcd_use = 0; + my $lib = Math::BigInt->config()->{lib}; + $_big_gcd_use = 1 if $lib =~ /^Math::BigInt::(GMP|Pari)/; + _make_big_gcds() if $_big_gcd_use; + } + + while ($loop_limit-- > 0) { + my $rand = $irandf->($partsize); + # Check wheel-30 mod + my $rand30 = $rand % 30; + next if $w30[($primelow30 + 2*$rand30) % 30] + && ($rand > 3 || $primelow > 5); + # Construct prime + $prime = $primelow + $rand + $rand; croak "random prime failure, $prime > $high" if $prime > $high; - croak "Random function broken?" if $loop_limit-- < 0; - # If we are a small int, then some mods are good. - # If we're a bigint and have MPU:GMP installed then everything here is - # wasteful. If we're a bigint without MPU:GMP, then a bgcd is faster. - next if $prime > 11 && (!($prime % 3) || !($prime % 5) || !($prime % 7) || !($prime % 11)); - do { $prime = 2; last; } if $prime == 1; # special case for low = 2 - last if is_prob_prime($prime); + if ($prime <= 23) { + $prime = 2 if $prime == 1; # special case for low = 2 + next unless (0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1)[$prime]; + return $prime; + } + # Perform quick trial division + next unless Math::BigInt::bgcd($prime, 7436429) == 1; + if ($_big_gcd_use && $prime > $_big_gcd_top) { + next unless Math::BigInt::bgcd($prime, $_big_gcd[0]) == 1; + next unless Math::BigInt::bgcd($prime, $_big_gcd[1]) == 1; + next unless Math::BigInt::bgcd($prime, $_big_gcd[2]) == 1; + next unless Math::BigInt::bgcd($prime, $_big_gcd[3]) == 1; + } + # It looks promising. Check it. + next unless is_prob_prime($prime); + return $prime; } - return $prime; + croak "Random function broken?"; }; + # Cache of tight bounds for each digit. Helps performance a lot. my @_random_ndigit_ranges = (undef, [2,7], [11,97] ); my @_random_nbit_ranges = (undef, undef, [2,3],[5,7] ); @@ -540,24 +691,37 @@ sub primes { sub random_ndigit_prime { my($digits) = @_; - my $usebigint = ( defined $bigint::VERSION - || (defined $digits && ref($digits) =~ /^Math::Big/)); - _validate_positive_integer($digits, 1, $usebigint ? undef : $_Config{'maxbits'}); + _validate_positive_integer($digits, 1); + + my $bigdigits = $digits >= $_Config{'maxdigits'}; + + if ($bigdigits && $_Config{'nobigint'}) { + _validate_positive_integer($digits, 1, $_Config{'maxdigits'}); + # Special case for nobigint and threshold digits + if (!defined $_random_ndigit_ranges[$digits]) { + my $low = int(10 ** ($digits-1)); + my $high = ~0; + $_random_ndigit_ranges[$digits] = [next_prime($low),prev_prime($high)]; + } + } if (!defined $_random_ndigit_ranges[$digits]) { - if ( $usebigint && $digits >= $_Config{'maxdigits'} ) { + if ($bigdigits) { + if (!defined $Math::BigInt::VERSION) { + eval { require Math::BigInt; Math::BigInt->import(try=>'GMP,Pari'); 1; } + or do { croak "Cannot load Math::BigInt"; }; + } my $low = Math::BigInt->new('10')->bpow($digits-1); my $high = Math::BigInt->new('10')->bpow($digits); # Just pull the range in to the nearest odd. $_random_ndigit_ranges[$digits] = [$low+1, $high-1]; } else { my $low = int(10 ** ($digits-1)); - my $high = int(10 ** int($digits)); - # Note: Perl 5.6.2 cannot represent 10**15 as an integer, so things will - # crash all over the place if you try. We can stringify it, but then it - # starts failing math tests later. - $high = ~0 if $high > ~0; - $_random_ndigit_ranges[$digits] = [next_prime($low), prev_prime($high)]; + my $high = int(10 ** $digits); + # Note: Perl 5.6.2 cannot represent 10**15 as an integer, so things + # will crash all over the place if you try. We can stringify it, but + # will just fail tests later. + $_random_ndigit_ranges[$digits] = [next_prime($low),prev_prime($high)]; } } my ($low, $high) = @{$_random_ndigit_ranges[$digits]}; @@ -566,12 +730,15 @@ sub primes { sub random_nbit_prime { my($bits) = @_; - my $usebigint = ( defined $bigint::VERSION - || (defined $bits && ref($bits) =~ /^Math::Big/)); - _validate_positive_integer($bits, 2, $usebigint ? undef : $_Config{'maxbits'}); + _validate_positive_integer($bits, 2); if (!defined $_random_nbit_ranges[$bits]) { - if ( $usebigint && $bits >= $_Config{'maxbits'} ) { + my $bigbits = $bits > $_Config{'maxbits'}; + if ($bigbits) { + if (!defined $Math::BigInt::VERSION) { + eval { require Math::BigInt; Math::BigInt->import(try=>'GMP,Pari'); 1; } + or do { croak "Cannot load Math::BigInt"; }; + } my $low = Math::BigInt->new('2')->bpow($bits-1); my $high = Math::BigInt->new('2')->bpow($bits); # Don't pull the range in to primes, just odds @@ -581,7 +748,7 @@ sub primes { my $high = ($bits == $_Config{'maxbits'}) ? ~0-1 : ~0 >> ($_Config{'maxbits'} - $bits); - $_random_nbit_ranges[$bits] = [next_prime($low-1), prev_prime($high+1)]; + $_random_nbit_ranges[$bits] = [next_prime($low-1),prev_prime($high+1)]; # Example: bits = 7. # low = 1<<6 = 64. next_prime(64-1) = 67 # high = ~0 >> (64-7) = 127. prev_prime(127+1) = 127 @@ -593,31 +760,36 @@ sub primes { sub random_maurer_prime { my($k) = @_; - my $usebigint = ( defined $bigint::VERSION - || (defined $k && ref($k) =~ /^Math::Big/)); - _validate_positive_integer($k, 2, $usebigint ? undef : $_Config{'maxbits'}); + _validate_positive_integer($k, 2); - my $p0 = 32; # Use uniform random method for this many or less + # Results for random_nbit_prime are proven for all native bit sizes. We + # could go even higher if we used is_provable_prime or looked for is_prime + # returning 2. + my $p0 = $_Config{'maxbits'}; return random_nbit_prime($k) if $k <= $p0; if (!defined $Math::BigInt::VERSION) { - eval { require Math::BigInt; Math::BigInt->import(try=>'GMP,Pari'); 1; } + eval { require Math::BigInt; Math::BigInt->import(try=>'GMP,Pari'); 1; } or do { croak "Cannot load Math::BigInt"; }; } if (!defined $Math::BigFloat::VERSION) { - eval { require Math::BigFloat; Math::BigFloat->import(); 1; } + eval { require Math::BigFloat; Math::BigFloat->import(); 1; } or do { croak "Cannot load Math::BigFloat"; }; } + my $verbose = $_Config{'verbose'}; + local $| = 1 if $verbose > 2; + my $c = Math::BigFloat->new("0.065"); # higher = more trial divisions my $r = Math::BigFloat->new("0.5"); # relative size of the prime q - my $m = 24; # How much randomness we're trying to get at a time + my $m = 20; # makes sure R is big enough my $B = ($c * $k * $k)->bfloor; - # Generate a random prime q of size $r*$k, where $r >= 0.5. - # Select r so it more-or-less matches the size of a typical random factor. + # Generate a random prime q of size $r*$k, where $r >= 0.5. Try to + # cleverly select r to match the size of a typical random factor. if ($k > 2*$m) { + my $rand_max_val = 1 << $irandmaxbitsf->(); do { my $s = Math::BigFloat->new($irandf->($rand_max_val)) ->bdiv($rand_max_val); @@ -627,105 +799,118 @@ sub primes { # I've seen +0, +1, and +2 here. Maurer uses +0. Menezes uses +1. my $q = random_maurer_prime( ($r * $k)->bfloor + 1 ); - #warn "B = $B r = $r k = $k q = $q\n"; my $I = Math::BigInt->new(2)->bpow($k-1)->bdiv(2 * $q)->bfloor; - #warn "I = $I\n"; - - my $primemult; + print "B = $B r = $r k = $k q = $q I = $I\n" if $verbose; + + # Big GCD's are hugely fast with GMP or Pari, but super slow with Calc. + if ($_big_gcd_use < 0) { + $_big_gcd_use = 0; + my $lib = Math::BigInt->config()->{lib}; + $_big_gcd_use = 1 if $lib =~ /^Math::BigInt::(GMP|Pari)/; + _make_big_gcds() if $_big_gcd_use; + } while (1) { # R is a random number between $I+1 and 2*$I - my $R = $I + 1 + $get_rand_range->( int($I - 1) ); - my $n = 2 * $R * $q + 1; + my $R = $I + 1 + $get_rand_range->( $I - 1 ); + #my $n = 2 * $R * $q + 1; + my $n = Math::BigInt->new(2)->bmul($R)->bmul($q)->badd(1); # We constructed a promising looking $n. Now test it. - - # Weed out non-primes as fast as we can. First test for 3,5,7,11,13 - next unless Math::BigInt::bgcd($n, 15015) == 1; - # Then do either MR or primes up to B (using a gcd) + print "." if $verbose > 2; + + # Trial divisions, trying to quickly weed out non-primes. + next unless Math::BigInt::bgcd($n, 111546435) == 1; + if ($_big_gcd_use && $n > $_big_gcd_top) { + next unless Math::BigInt::bgcd($n, $_big_gcd[0]) == 1; + next unless Math::BigInt::bgcd($n, $_big_gcd[1]) == 1; + next unless Math::BigInt::bgcd($n, $_big_gcd[2]) == 1; + next unless Math::BigInt::bgcd($n, $_big_gcd[3]) == 1; + } + print "+" if $verbose > 2; if ($_HAVE_GMP) { - next unless Math::Prime::Util::GMP::is_strong_pseudoprime($n, 2, 7); - } else { - if (!defined $primemult) { - $primemult = Math::BigInt->bone; - foreach my $p (@{primes(17,$B)}) { $primemult *= $p; } - } - next unless Math::BigInt::bgcd($n, $primemult) == 1; + next unless Math::Prime::Util::GMP::is_strong_pseudoprime($n, 2); } - #warn "$n passes trial division\n"; + print "*" if $verbose > 2; # Now we do Lemma 1 -- a special case of the Pocklington test. # Let F = q where q is prime, and n = 2RF+1. # If F > sqrt(n) or F odd and F > R, and a^((n-1)/F)-1 mod n = 1, n prime. - # a is a random number between 2 and $n-2 - my $a = 2 + $get_rand_range->( $n - 4 ); - my $b = $a->copy->bmodpow($n-1, $n); - next unless $b == 1; - #warn "$n passes a^n-1 == 1\n"; - - # This is Lemma 1 from Maurer's paper. Also see Menezes 4.59. - # First let's double check our conditions. This really isn't necessary - # as we went to a lot of trouble to ensure these hold when we get here. - # Menezes doesn't bother, but I want to check. - next if $q <= $R || ($q % 2) == 0; - - # Given the above, we have only one GCD to check and we're done! - $b = $a->copy->bmodpow(2*$R, $n); - next unless Math::BigInt::bgcd($b-1, $n) == 1; - #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 (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->broot(3); - # 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->bsqrt->bfloor->bpow(2) == $z; # perfect square - # Menezes seems to imply only the q test needs to be done, but that - # doesn't follow from Lemma 2. Here's my problem: with q > R we'll - # end up with x=0 most of the time, hence z will be a perfect square. - - # We perhaps could verify with a BPSW test on the result. This could: - # 1) save us from accidently outputing a non-prime due to some mistake - # 2) make history by finding the first known BPSW pseudo-prime - croak "Maurer prime $n=2*$R*$q+1 failed BPSW" unless is_prob_prime($n); - #warn " and passed BPSW.\n"; - - return $n; + # Based on our construction, this should always be true. Check anyway. + next unless $q > $R; + + # Select random 'a' values. If n is prime, then almost any 'a' value + # will work, so just try two small ones instead of generating a giant + # random 'a' between 2 and n-2. This makes the powmods run faster. + foreach my $try_a (2, 7) { + # my $a = 2 + $get_rand_range->( $n - 4 ); + my $a = Math::BigInt->new($try_a); + my $b = $a->copy->bmodpow($n-1, $n); + next unless $b == 1; + + # Now do the one gcd check we need to do. + $b = $a->copy->bmodpow(2*$R, $n); + next unless Math::BigInt::bgcd($b-1, $n) == 1; + print "$n passed final gcd\n" if $verbose > 2; + + # Instead of the previous gcd, we could check q >= n**1/3 and also do + # 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 do the x/y tests. + # next if ($q <= $n->copy->broot(3)); + # 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->bsqrt->bfloor->bpow(2) == $z; # perfect square + # Menezes seems to imply only the q test needs to be done, but this + # doesn't follow from Lemma 2. Also note the entire POINT of going to + # Lemma 2 is that we now allow r to be 0.3334, making q smaller. If we + # run this without changing r, then x will typically be 0 and this fails. + + # Verify with a BPSW test on the result. This could: + # 1) save us from accidently outputing a non-prime due to some mistake + # 2) make history by finding the first known BPSW pseudo-prime + croak "Maurer prime $n=2*$R*$q+1 failed BPSW" unless is_prob_prime($n); + + return $n; + } + # Didn't pass the selected a values. Try another R. } - } -} + } # End of random_maurer_prime + +} # end of the random prime section sub primorial { my($n) = @_; _validate_positive_integer($n); - if ($_HAVE_GMP && defined &Math::Prime::Util::GMP::primorial) { - return (ref($_[0]) eq 'Math::BigInt') - ? $_[0]->copy->bzero->badd( Math::Prime::Util::GMP::primorial($n) ) - : Math::Prime::Util::GMP::primorial($n); - } - my $pn = 1; - $pn = Math::BigInt->new->bone if defined $Math::BigInt::VERSION && - $n >= (($_Config{'maxbits'} == 32) ? 29 : 53); + if ($n >= (($_Config{'maxbits'} == 32) ? 29 : 53)) { + if (!defined $Math::BigInt::VERSION) { + eval { require Math::BigInt; Math::BigInt->import(try=>'GMP,Pari'); 1; } + or do { croak "Cannot load Math::BigInt"; }; + } + $pn = Math::BigInt->bone(); + } + # Make sure we use their type if they passed one in. + $pn = $_[0]->copy->bone() if ref($_[0]) eq 'Math::BigInt'; - foreach my $p ( @{ primes($n) } ) { - $pn *= $p; + if ($_HAVE_GMP && defined &Math::Prime::Util::GMP::primorial) { + if (ref($pn) eq 'Math::BigInt') { + $pn->bzero->badd( Math::Prime::Util::GMP::primorial($n) ); + } else { + $pn = int( Math::Prime::Util::GMP::primorial($n) ); + } + } else { + foreach my $p ( @{ primes($n) } ) { + $pn *= $p; + } } return $pn; } sub pn_primorial { my($n) = @_; - if ($_HAVE_GMP && defined &Math::Prime::Util::GMP::pn_primorial) { - return (ref($_[0]) eq 'Math::BigInt') - ? $_[0]->copy->bzero->badd( Math::Prime::Util::GMP::pn_primorial($n) ) - : Math::Prime::Util::GMP::pn_primorial($n); - } return primorial(nth_prime($n)); } @@ -1507,7 +1692,7 @@ Math::Prime::Util - Utilities related to prime numbers, including fast sieves an =head1 VERSION -Version 0.14 +Version 0.15 =head1 SYNOPSIS @@ -2063,8 +2248,8 @@ definition. primorial(0) == 1 primorial($n) == pn_primorial( prime_count($n) ) -The result will be calculated using native numbers if neither bigint nor -L<Math::Prime::Util::GMP> are loaded. +The result will be a L<Math::BigInt> object if it is larger than the native +bit size. Be careful about which version (C<primorial> or C<pn_primorial>) matches the definition you want to use. Not all sources agree on the terminology, though @@ -2086,8 +2271,8 @@ definition. pn_primorial(0) == 1 pn_primorial($n) == primorial( nth_prime($n) ) -The result will be calculated using native numbers if neither bigint nor -L<Math::Prime::Util::GMP> are loaded. +The result will be a L<Math::BigInt> object if it is larger than the native +bit size. =head2 random_prime @@ -2102,29 +2287,46 @@ range. The L<rand> function is called one or more times for selection. The goal is to return a uniform distribution of the primes in the range, meaning for each prime in the range, the chances are equally likely that it -will be seen. - -The current algorithm does a random index selection for small numbers, which -is deterministic. For larger numbers, this slows down, so for 32-bit ranges, -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. +will be seen. This is removes from consideration such algorithms as +C<PRIMEINC>, which although efficient, gives very non-random output. + +For small numbers, a random index selection is done, which gives ideal +uniformity and is very efficient with small inputs. For ranges larger than +this ~16-bit threshold but small enough to be handled by a single C<rand> +call, a Monte Carlo method is used. This also gives ideal uniformity and +can be very fast for reasonably sized ranged. For even larger numbers, we +partition the range, choose a random partition, then select a random prime +from the partition. This gives some loss of uniformity but results in many +fewer bits of randomness being consumed as well as being much faster. 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. -Examples: +return a float value between 0 and 1-epsilon, with at least 31 bits of +randomness. Examples: - # Use Mersenne Twister + # Math::Random::Secure. Very good, uses ISAAC and strong seed methods. + use Math::Random::Secure qw/rand/; + + # Bytes::Random::Secure. Also uses ISAAC and strong seed methods. + use Bytes::Random::Secure qw/random_bytes/; + sub rand { return unpack("L", random_bytes(4))/4294967296; } + + # Crypt::Random. Uses Pari and /dev/random. + use Crypt::Random qw/makerandom/; + sub rand { return makerandom(Size=>32,Uniform=>1)->pari2nv/4294967296; } + + # Mersenne Twister. Very fast, decent RNG, auto seeding. use Math::Random::MT::Auto qw/rand/; - # Use a custom random function - sub rand { ... } + # A custom random function + sub rand { ... do your own cool stuff here ... } -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. +For cryptographically secure primes, you need to use something better than the +default for both seeding and random number generation. I would recommend +using L<Math::Random::Secure> and also installing L<Math::Random::ISAAC::XS> +if possible. It is fast and does everything needed by default. If you want +to know more, I recommend reading the documentation for L<Math::Random::Secure> +and L<Bytes::Random::Secure>. =head2 random_ndigit_prime @@ -2137,10 +2339,15 @@ digits between 1 and the maximum native type (10 for 32-bit, 20 for 64-bit, (e.g. 1000 - 9999 for 4-digits) will be uniformly selected using the L<rand> function as described above. +If the number of digits is greater than or equal to the maximum native type, +then the result will be returned as a BigInt. However, if the '-nobigint' +tag was used, then numbers larger than the threshold will be flagged as an +error, and numbers on the threshold will be restricted to native numbers. + =head2 random_nbit_prime - use bigint; my $bigprime = random_nbit_prime(512); + my $bigprime = random_nbit_prime(512); Selects a random n-bit prime, where the input is an integer number of bits between 2 and the maximum representable bits (32, 64, or 100000 for native @@ -2152,36 +2359,68 @@ Since this uses the random_prime function, all uniformity properties of that function apply to this. The n-bit range is partitioned into nearly equal segments less than C<2^31>, a segment is randomly selected, then the trivial Monte Carlo algorithm is used to select a prime from within the segment. -This gives a nearly uniform distribution, doesn't use excessive random source, -and can be very fast. +This gives a reasonably uniform distribution, doesn't use excessive random +source, and can be very fast. -Note that if you use Perl default bigints, it can get very slow as the -number of bits increases. Either use Math::BigInt::GMP or install -L<Math::Prime::Util::GMP>, which can make it run B<much> faster. +The result will be a BigInt if the number of bits is greater than the native +bit size. For better performance with very large bit sizes, install +L<Math::BigInt::GMP>. =head2 random_maurer_prime - use bigint; my $bigprime = random_maurer_prime(512); + my $bigprime = random_maurer_prime(512); + +Construct an n-bit provable prime, using the FastPrime algorithm of +Ueli Maurer (1995). This is the same algorithm used by L<Crypt::Primes>. +Similar to L</"random_nbit_prime">, the result will be a BigInt if the +number of bits is greater than the native bit size. -Construct an n-bit provable prime, using the algorithm of Ueli Maurer (1995). -This is the same algorithm used by L<Crypt::Primes>. +For cryptographic purposes you need to ensure you're using a good RNG that +is well seeded. See the notes for L</"random_prime">. The differences between this function and that in L<Crypt::Primes> include -(1) the current version of C::P has been in use for 9 years, while M::P::U -is new and relatively untested; -(2) no external libraries are needed for this module, while C::P requires -L<Math::Pari>; -(3) C::P is quite fast for all sizes -- M::P::U is really -fast for native bit sizes, so-so for large bit sizes when -L<Math::Prime::Util::GMP> is installed, but ridiculously slow when using -native Perl bigints for large bit sizes; -(4) 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; -(5) C::P has some useful options for cryptography; -(6) C::P is hardcoded to use L<Crypt::Random>, while this function will use -whatever you set C<rand> to (this is more flexible but also prone to misuse). + +=over + +=item * + +Version 0.50 of Crypt::Primes can return composites. + +=item * + +Version 0.50 of Crypt::Primes uses the C<PRIMEINC> algorithm for the base +generator, which gives a very non-uniform distribution. This differs +from Maurer's algorithm which uses the Monte Carlo algorithm (which is what +this module uses). + +=item * + +No external libraries are needed for this module, while C::P requires +L<Math::Pari>. See the next item however. + +=item * + +Crypt::Primes is quite fast for all sizes since it uses Pari for all heavy +lifting. M::P::U is really fast for native bit sizes. It is similar speed +to Crypt::Primes if the BigInt package in use is GMP or Pari, e.g. + + use Math::BigInt lib=>'GMP'; + +but a lot slower without. Having the L<Math::Prime::Util::GMP> module +installed helps in any case. + +=item * + +Crypt::Primes has some useful options for cryptography. + +=item * + +Crypt::Primes is hardcoded to use L<Crypt::Random>, while this function will +use whatever you set C<rand> to. This is more flexible but also prone to +misuse. You ought to use something like L<Math::Random::Secure>. + +=back Any feedback on this function would be greatly appreciated. @@ -2399,7 +2638,7 @@ C<accuracy()> value of the input (or the default BigFloat accuracy, which is 40 by default). MPFR is used for positive inputs only. If Math::MPFR is not installed or the -input is negative, then other methods are used: +input is negative, then other methods are used: continued fractions (C<x E<lt> -1>), rational Chebyshev approximation (C< -1 E<lt> x E<lt> 0>), a convergent series (small positive C<x>), -- 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