This is an automated email from the git hooks/post-receive script. ppm-guest pushed a commit to annotated tag v0.06 in repository libmath-prime-util-perl.
commit f7169a11b7a4d57c7c08bf4434f912f9bbadc39b Author: Dana Jacobsen <d...@acm.org> Date: Sat Jun 9 05:54:48 2012 -0600 Overflow for nth_prime, segment prime_count --- Changes | 3 ++ TODO | 4 +- XS.xs | 18 ++++++-- t/13-primecount.t | 68 ++++++++++++++++++--------- t/14-nthprime.t | 16 ++++++- util.c | 135 +++++++++++++++++++++++++++++++++++++++++++++--------- util.h | 1 + 7 files changed, 197 insertions(+), 48 deletions(-) diff --git a/Changes b/Changes index 980499a..eaf0d16 100644 --- a/Changes +++ b/Changes @@ -2,6 +2,9 @@ Revision history for Perl extension Math::Prime::Util. 0.05 8 June 2012 - Use assembler for mulmod if 64-bit and gcc and x86 + - nth_prime routines should now all croak on overflow in the same way. + - Segmented prime_count, things like this are efficient: + say prime_count( 10**16, 10**16 + 2**20 ) 0.04 7 June 2012 - Didn't do tests on 32-bit machine before release. Test suite caught diff --git a/TODO b/TODO index 8644218..36bdc9e 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,6 @@ - speed up random_prime for large numbers -- Add other bases to pseudoprime test - - test x64 asm if Perl is built for 32-bit + +- Clean up prime_count ranges. Write tests. diff --git a/XS.xs b/XS.xs index 0188258..a73f3c2 100644 --- a/XS.xs +++ b/XS.xs @@ -21,13 +21,19 @@ void prime_memfree() UV -prime_count(IN UV n) +prime_count(IN UV low, IN UV high = 0) CODE: + if (high == 0) { /* Without a Perl layer in front of this, we'll have */ + high = low; /* the pathological case of a-0 turning into 0-a. */ + low = 0; + } if (GIMME_V == G_VOID) { - prime_precalc(n); + prime_precalc(high); RETVAL = 0; + } else if (low == 0) { + RETVAL = prime_count(high); } else { - RETVAL = prime_count(n); + RETVAL = prime_count_seg(low, high); } OUTPUT: RETVAL @@ -140,6 +146,12 @@ segment_primes(IN UV low, IN UV high, IN UV segment_size = 65536UL) /* To protect vs. overflow, work entirely with d. */ low_d = low / 30; high_d = high / 30; + + { /* Avoid recalculations of this */ + UV endp = (high_d >= (UV_MAX/30)) ? UV_MAX-2 : 30*high_d+29; + prime_precalc( sqrt(endp) + 1 ); + } + while ( low_d <= high_d ) { UV seghigh_d = ((high_d - low_d) < segment_size) ? high_d diff --git a/t/13-primecount.t b/t/13-primecount.t index a0711b7..789b259 100644 --- a/t/13-primecount.t +++ b/t/13-primecount.t @@ -8,30 +8,45 @@ use Math::Prime::Util qw/prime_count prime_count_lower prime_count_upper prime_c my $use64 = Math::Prime::Util::_maxbits > 32; my $extra = defined $ENV{RELEASE_TESTING} && $ENV{RELEASE_TESTING}; -plan tests => 10*3 + ($extra ? 10 : 7) + ($use64 ? 2*9 : 0); +plan tests => 14*3 + ($extra ? 14 : 8) + ($use64 ? 2*18 : 0); +# Powers of 2: http://oeis.org/A007053/b007053.txt +# Powers of 10: http://oeis.org/A006880/b006880.txt my %pivals32 = ( - 1 => 0, - 10 => 4, - 100 => 25, - 1000 => 168, - 10000 => 1229, - 100000 => 9592, - 1000000 => 78498, - 10000000 => 664579, - 100000000 => 5761455, - 1000000000 => 50847534, + 1 => 0, + 10 => 4, + 100 => 25, + 1000 => 168, + 10000 => 1229, + 100000 => 9592, + 1000000 => 78498, + 10000000 => 664579, + 100000000 => 5761455, + 1000000000 => 50847534, + 65535 => 6542, + 16777215 => 1077871, + 2147483647 => 105097565, + 4294967295 => 203280221, ); my %pivals64 = ( - 10000000000 => 455052511, - 100000000000 => 4118054813, - 1000000000000 => 37607912018, - 10000000000000 => 346065536839, - 100000000000000 => 3204941750802, - 1000000000000000 => 29844570422669, - 10000000000000000 => 279238341033925, - 100000000000000000 => 2623557157654233, -1000000000000000000 => 24739954287740860, + 10000000000 => 455052511, + 100000000000 => 4118054813, + 1000000000000 => 37607912018, + 10000000000000 => 346065536839, + 100000000000000 => 3204941750802, + 1000000000000000 => 29844570422669, + 10000000000000000 => 279238341033925, + 100000000000000000 => 2623557157654233, + 1000000000000000000 => 24739954287740860, +10000000000000000000 => 234057667276344607, + 68719476735 => 2874398515, + 1099511627775 => 41203088796, + 17592186044415 => 597116381732, + 281474976710655 => 8731188863470, + 4503599627370495 => 128625503610475, + 72057594037927935 => 1906879381028850, + 1152921504606846975 => 28423094496953330, +18446744073709551615 => 425656284035217743, ); while (my($n, $pin) = each (%pivals32)) { cmp_ok( prime_count_upper($n), '>=', $pin, "Pi($n) <= upper estimate" ); @@ -40,7 +55,7 @@ while (my($n, $pin) = each (%pivals32)) { is( prime_count($n), $pin, "Pi($n) = $pin" ); } my $approx_range = abs($pin - prime_count_approx($n)); - my $range_limit = 1100; + my $range_limit = ($n <= 1000000000) ? 1100 : 70000; cmp_ok( $approx_range, '<=', $range_limit, "prime_count_approx($n) within $range_limit"); } if ($use64) { @@ -50,3 +65,14 @@ if ($use64) { } } + +# TODO: intervals. From primesieve: +# 155428406, // prime count 2^32 interval starting at 10^12 +# 143482916, // prime count 2^32 interval starting at 10^13 +# 133235063, // prime count 2^32 interval starting at 10^14 +# 124350420, // prime count 2^32 interval starting at 10^15 +# 116578809, // prime count 2^32 interval starting at 10^16 +# 109726486, // prime count 2^32 interval starting at 10^17 +# 103626726, // prime count 2^32 interval starting at 10^18 +# 98169972}; // prime count 2^32 interval starting at 10^19 +# Note: ./primesieve 1e10 -o2**32 -c1 diff --git a/t/14-nthprime.t b/t/14-nthprime.t index 1164387..8f35691 100644 --- a/t/14-nthprime.t +++ b/t/14-nthprime.t @@ -8,7 +8,7 @@ use Math::Prime::Util qw/nth_prime nth_prime_lower nth_prime_upper nth_prime_app my $use64 = Math::Prime::Util::_maxbits > 32; my $extra = defined $ENV{RELEASE_TESTING} && $ENV{RELEASE_TESTING}; -plan tests => 7*2 + 9*3 + ($extra ? 9 : 7) + ($use64 ? 9*3 : 0); +plan tests => 7*2 + 9*3 + 7 + ($extra ? 9 : 7) + ($use64 ? 9*3 : 0); my %pivals32 = ( 1 => 0, @@ -26,6 +26,8 @@ while (my($n, $pin) = each (%pivals32)) { cmp_ok( nth_prime($next), '>=', $n, "nth_prime($next) >= $n"); } +# Powers of 10: http://oeis.org/A006988/b006988.txt +# Powers of 2: http://oeis.org/A033844/b033844.txt my %nthprimes32 = ( 1 => 2, 10 => 29, @@ -73,3 +75,15 @@ if ($use64) { } } +my $maxindex = $use64 ? 425656284035217743 : 203280221; +my $maxprime = $use64 ? 18446744073709551557 : 4294967291; +cmp_ok( nth_prime_lower($maxindex), '<=', $maxprime, "nth_prime_lower(maxindex) <= maxprime"); +cmp_ok( nth_prime_upper($maxindex), '>=', $maxprime, "nth_prime_upper(maxindex) >= maxprime"); +cmp_ok( nth_prime_approx($maxindex), '==', $maxprime, "nth_prime_approx(maxindex) == maxprime"); +cmp_ok( nth_prime_lower($maxindex+1), '>=', nth_prime_lower($maxindex), "nth_prime_lower(maxindex+1) >= nth_prime_lower(maxindex)"); +eval { nth_prime_upper($maxindex+1); }; +like($@, qr/overflow/, "nth_prime_upper(maxindex+1) overflows"); +eval { nth_prime_approx($maxindex+1); }; +like($@, qr/overflow/, "nth_prime_approx(maxindex+1) overflows"); +eval { nth_prime($maxindex+1); }; +like($@, qr/overflow/, "nth_prime(maxindex+1) overflows"); diff --git a/util.c b/util.c index f969af5..c924322 100644 --- a/util.c +++ b/util.c @@ -284,7 +284,7 @@ static const double F1 = 1.0; UV prime_count_lower(UV x) { double fx, flogx; - double a = 1.80; + double a = 1.80; /* Dusart 1999, page 14 */ if (x < NPRIME_COUNT_SMALL) return prime_count_small[x]; @@ -314,7 +314,7 @@ UV prime_count_lower(UV x) UV prime_count_upper(UV x) { double fx, flogx; - double a = 2.51; + double a = 2.51; /* Dusart 1999, page 14 */ if (x < NPRIME_COUNT_SMALL) return prime_count_small[x]; @@ -471,42 +471,49 @@ static const unsigned short primes_small[] = 409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499}; #define NPRIMES_SMALL (sizeof(primes_small)/sizeof(primes_small[0])) -/* The nth prime will be more than this number */ +/* The nth prime will be greater than or equal to this number */ UV nth_prime_lower(UV n) { double fn = (double) n; double flogn, flog2n, lower; if (n < NPRIMES_SMALL) - return (n==0) ? 0 : primes_small[n]-1; + return (n==0) ? 0 : primes_small[n]; flogn = log(n); - flog2n = log(flogn); + flog2n = log(flogn); /* Note distinction between log_2(n) and log^2(n) */ - /* Dusart 1999, for all n >= 2 */ + /* Dusart 1999 page 14, for all n >= 2 */ lower = fn * (flogn + flog2n - 1.0 + ((flog2n-2.25)/flogn)); - if (lower > (double)UV_MAX) - return 0; + /* Watch out for overflow */ + if (lower >= (double)UV_MAX) { +#if BITS_PER_WORD == 32 + if (n <= UVCONST(203280221)) return UVCONST(4294967291); +#else + if (n <= UVCONST(425656284035217743)) return UVCONST(18446744073709551557); +#endif + croak("nth_prime_lower(%"UVuf") overflow", n); + } return (UV) lower; } -/* The nth prime will be less than this number */ +/* The nth prime will be less or equal to this number */ UV nth_prime_upper(UV n) { double fn = (double) n; double flogn, flog2n, upper; if (n < NPRIMES_SMALL) - return primes_small[n]+1; + return primes_small[n]; flogn = log(n); - flog2n = log(flogn); + flog2n = log(flogn); /* Note distinction between log_2(n) and log^2(n) */ if (n >= 39017) - upper = fn * ( flogn + flog2n - 0.9484 ); /* Dusart 1999 */ + upper = fn * ( flogn + flog2n - 0.9484 ); /* Dusart 1999 page 14*/ else if (n >= 27076) upper = fn * (flogn + flog2n - 1.0 + ((flog2n-1.80)/flogn)); /*Dusart 1999*/ else if (n >= 7022) @@ -514,9 +521,15 @@ UV nth_prime_upper(UV n) else upper = fn * ( flogn + flog2n ); - /* Special case to not overflow any 32-bit */ - if (upper > (double)UV_MAX) - return (n <= UVCONST(203280221)) ? UVCONST(4294967292) : 0; + /* Watch out for overflow */ + if (upper >= (double)UV_MAX) { +#if BITS_PER_WORD == 32 + if (n <= UVCONST(203280221)) return UVCONST(4294967291); +#else + if (n <= UVCONST(425656284035217743)) return UVCONST(18446744073709551557); +#endif + croak("nth_prime_upper(%"UVuf") overflow", n); + } return (UV) ceil(upper); } @@ -542,6 +555,8 @@ UV nth_prime_approx(UV n) * m=1 + ((flog2n - 2)/flogn) ); * m=2 - (((flog2n*flog2n) - 6*flog2n + 11) / (2*flogn*flogn)) * + O((flog2n/flogn)^3) + * + * Shown in Dusart 1999 page 12, as well as other sources */ approx = fn * ( flogn + flog2n - 1 + ((flog2n - 2)/flogn) @@ -560,8 +575,24 @@ UV nth_prime_approx(UV n) else if (n < 12000) approx += 3.0 * order; else if (n <150000) approx += 2.1 * order; - if (approx > (double)UV_MAX) - return 0; + /* For all three analytical functions, it is possible that for a given valid + * input, we will not be able to return an output that fits in the UV type. + * For example, if they ask for the 203280222nd prime, we should return + * 4294967311. But in 32-bit, that overflows. What we do is calculate our + * double precision value. If that would overflow, then we look at the input + * and if it is <= the index of the last representable prime, then we return + * the last representable prime. Otherwise, we croak an overflow message. + * This should maintain the invariant: + * nth_prime_lower(n) <= nth_prime(n) <= nth_prime_upper(n) + */ + if (approx >= (double)UV_MAX) { +#if BITS_PER_WORD == 32 + if (n <= UVCONST(203280221)) return UVCONST(4294967291); +#else + if (n <= UVCONST(425656284035217743)) return UVCONST(18446744073709551557); +#endif + croak("nth_prime_approx(%"UVuf") overflow", n); + } return (UV) (approx + 0.5); } @@ -622,10 +653,7 @@ UV nth_prime(UV n) /* Determine a bound on the nth prime. We know it comes before this. */ upper_limit = nth_prime_upper(n); - if (upper_limit == 0) { - croak("nth_prime(%"UVuf") would overflow", n); - return 0; - } + MPUassert(upper_limit > 0, "nth_prime got an upper limit of 0"); /* Get the primary cache, and ensure it is at least this large. If the * input is small enough, get a sieve covering the range. Otherwise, we'll @@ -669,3 +697,68 @@ UV nth_prime(UV n) MPUassert(count == target, "nth_prime got incorrect count"); return ( (segbase*30) + p ); } + +UV prime_count_seg(UV low, UV high) +{ + UV const segment_size = SEGMENT_CHUNK_SIZE; + unsigned char* segment; + UV low_d, high_d; + UV count = 0; + + if ((low <= 2) && (high >= 2)) count++; + if ((low <= 3) && (high >= 3)) count++; + if ((low <= 5) && (high >= 5)) count++; + if (low < 7) low = 7; + + if (low > high) return count; + + /* + * (1) be smart about small low/high values. + * (2) be generic so we can kill the other function + * (3) use fast counting. + */ + + segment = get_prime_segment(); + if (segment == 0) + return 0; + + low_d = low / 30; + high_d = high / 30; + + { /* Avoid recalculations of this */ + UV endp = (high_d >= (UV_MAX/30)) ? UV_MAX-2 : 30*high_d+29; + prime_precalc( sqrt(endp) + 1 ); + } + + while ( low_d <= high_d ) { + UV seghigh_d = ((high_d - low_d) < segment_size) + ? high_d + : (low_d + segment_size-1); + UV range_d = seghigh_d - low_d + 1; + UV seghigh = (seghigh_d == high_d) ? high : (seghigh_d*30+29); + UV segbase = low_d * 30; + /* printf(" startd = %"UVuf" endd = %"UVuf"\n", startd, endd); */ + + MPUassert( seghigh_d >= low_d, "segment_primes highd < lowd"); + MPUassert( range_d <= segment_size, "segment_primes range > segment size"); + + /* Sieve from startd*30+1 to endd*30+29. */ + if (sieve_segment(segment, low_d, seghigh_d) == 0) { + croak("Could not segment sieve from %"UVuf" to %"UVuf, segbase+1, seghigh); + break; + } + + if (seghigh < high) { + count += count_zero_bits(segment, range_d); + } else { + START_DO_FOR_EACH_SIEVE_PRIME( segment, low - segbase, seghigh - segbase ) + count++; + END_DO_FOR_EACH_SIEVE_PRIME + } + + low_d += range_d; + low = seghigh+2; + } + + return count; +} diff --git a/util.h b/util.h index b14c175..6a09d98 100644 --- a/util.h +++ b/util.h @@ -13,6 +13,7 @@ extern UV prime_count_lower(UV x); extern UV prime_count_upper(UV x); extern UV prime_count_approx(UV x); extern UV prime_count(UV x); +extern UV prime_count_seg(UV low, UV high); extern UV nth_prime_lower(UV n); extern UV nth_prime_upper(UV 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