[libmath-prime-util-perl] 07/16: Overflow for nth_prime, segment prime_count
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:44:24 UTC 2015
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 <dana at 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
More information about the Pkg-perl-cvs-commits
mailing list