This is an automated email from the git hooks/post-receive script. ppm-guest pushed a commit to annotated tag v0.14 in repository libmath-prime-util-perl.
commit 2ffb217b6d4d05928297c1871553ed4cafc6961f Author: Dana Jacobsen <d...@acm.org> Date: Thu Nov 29 13:49:49 2012 -0800 Speed up PP Lehmer by 10-100x at the expense of memory --- Changes | 26 ++++++++++++++++------- lib/Math/Prime/Util/PP.pm | 53 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/Changes b/Changes index f309135..390ef11 100644 --- a/Changes +++ b/Changes @@ -2,16 +2,14 @@ Revision history for Perl extension Math::Prime::Util. 0.14 ?? November 2012 - - Clean up some tests. A lot of tests moved from testing 3000 cases as - separate tests, to putting everything in arrays and using is_deeply. - Output should still be specific, but it is a huge speedup on some - platforms (e.g. Cygwin on a netbook). + - Compilation and test issues: + Fix compilation on NetBSD + Try to fix compilation on Win32 + MSVC + Speed up some testing, helps a lot with Cygwin on slow machines + Speed up a lot of slow PP areas, especially used by test suite - XS AKS extended from half-word to full-word. - - Moved bignum Zeta and R to separate file, only loaded when needed. - Helpful to get the big rarely-used tables out of the main loading. - - Add functions: jordan_totient generalization of Euler Totient divisor_sum run coderef for every divisor @@ -20,7 +18,19 @@ Revision history for Perl extension Math::Prime::Util. GMP support respectively if they are defined and equal to 1. - Lehmer prime count for Pure Perl code, including use in nth_prime. - Almost 10x speedup on test suite if using only Pure Perl. + prime count 10^9 using sieve: + 71.9s PP sieve + 0.47s XS sieve + prime count 10^9 using Lehmer: + 0.70s PP lehmer + 0.03s XS lehmer + + - Moved bignum Zeta and R to separate file, only loaded when needed. + Helpful to get the big rarely-used tables out of the main loading. + + - Quote arguments to Math::Big{Int,Float} in a few places it wasn't. + Math::Big* coerces the input to a signed value if it isn't a string, + which causes us all sorts of grief. 0.13 19 November 2012 diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm index 73569c8..ba1f96d 100644 --- a/lib/Math/Prime/Util/PP.pm +++ b/lib/Math/Prime/Util/PP.pm @@ -438,6 +438,20 @@ sub _sieve_prime_count { return $count; } +sub _count_with_sieve { + my ($sref, $high) = @_; + return (0,0,1,2,2,3,3)[$high] if $high < 7; + $high-- if ($high % 2) == 0; # Make high go to odd number. + my $send = ($high >> 1) + 1; + + if ( !defined $sref || $send > length($$sref) ) { + # We could take the full count of $sref, then segment sieve to high. + $sref = _sieve_erat($high); + return 1 + $$sref =~ tr/0//; + } + return 1 + substr($$sref, 0, $send) =~ tr/0//; +} + sub _lehmer_pi { my $x = shift; return _sieve_prime_count($x) if $x < 1_000; @@ -447,23 +461,28 @@ sub _lehmer_pi { my $c = _lehmer_pi(int($x**(1/3)+0.5)); # Generate at least b primes. - my $nth_prime_upper = ($b <= 10) ? 29 : int($b*(log($b) + log(log($b)))) + 1; - my $primes = primes( $nth_prime_upper ); + my $bth_prime_upper = ($b <= 10) ? 29 : int($b*(log($b) + log(log($b)))) + 1; + my $primes = primes( $bth_prime_upper ); my $sum = int(($b + $a - 2) * ($b - $a + 1) / 2); $sum += _legendre_phi($x, $a, $primes); - # Make sure these calls are fast. - # 1M for 10**8, 32M for 10**10, 5600M for 10**12 - prime_precalc( int($x / $primes->[$a]) ); + # Get a big sieve for our primecounts. The C code uses b*16 as a compromise, + # as that cuts out all the inner loop sieves and about half the outer loop. + # It also takes good advantage of segment sieving for the big outer counts. + # We'll just go ahead and sieve everything we need now. This is really much + # more than we should use, but it saves a _huge_ amount of time given we're + # not using a segment sieve for the outer loop. + #my $sref = _sieve_erat($b * 16); + my $sref = _sieve_erat( int($x / $primes->[$a]) ); foreach my $i ($a+1 .. $b) { my $w = int($x / $primes->[$i-1]); - $sum = $sum - _sieve_prime_count($w); + $sum = $sum - _count_with_sieve($sref,$w); if ($i <= $c) { - my $bi = _sieve_prime_count(int(sqrt($w)+0.5)); + my $bi = _count_with_sieve($sref,int(sqrt($w)+0.5)); foreach my $j ($i .. $bi) { - $sum = $sum - _sieve_prime_count(int($w / $primes->[$j-1])) + $j - 1; + $sum = $sum - _count_with_sieve($sref,int($w / $primes->[$j-1])) + $j - 1; } } } @@ -492,8 +511,8 @@ sub prime_count { if ( ref($low) eq 'Math::BigInt' || ref($high) eq 'Math::BigInt' || ($high-$low) < 10 - || ($high-$low) < int($low/1_000_000) ) { - # Trial primes seems best. + || ($high-$low) < int($low/100_000_000_000) ) { + # Trial primes seems best. Needs some tuning. my $curprime = next_prime($low-1); while ($curprime <= $high) { $count++; @@ -674,6 +693,11 @@ sub miller_rabin { return 1 if ($n == 2) || ($n == 3); return 0 if !($n % 2); + # Die on invalid bases + do { croak "Base $_ is invalid" if $_ < 2 } for (@bases); + # Make sure we handle big bases ok. + @bases = grep { $_ > 1 } map { ($_ >= $n) ? $_ % $n : $_ } @bases; + if ( ref($n) eq 'Math::BigInt' ) { my $s = 0; @@ -685,7 +709,6 @@ sub miller_rabin { } foreach my $a (@bases) { - croak "Base $a is invalid" if $a < 2; my $x = $n->copy->bzero->badd($a)->bmodpow($d,$n); next if ($x->is_one) || ($x->bcmp($nminus1) == 0); foreach my $r (1 .. $s) { @@ -710,7 +733,6 @@ sub miller_rabin { if ($n < $_half_word) { foreach my $a (@bases) { - croak "Base $a is invalid" if $a < 2; my $x = _native_powmod($a, $d, $n); next if ($x == 1) || ($x == ($n-1)); foreach my $r (1 .. $s) { @@ -725,7 +747,6 @@ sub miller_rabin { } } else { foreach my $a (@bases) { - croak "Base $a is invalid" if $a < 2; my $x = _powmod($a, $d, $n); next if ($x == 1) || ($x == ($n-1)); @@ -850,7 +871,7 @@ sub is_strong_lucas_pseudoprime { if (ref($n) ne 'Math::BigInt') { require Math::BigInt; - $n = Math::BigInt->new($n); + $n = Math::BigInt->new("$n"); } my $m = $n->copy->badd(1); @@ -1008,7 +1029,7 @@ sub is_aks_prime { # limit = floor( log2(n) * log2(n) ). o_r(n) must be larger than this my $sqrtn = int(Math::BigFloat->new($n)->bsqrt->bfloor->bstr); - my $log2n = Math::BigFloat->new($n)->blog(2); + my $log2n = Math::BigFloat->new("$n")->blog(2); my $log2_squared_n = $log2n * $log2n; my $limit = int($log2_squared_n->bfloor->bstr); @@ -1028,7 +1049,7 @@ sub is_aks_prime { return 1 if $r >= $n; # Since r is a prime, phi(r) = r-1 - my $rlimit = int( Math::BigFloat->new($r)->bsub(1) + my $rlimit = int( Math::BigFloat->new("$r")->bsub(1) ->bsqrt->bmul($log2n)->bfloor->bstr); $_poly_bignum = 1; -- 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