This is an automated email from the git hooks/post-receive script. ppm-guest pushed a commit to annotated tag v0.17 in repository libmath-prime-util-perl.
commit e6b826cc7ce29809069557f217645040f3e728b7 Author: Dana Jacobsen <d...@acm.org> Date: Thu Dec 20 00:22:25 2012 -0800 Revamp rand internals yet again. Sadly also a rand API change. --- Changes | 2 + TODO | 6 +- lib/Math/Prime/Util.pm | 255 ++++++++++++++++++++++++++++++------------------- 3 files changed, 162 insertions(+), 101 deletions(-) diff --git a/Changes b/Changes index bff55b0..61b3475 100644 --- a/Changes +++ b/Changes @@ -10,6 +10,8 @@ Revision history for Perl extension Math::Prime::Util. practical application now that we use Lehmer's method for counts, but there are some cases that can still show speedups. + - Changed the rand functionality yet again. Sorry. + 0.16 11 December 2012 - randbits >= 32 on some 32-bit systems was messing us up. Restrict our diff --git a/TODO b/TODO index 76f5081..55b7ef0 100644 --- a/TODO +++ b/TODO @@ -41,5 +41,7 @@ - Dynamically use a mulmodadd in PP aks, just like the new C code does. This will mean it'll work for full-size native ints. -- Add configuration options for rand and randbits (maybe irand and irandrange). - This will help when being used as part of a library. +- Perl's rand() is a mess. I believe a decent workaround is to include + tinymt32, seed it using the system rand (multiple calls, just use 8-bits + each), then use it to get 32-bit irands. This would only be used if they + didn't give us a RNG (so they don't care about strict crypto). diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm index a1e889a..d196d5f 100644 --- a/lib/Math/Prime/Util.pm +++ b/lib/Math/Prime/Util.pm @@ -125,6 +125,7 @@ if ($_Config{'maxbits'} == 32) { } $_Config{'assume_rh'} = 0; $_Config{'verbose'} = 0; +$_Config{'irand'} = undef; # used for code like: # return _XS_foo($n) if $n <= $_XS_MAXVAL @@ -186,6 +187,9 @@ sub prime_set_config { $_HAVE_GMP = $_Config{'gmp'}; } elsif ($param eq 'nobigint') { $_Config{'nobigint'} = ($value) ? 1 : 0; + } elsif ($param eq 'irand') { + croak "irand must supply a sub" unless ref($value) eq 'CODE'; + $_Config{'irand'} = $value; } elsif ($param =~ /^(assume[_ ]?)?[ge]?rh$/ || $param =~ /riemann\s*h/) { $_Config{'assume_rh'} = ($value) ? 1 : 0; } elsif ($param eq 'verbose') { @@ -386,17 +390,19 @@ sub primes { # - If using system rand, is RANDBITS large? # - What RNG? # +# Timings using Math::BigInt::GMP, x86_64, system rand with 32+ randbits. +# # 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 +# 24-bit 25uS same same same +# 64-bit 87uS same same same +# 128-bit 0.032s 0.0049s 0.098s 0.056s +# 256-bit 0.062s 0.0097s 0.25s 0.15s +# 512-bit 0.13s 0.019s 0.65s 0.30s +# 1024-bit 0.28s 0.058s 1.3s 0.94s +# 2048-bit 0.91s 0.4s 3.2s 3.1s +# 4096-bit 6.6s 4.0s 23s 12.0s # # 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 @@ -407,15 +413,22 @@ sub primes { # 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. +# 0.032 system rand +# 0.20 Math::Random::MT::Auto +# 0.96 Math::Random::Secure irand (with Math::Random::ISAAC::XS) +# 1.71 Math::Random::Secure (with Math::Random::ISAAC::XS) +# 1.90 *Bytes::Random::Secure irand (with Math::Random::ISAAC::XS) +# 2.40 Math::Random::Secure irand +# 3.37 Math::Random::Secure +# 2.21 *Bytes::Random::Secure (with Math::Random::ISAAC::XS) +# 3.32 *Bytes::Random::Secure irand +# 3.62 *Bytes::Random::Secure +# 123.8 *Crypt::Random irand +# BRS: sub irand { return unpack("L", random_bytes(4)); } +# sub rand {return ($_[0]||1)*(unpack("L",random_bytes(4))/4294967296.0)} +# CR: makerandom(Size=>32, Uniform=>1, Strength=>0) +# haveged daemon running to stop /dev/random blocking +# Both BRS and CR have more features that this isn't measuring. # # 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;' @@ -439,68 +452,81 @@ sub primes { $_big_gcd[3] = $p3 / $p2; } - # Returns a function that will get a uniform random number between [0,$range] - # inclusive. Uses either the system rand or a user defined rand. Will use - # the function directly if possible, and if the range is larger than the - # randomness in a single call, will build up a random number. + # Returns a function that will get a uniform random number between 0 and + # $max inclusive. # - # Relies on rand working like system rand. If you use Math::Random::MT, make - # sure you use version 1.16 or later. + # Relies on rand working like system rand. If you use Math::Random::MT, + # make sure you use version 1.16 or later. sub _get_rand_func { - my $irandf; - if (defined &::rand) { # User-defined rand function - $irandf = sub { - my($range) = @_; - return 0 if $range <= 0; - my $zero = $range - $range; # zero in possible bigint - return $zero+int(::rand($range+1)) if $range < (1 << 31); - my $rbits = 0; - if (ref($range) eq 'Math::BigInt') { - $rbits = length($range->as_bin) - 2; - } else { - my $t = $range; - while ($t) { $rbits++; $t >>= 1; } - } - while (1) { - my $rbitsleft = $rbits; - my $U = $zero; - while ($rbitsleft > 0) { - my $usebits = ($rbitsleft > 31) ? 31 : $rbitsleft; - $U = ($U << $usebits) + int(::rand(1 << $usebits)); - $rbitsleft -= $usebits; - } - return $U if $U <= $range; - } - }; - } else { # System rand function - croak "System rand has too few bits. Use a custom RNG." - if $_Config{'system_randbits'} < 15; - $irandf = sub { - my($range) = @_; - return 0 if $range <= 0; - my $zero = $range - $range; # zero in possible bigint - my $rand_max_bits = $_Config{'system_randbits'}; - return $zero+int(rand($range+1)) if $range < (1 << $rand_max_bits); - my $rbits = 0; - if (ref($range) eq 'Math::BigInt') { - $rbits = length($range->as_bin) - 2; - } else { - my $t = $range; - while ($t) { $rbits++; $t >>= 1; } - } - while (1) { - my $rbitsleft = $rbits; - my $U = $zero; - while ($rbitsleft > 0) { - my $usebits = ($rbitsleft > $rand_max_bits) ? $rand_max_bits : $rbitsleft; - $U = ($U << $usebits) + int(rand(1 << $usebits)); - $rbitsleft -= $usebits; - } - return $U if $U <= $range; - } - }; + if (!defined $_Config{'irand'} && defined &::rand) { + # They have not given us an irand function, but they have their own rand. + $_Config{'irand'} = sub { int(4294967296.0 * ::rand()) }; + } + # We first make a function irandf that returns a 32-bit integer. This + # will be a number uniformly in the range [0, 2^32-1]. This corresponds + # to the irand function of many CPAN modules: + # Math::Random::MT + # Math::Random::ISAAC + # Math::Random::Xorshift + # Math::Random::Secure + # but not: + # Math::Random::MT::Auto (it will return 64-bits) + # + # We try in order: + # 1) the function they gave us via prime_set_config(irand=> \&irand ) + # 2) main::rand(), exportable by many modules + # 3) CORE::rand(). Hopefully one call will work, otherwise use many. + my $irandf = $_Config{'irand'}; + if (!defined $irandf) { + if ($_Config{'system_randbits'} >= 32) { + $irandf = sub { int(4294967296.0 * CORE::rand()) }; + } else { + my $randbits = $_Config{'system_randbits'}; + $irandf = sub { + my $rwords = int((32+$randbits-1)/$randbits); + my $U = 0; + $U = ($U << $randbits) + int((1 << $randbits) * CORE::rand()) + for 1 .. $rwords; + $U &= 0xFFFFFFFF; + return $U; + }; + } } - return $irandf; + # OK, now we have a function irandf. Use it. + my $randf = sub { + my($max) = @_; + return 0 if $max <= 0; + my $range = $max+1; + my $U; + if (ref($range) eq 'Math::BigInt') { + my $zero = $range - $range; + 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 $remainder = $rmax % $range; + do { + $U = $zero; + $U = ($U << 32) + $irandf->() for 1 .. $rwords; + } while $U >= ($rmax - $remainder); + } elsif ($range <= 4294967295) { + my $remainder = 4294967295 % $range; + do { + $U = $irandf->(); + } while $U >= (4294967295 - $remainder); + } else { + croak "randf given max out of bounds: $max" if $range > ~0; + my $remainder = 18446744073709551615 % $range; + do { + $U = ($irandf->() << 32) + $irandf->(); + } while $U >= (18446744073709551615 - $remainder); + } + #return $U % $range; + $U %= $range; + die "random failure: $max $U\n" if $U > $max; + return $U; + }; + return $randf; } # Sub to call with low and high already primes and verified range. @@ -508,6 +534,7 @@ sub primes { my($low,$high) = @_; my $prime; + # irandf->($n) gives numbers in the range [0, $n]. my $irandf = _get_rand_func(); #{ my $bsize = 100; my @bins; my $counts = 10000000; @@ -531,7 +558,7 @@ sub primes { # We're going to look at the odd numbers only. #my $range = $high - $low + 1; - my $oddrange = int(($high - $low) / 2) + 1; + my $oddrange = (($high - $low) >> 1) + 1; # 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 @@ -539,7 +566,7 @@ sub primes { # If the range is reasonably small, generate using simple Monte Carlo # method (aka the 'trivial' method). Completely uniform. - if ($oddrange < $_Config{'maxbits'}) { + if ($oddrange < ~0) { $oddrange = int($oddrange->bstr) if ref($oddrange) eq 'Math::BigInt'; my $loop_limit = 2000 * 1000; # To protect against broken rand if ($low > 11) { @@ -606,7 +633,7 @@ sub primes { # which accounts for the prime distribution. my($binsize, $nparts); - my $rand_part_size = 1 << 31; # Max size we want to use. + my $rand_part_size = 1 << (($_Config{'maxbits'} > 32) ? 32 : 31); if (ref($oddrange) eq 'Math::BigInt') { # Go to some trouble here because some systems are wonky, such as # giving us +a/+b = -r. Also note the quotes for the bigint argument. @@ -622,7 +649,7 @@ sub primes { my $nbins = int($oddrange / $rand_part_size); $nbins++ if $nbins * $rand_part_size != $oddrange; $binsize = int($oddrange / $nbins); - $binsize++ if $binsize*$nbins != $oddrange; + $binsize++ if $binsize * $nbins != $oddrange; $nparts = int($oddrange/$binsize); } $nparts-- if ($nparts * $binsize) == $oddrange; @@ -798,7 +825,7 @@ sub primes { # 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. + # returning 2. This should be reasonably fast to ~128 bits with MPU::GMP. my $p0 = $_Config{'maxbits'}; return random_nbit_prime($k) if $k <= $p0; @@ -812,6 +839,7 @@ sub primes { or do { croak "Cannot load Math::BigFloat"; }; } + # Set verbose to 3 to get pretty output like Crypt::Primes my $verbose = $_Config{'verbose'}; local $| = 1 if $verbose > 2; @@ -834,7 +862,7 @@ sub primes { my $q = random_maurer_prime( ($r * $k)->bfloor + 1 ); $q = Math::BigInt->new("$q") unless ref($q) eq 'Math::BigInt'; my $I = Math::BigInt->new(2)->bpow($k-2)->bdiv($q)->bfloor; - print "B = $B r = $r k = $k q = $q I = $I\n" if $verbose; + print "B = $B r = $r k = $k q = $q I = $I\n" if $verbose && $verbose != 3; # Big GCD's are hugely fast with GMP or Pari, but super slow with Calc. if ($_big_gcd_use < 0) { @@ -886,7 +914,9 @@ sub primes { # 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; + if ($verbose) { + print "", ($verbose == 3) ? "($k)" : "$n passed final gcd\n"; + } # 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). @@ -2328,33 +2358,55 @@ 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. It will be called with an integer argument -between 1 and 2**31, and should return a uniform random value between 0 and -the argument-1. The value may be a float or integer. +If an C<irand> function has been set via L</"prime_set_config">, it will be +used. The function should return a uniformly random 32-bit integer, which +is how the irand functions exported by L<Math::Random::Secure>, +L<Math::Random::MT>, L<Math::Random::ISAAC> and some other modules behave. + +If no C<irand> function was set, then we check if the sub C<main::rand> +exists and use it if so. It will be called with no arguments and should +return a floating point value in the interval [0,1) with 32 bits of entropy. +This allows the C<rand> function from most CPAN modules to be used. + +Lastly, if no C<irand> function was set and no sub <main::rand> exists, then +the system rand will be used. System rand functions are notoriously poor, +and a later version of this module may implement something like TinyMT to +cover the default case. + +Examples of irand functions: # Math::Random::Secure. Uses ISAAC and strong seed methods. Recommended. - use Math::Random::Secure qw/rand/; + use Math::Random::Secure; + prime_set_config(irand => \&Math::Random::Secure::irand); # Bytes::Random::Secure. Also uses ISAAC and strong seed methods. use Bytes::Random::Secure qw/random_bytes/; - sub rand { return ($_[0]||1)*(unpack("L", random_bytes(4))/4294967296); } + prime_set_config(irand => sub { return unpack("L", random_bytes(4)); }); # Crypt::Random. Uses Pari and /dev/random. Very slow. - use Crypt::Random qw/makerandom_itv/; - sub rand { return makerandom_itv(Lower=>0,Upper=>$_[0]); } + use Crypt::Random qw/makerandom/; + prime_set_config(irand => sub { makerandom(Size=>32, Uniform=>1); }); # Mersenne Twister. Very fast, decent RNG, auto seeding. + use Math::Random::MT::Auto; + prime_set_config(irand=>sub {Math::Random::MT::Auto::irand() & 0xFFFFFFFF}); + +Examples of main::rand, where this is done in your script: + + use Math::Random::Secure qw/rand/; + + use Bytes::Random::Secure qw/random_bytes/; + sub rand { ($_[0]||1) * (unpack("L", random_bytes(4))/4294967296.0)} + use Math::Random::MT::Auto qw/rand/; - # A custom random function sub rand { ... do your own cool stuff here ... } 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 reasonably fast and does everything needed by default. If -you want to know more, I recommend reading the documentation for +if possible. It is reasonably fast and does everything needed by default. +For more information, I recommend reading the documentation for L<Math::Random::Secure> and L<Bytes::Random::Secure>. @@ -2386,7 +2438,7 @@ L<rand> function as described above. 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 +segments less than C<2^32>, a segment is randomly selected, then the trivial Monte Carlo algorithm is used to select a prime from within the segment. This gives a reasonably uniform distribution, doesn't use excessive random source, and can be very fast. @@ -2445,8 +2497,8 @@ 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 +Crypt::Primes is hardcoded to use L<Crypt::Random>, while M::P::U allows +plugging in the random function. This is more flexible but also prone to misuse. You ought to use something like L<Math::Random::Secure>. =back @@ -2514,11 +2566,11 @@ the configuration, so changing it has no effect. The settings include: Allows setting of some parameters. Currently the only parameters are: - xs allows turning off the XS code, forcing the Pure Perl code + xs Allows turning off the XS code, forcing the Pure Perl code to be used. Set to 0 to disable XS, set to 1 to re-enable. You probably will never want to do this. - gmp allows turning off the use of L<Math::Prime::Util::GMP>, + gmp Allows turning off the use of L<Math::Prime::Util::GMP>, which means using Pure Perl code for big numbers. Set to 0 to disable GMP, set to 1 to re-enable. You probably will never want to do this. @@ -2530,6 +2582,11 @@ Allows setting of some parameters. Currently the only parameters are: A later version may also have a way to indicate whether no RH, RH, GRH, or ERH is to be assumed. + irand Takes a code ref to an irand function returning a uniform + number between 0 and 2**32-1. This will be used for all + random number generation, and is the preferred way to use + cryptographic RNGs. + =head1 FACTORING FUNCTIONS -- 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