I see this was pushed, with a few more polishing tweaks. Thanks for the collaboration to improve my rough code into something much cleaner.
I see you also added more testing in tests/devel/primes.c which is a great triple check. It looks like the usage on line 21-28 / 399 wasn't updated. Now that this is in I have a much smaller patch to add mpz_prevprime() My patch includes t-nextprime-tune.c and Makefile.am which should not be committed but I used for internal testing and might be useful. On Tue, Mar 17, 2020 at 10:53 PM Seth Troisi <brain...@gmail.com> wrote: > > > On Tue, Mar 17, 2020 at 3:04 PM Marco Bodrato <bodr...@mail.dm.unipi.it> > wrote: > >> Ciao, >> >> Il 2020-03-16 02:23 Seth Troisi ha scritto: >> > On Sun, Mar 15, 2020 at 4:38 PM Marco Bodrato >> > <bodr...@mail.dm.unipi.it> wrote: >> >> May I write a few more comments? >> >> > Always, my opinion about being ready is just that it's passed >> > the bar of being good enough not that it's perfect. >> >> I'm tempted to simply commit your proposal and then change it :-) >> But if we keep on discussing here, we can improving the code with the >> opinions of two persons... >> >> >> I see now: >> >> + if (nbits <= 32) >> >> + incr_limit = 336; >> >> >> Probably, we should change the name of the variable to >> >> odd_candidates_in_sieve, then you would probably have written: >> >> if (nbits <= 32) >> >> odd_numbers_in_sieve = 168; >> >> > Renamed to odds_in_composite_sieve >> >> Of course the name is not essential, the values are :-) >> And they seem correct to me, now. >> > I only discovered on experimental verification but the +1 is not needed, > it took me a while to > understand why but p is incremented to the first odd after the start of > the gap by so the gap is -2 > >> >> >> Then I wonder if the cost of the /* Sieve next segment */ step >> [...] >> >> need to allocate the possibly huge next_mult array? >> > >> > You're correct, this reduces the memory use 80% >> >> Moreover the code has now the following structure: >> >> SIEVE; >> for (;;) { >> CHECK_AND_POSSIBLY_BREAK; >> SIEVE; >> } >> >> There is an unneeded code duplication, it should be: >> >> for (;;) { >> SIEVE; >> CHECK_AND_POSSIBLY_BREAK; >> } >> > This is a great improvement :) thanks for the additional pair of eyes :) > > >> >> If you need to store larger gaps, you can also save in >> >> the array gap>>1, because they all are even. The prime >> >> limit will be able to go beyond 2^37... >> > >> > Updated comment for how this could be done in the future. >> >> The first gap larger than 500, if I'm not wrong, is >> 304599508537+514=304599509051 >> > I added "far" so the comment now reads "allow this to go far beyond 436M" > >> >> >> I'd expect a condition like >> >> >> >> + if (nbits <= numberof (primegap_small) * 2 + 1) >> >> + { >> >> + primegap = primegap_small; >> >> + prime_limit = nbits / 2; >> >> + } >> >> > Done >> >> I read: >> + if (2 * nbits <= NUMBER_OF_PRIMES) >> + { >> + primegap = primegap_small; >> + prime_limit = nbits / 2; >> + } >> >> But ... this means that at most one fourth of the constants in >> primegap_small are used, am I wrong? >> > I was experimenting with using 3 * nbits / 5 and other ratios and > miscommitted this. > Thanks again for the 2nd pair of eyes. > >> >> Ĝis, >> m >> >
diff -r a7836288d2c0 doc/gmp.texi --- a/doc/gmp.texi Fri Mar 20 16:19:07 2020 +0100 +++ b/doc/gmp.texi Sat Mar 21 03:40:28 2020 -0700 @@ -3563,8 +3563,19 @@ @deftypefun void mpz_nextprime (mpz_t @var{rop}, const mpz_t @var{op}) @cindex Next prime function Set @var{rop} to the next prime greater than @var{op}. - -This function uses a probabilistic algorithm to identify primes. For +@end deftypefun + +@deftypefun int mpz_prevprime (mpz_t @var{rop}, const mpz_t @var{op}) +@cindex Previous prime function +Set @var{rop} to the greatest prime less than @var{op}. + +If previous prime doesn't exist (e.g. @var{op} < 3), rop is unchanged and +0 is returned. + +Return 1 if @var{rop} is a probably prime, and 2 if @var{rop} is definitely +prime. + +These functions use a probabilistic algorithm to identify primes. For practical purposes it's adequate, the chance of a composite passing will be extremely small. @end deftypefun diff -r a7836288d2c0 gmp-h.in --- a/gmp-h.in Fri Mar 20 16:19:07 2020 +0100 +++ b/gmp-h.in Sat Mar 21 03:40:28 2020 -0700 @@ -947,6 +947,9 @@ #define mpz_nextprime __gmpz_nextprime __GMP_DECLSPEC void mpz_nextprime (mpz_ptr, mpz_srcptr); +#define mpz_prevprime __gmpz_prevprime +__GMP_DECLSPEC int mpz_prevprime (mpz_ptr, mpz_srcptr); + #define mpz_out_raw __gmpz_out_raw #ifdef _GMP_H_HAVE_FILE __GMP_DECLSPEC size_t mpz_out_raw (FILE *, mpz_srcptr); diff -r a7836288d2c0 mpz/nextprime.c --- a/mpz/nextprime.c Fri Mar 20 16:19:07 2020 +0100 +++ b/mpz/nextprime.c Sat Mar 21 03:40:28 2020 -0700 @@ -120,8 +120,10 @@ return sieve_limit; } -void -mpz_nextprime (mpz_ptr p, mpz_srcptr n) +int +findnext (mpz_ptr p, + unsigned long(*nextmod_func)(const mpz_t, unsigned long), + void(*nextseq_func)(mpz_t, const mpz_t, unsigned long)) { char *composite; const unsigned char *primegap; @@ -132,21 +134,13 @@ unsigned odds_in_composite_sieve; TMP_DECL; - /* First handle tiny numbers */ - if (mpz_cmp_ui (n, 2) < 0) - { - mpz_set_ui (p, 2); - return; - } - mpz_add_ui (p, n, 1); - mpz_setbit (p, 0); + TMP_MARK; - if (mpz_cmp_ui (p, 7) <= 0) - return; - - TMP_MARK; pn = SIZ(p); MPN_SIZEINBASE_2EXP(nbits, PTR(p), pn, 1); + /* p >= 8, smaller numbers handled */ + ASSERT (nbits <= 3); + if (nbits / 2 <= NUMBER_OF_PRIMES) { primegap = primegap_small; @@ -165,8 +159,8 @@ prime_limit = gmp_primesieve(sieve, sieve_limit); /* TODO: Storing (prime - last_prime)/2 would allow this to go - up to the gap 304599508537+514=304599509051 . - With the current code our limit is 436273009+282=436273291 */ + up to the gap 304599508537+514=304599509051 . + With the current code our limit is 436273009+282=436273291 */ ASSERT (sieve_limit < 436273291); /* Needed to avoid assignment of read-only location */ @@ -199,13 +193,14 @@ { unsigned long difference; unsigned long incr, prime; + int primetest; memset (composite, 0, odds_in_composite_sieve); prime = 3; for (i = 0; i < prime_limit; i++) { - m = mpz_cdiv_ui(p, prime); - /* Only care about odd multiplies of prime. */ + m = nextmod_func(p, prime); + /* Only care about odd multiplies */ if (m & 1) m += prime; m >>= 1; @@ -222,20 +217,62 @@ if (composite[incr]) continue; - mpz_add_ui (p, p, difference); + nextseq_func(p, p, difference); difference = 0; /* Miller-Rabin test */ - if (mpz_millerrabin (p, 25)) - { - TMP_FREE; - return; - } + primetest = mpz_millerrabin (p, 25); + if (primetest) + { + TMP_FREE; + return primetest; + } } /* Sieve next segment, very rare */ - mpz_add_ui (p, p, difference); + nextseq_func(p, p, difference); + } +} + +void +mpz_nextprime (mpz_ptr p, mpz_srcptr n) +{ + /* First handle tiny numbers */ + if (mpz_cmp_ui (n, 2) < 0) + { + mpz_set_ui (p, 2); + return; } + mpz_add_ui (p, n, 1); + mpz_setbit (p, 0); + + if (mpz_cmp_ui (p, 7) <= 0) + return; + + findnext(p, mpz_cdiv_ui, mpz_add_ui); +} + +int +mpz_prevprime (mpz_ptr p, mpz_srcptr n) +{ + /* First handle tiny numbers */ + if (mpz_cmp_ui (n, 2) <= 0) + return 0; + + if (mpz_cmp_ui (n, 3) == 0) + { + mpz_set_ui(p, 2); + return 2; + } + + /* First odd less than n */ + mpz_sub_ui (p, n, 2); + mpz_setbit (p, 0); + + if (mpz_cmp_ui (n, 9) <= 0) + return 2; + + return findnext(p, mpz_fdiv_ui, mpz_sub_ui); } #undef LOOP_ON_SIEVE_END diff -r a7836288d2c0 tests/mpz/Makefile.am --- a/tests/mpz/Makefile.am Fri Mar 20 16:19:07 2020 +0100 +++ b/tests/mpz/Makefile.am Sat Mar 21 03:40:28 2020 -0700 @@ -30,7 +30,8 @@ t-fac_ui t-mfac_uiui t-primorial_ui t-fib_ui t-lucnum_ui t-scan t-fits \ t-divis t-divis_2exp t-cong t-cong_2exp t-sizeinbase t-set_str \ t-aorsmul t-cmp_d t-cmp_si t-hamdist t-oddeven t-popcount t-set_f \ - t-io_raw t-import t-export t-pprime_p t-nextprime t-remove t-limbs + t-io_raw t-import t-export t-pprime_p t-nextprime t-remove t-limbs \ + t-nextprime-tune TESTS = $(check_PROGRAMS) diff -r a7836288d2c0 tests/mpz/t-nextprime-tune.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/mpz/t-nextprime-tune.c Sat Mar 21 03:40:28 2020 -0700 @@ -0,0 +1,107 @@ +/* Test mpz_nextprime. + +Copyright 2009, 2015, 2018, 2020 Free Software Foundation, Inc. + +This file is part of the GNU MP Library test suite. + +The GNU MP Library test suite is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 3 of the License, +or (at your option) any later version. + +The GNU MP Library test suite is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along with +the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ + + +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> + +#include "gmp-impl.h" +#include "tests.h" + +int +main (int argc, char **argv) +{ + gmp_randstate_ptr rands; + int reps = 20; + + tests_start(); + + struct timeval tval_b, tval_a, tval_result; + + mpz_t n, m; + mpz_init(n); + mpz_init(m); + + //int sizes[] = {100, 200, 300, 500, 1000, 2000, 5000}; + //int sizes[] = {300, 500, 1000, 2000}; + //int sizes[] = {3000, 5000}; + int sizes[] = {32, 64, 128, 256, +// 180, 200, 220, +// 400, 500, 600, 700, 800, 900, + 1000, 1250, 1500, 1750, 2000, 2500, 3000, 4000, + 5000, 7500, + 10000, 12500, 15000 + }; + + for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) { + int size = sizes[i]; + int sumgap_next, sumgap_prev; + float seconds_next, seconds_prev; + + mpz_ui_pow_ui(n, 2, size-1); + mpz_set(m, n); + + gettimeofday(&tval_a, NULL); + +// int counts = 5; +// int counts = 50; + int counts = 30 * 15000 / size; + for (int t = 0; t < counts; t++) { + mpz_nextprime(n, n); + } + + gettimeofday(&tval_b, NULL); + timersub(&tval_b, &tval_a, &tval_result); + seconds_next = tval_result.tv_sec; + seconds_next += tval_result.tv_usec / 1e6; + + mpz_sub(m, n, m); + sumgap_next = mpz_get_ui(m); + mpz_set(m, n); + + for (int t = 0; t < counts; t++) { + mpz_prevprime(n, n); + } + + gettimeofday(&tval_a, NULL); + timersub(&tval_a, &tval_b, &tval_result); + seconds_prev = tval_result.tv_sec; + seconds_prev += tval_result.tv_usec / 1e6; + + mpz_sub(m, m, n); + sumgap_prev = mpz_get_ui(m); + + if (sumgap_prev <= sumgap_next) + // should have returned to <= the first value + gmp_printf("Error!\n"); + + gmp_printf("%d, count: %d, gap: %d %d, %.4f, %.4f seconds = avg %.5f, %.5f\n\n\n", + size, counts, + sumgap_next, sumgap_prev, + seconds_next, seconds_prev, + seconds_next / counts, seconds_prev / counts); + } + + mpz_clear(n); + mpz_clear(m); + + tests_end (); + return 0; +} diff -r a7836288d2c0 tests/mpz/t-nextprime.c --- a/tests/mpz/t-nextprime.c Fri Mar 20 16:19:07 2020 +0100 +++ b/tests/mpz/t-nextprime.c Sat Mar 21 03:40:28 2020 -0700 @@ -33,6 +33,23 @@ } void +refmpz_prevprime (mpz_ptr p, mpz_srcptr t) +{ + if (mpz_cmp_ui(t, 2) <= 0) + return; + + if (mpz_cmp_ui(t, 3) <= 0) + { + mpz_set_ui (p, 2); + return; + } + + mpz_sub_ui (p, t, 1L); + while (! mpz_probab_prime_p (p, 10)) + mpz_sub_ui (p, p, 1L); +} + +void test_largegap (mpz_t low, const int gap) { mpz_t t, nxt; @@ -60,30 +77,33 @@ mpz_init (x); - // This takes ~3 seconds on a fast computer. - // Gap 33008 from P454 = 55261931 * 1063#/210 - 13116 - mpz_primorial_ui (x, 1063); - mpz_mul_ui (x, x, 55261931); - mpz_divexact_ui (x, x, 210); - mpz_sub_ui (x, x, 13116); + // largest gap with start < 2^32. + mpz_set_ui (x, 3842610773); + test_largegap (x, 336); + + // largest gap with start < 2^64. + mpz_set_ui (x, 18361375334787046697UL); + test_largegap (x, 1550); + + // test high merit primegap in the P30 digit range. + mpz_set_str (x, "3001549619028223830552751967", 10); + test_largegap (x, 2184); - test_largegap(x, 33008); + // test high merit primegap in the P100 range. + mpz_primorial_ui (x, 257); + mpz_mul_ui (x, x, 4280516017); + mpz_divexact_ui (x, x, 5610); + mpz_sub_ui (x, x, 2560); + test_largegap (x, 9006); + + // test high merit primegap in the P200 range. + mpz_primorial_ui (x, 409); + mpz_mul_ui (x, x, 3483347771); + mpz_divexact_ui (x, x, 30); + mpz_sub_ui (x, x, 7016); + test_largegap (x, 15900); mpz_clear (x); - - - /* - // This takes ~30 seconds, it test the deep science magic constant in - // nextprime.c but takes too long to be always enabled. - // Gap 66520 from P816 = 1931 * 1933# / 7230 - 30244 - mpz_primorial_ui (x, 1933); - mpz_mul_ui (x, x, 1931); - mpz_divexact_ui (x, x, 7230); - mpz_sub_ui (x, x, 30244); - - test_largegap(x, 66520); - */ - } void @@ -112,8 +132,8 @@ if (mpz_cmp (x, y) != 0) { - gmp_printf ("got %Zx\n", x); - gmp_printf ("want %Zx\n", y); + gmp_printf ("got %Zd\n", x); + gmp_printf ("want %Zd\n", y); abort (); } @@ -121,6 +141,45 @@ mpz_clear (x); } +void +run_p (const char *start, int reps, const char *end, short diffs[]) +{ + mpz_t x, y; + int i; + + mpz_init_set_str (x, end, 0); + mpz_init (y); + + // Last rep doesn't share same data with nextprime + for (i = 0; i < reps - 1; i++) + { + mpz_prevprime (y, x); + mpz_sub (x, x, y); + if (diffs != NULL && + (! mpz_fits_sshort_p (x) || diffs[reps - i - 1] != (short) mpz_get_ui (x))) + { + gmp_printf ("diff list discrepancy %Zd, %d vs %d\n", + y, diffs[i], mpz_get_ui (x)); + abort (); + } + mpz_swap (x, y); + } + + // starts aren't always prime, so check that result is less than or equal + mpz_prevprime(x, x); + + mpz_set_str(y, start, 0); + if (mpz_cmp (x, y) > 0) + { + gmp_printf ("got %Zd\n", x); + gmp_printf ("want %Zd\n", y); + } + + mpz_clear (y); + mpz_clear (x); +} + + extern short diff1[]; extern short diff3[]; extern short diff4[]; @@ -128,15 +187,18 @@ extern short diff6[]; void -test_ref(gmp_randstate_ptr rands, int reps) { +test_ref (gmp_randstate_ptr rands, int reps, + void (*func)(mpz_t, const mpz_t), + void(*ref_func)(mpz_t, const mpz_t)) +{ int i; - mpz_t bs, x, next_p, ref_next_p; + mpz_t bs, x, test_p, ref_p; unsigned long size_range; mpz_init (bs); mpz_init (x); - mpz_init (next_p); - mpz_init (ref_next_p); + mpz_init (test_p); + mpz_init (ref_p); for (i = 0; i < reps; i++) { @@ -146,33 +208,26 @@ mpz_urandomb (bs, rands, size_range); mpz_rrandomb (x, rands, mpz_get_ui (bs)); -/* gmp_printf ("%ld: %Zd\n", mpz_sizeinbase (x, 2), x); */ - - mpz_nextprime (next_p, x); - refmpz_nextprime (ref_next_p, x); - if (mpz_cmp (next_p, ref_next_p) != 0) - abort (); + func (test_p, x); + ref_func (ref_p, x); + if (mpz_cmp (test_p, ref_p) != 0) + { + gmp_printf ("start %Zd\n", x); + gmp_printf ("got %Zd\n", test_p); + gmp_printf ("want %Zd\n", ref_p); + abort (); + } } mpz_clear (bs); mpz_clear (x); - mpz_clear (next_p); - mpz_clear (ref_next_p); + mpz_clear (test_p); + mpz_clear (ref_p); } -int -main (int argc, char **argv) +void +test_nextprime(gmp_randstate_ptr rands, int reps) { - gmp_randstate_ptr rands; - int reps = 20; - - tests_start(); - - rands = RANDS; - TESTS_REPS (reps, argv, argc); - - test_ref(rands, reps); - run ("2", 1000, "0x1ef7", diff1); run ("3", 1000 - 1, "0x1ef7", NULL); @@ -189,8 +244,106 @@ run ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80", 50, /* 2^128 - 128 */ "0x10000000000000000000000000000155B", diff6); - // Too slow to include in normal testing. - //test_largegaps (); + test_ref( + rands, reps, + (void (*)(mpz_t, const mpz_t)) mpz_nextprime, + refmpz_nextprime); +} + +void +test_prevprime (gmp_randstate_ptr rands, int reps) +{ + unsigned long i; + int retval; + mpz_t x, prvp; + + mpz_init (x); + mpz_init (prvp); + + /* Test mpz_prevprime(x <= 2) returns 0, leaves rop unchanged. */ + { + mpz_set_ui (prvp, 123); + for (i = -10; i <= 2; i++) + { + mpz_set_si(x, i); + retval = mpz_prevprime (prvp, x); + if ( retval != 0 || mpz_get_si (prvp) != 123 ) + { + gmp_printf ("mpz_prevprime(%d) return (%d) rop (%Zd)\n", i, retval, prvp); + abort (); + } + } + } + + /* Test mpz_prevprime(3 <= x < 2^45) returns 2. */ + { + for (i = 10; i < 0x200000000000L; i += i/10) + { + mpz_set_si(x, i); + retval = mpz_prevprime (prvp, x); + if ( retval != 2 ) + { + gmp_printf ("mpz_prevprime(%d) return (%d) rop (%Zd)\n", i, retval, prvp); + abort (); + } + } + } + + /* Test mpz_prevprime(x > 2^70) returns 1. */ + { + for (i = 70; i < 100; i++) + { + mpz_ui_pow_ui(x, 2, i); + retval = mpz_prevprime (prvp, x); + if ( retval != 1 ) + { + gmp_printf ("mpz_prevprime(%Zd) return (%d) rop (%Zd)\n", x, retval, prvp); + abort (); + } + } + } + + mpz_clear (x); + mpz_clear (prvp); + + run_p ("2", 1000, "0x1ef7", diff1); + + run_p ("3", 1000 - 1, "0x1ef7", NULL); + + run_p ("0x8a43866f5776ccd5b02186e90d28946aeb0ed914", 50, + "0x8a43866f5776ccd5b02186e90d28946aeb0eeec5", diff3); + + run_p ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", 50, /* 2^148 - 148 */ + "0x100000000000000000000000000000000010ab", diff4); + + run_p ("0x1c2c26be55317530311facb648ea06b359b969715db83292ab8cf898d8b1b", 50, + "0x1c2c26be55317530311facb648ea06b359b969715db83292ab8cf898da957", diff5); + + run_p ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80", 50, /* 2^128 - 128 */ + "0x10000000000000000000000000000155B", diff6); + + // Cast away int return from mpz_prevprime for test ref. + test_ref( + rands, reps, + (void (*)(mpz_t, const mpz_t)) mpz_prevprime, + refmpz_prevprime); +} + +int +main (int argc, char **argv) +{ + gmp_randstate_ptr rands; + int reps = 20; + + tests_start(); + + rands = RANDS; + TESTS_REPS (reps, argv, argc); + + test_nextprime(rands, reps); + test_prevprime(rands, reps); + + test_largegaps (); tests_end (); return 0; diff -r a7836288d2c0 tune/speed.h --- a/tune/speed.h Fri Mar 20 16:19:07 2020 +0100 +++ b/tune/speed.h Sat Mar 21 03:40:28 2020 -0700 @@ -3214,7 +3214,7 @@ /* Calculate nextprime(n) for random n of s->size bits (not limbs). */ #define SPEED_ROUTINE_MPZ_NEXTPRIME(function) \ { \ - unsigned i, j; \ + unsigned i, j, k; \ mpz_t wp, n; \ double t; \ \ @@ -3230,17 +3230,19 @@ \ speed_starttime (); \ i = s->reps; \ + /* Increase time_divisor significantly for small numbers */ \ + j = 200000 / s->size; \ do \ { \ /* nextprime timing is variable, so average over many calls */ \ - j = SPEED_BLOCK_SIZE - 1; \ + k = j-1; \ /* starts on random, after measures prime to next prime */ \ function (wp, n); \ do \ { \ function (wp, wp); \ } \ - while (--j != 0); \ + while (--k != 0); \ } \ while (--i != 0); \ t = speed_endtime (); \ @@ -3248,7 +3250,7 @@ mpz_clear (wp); \ mpz_clear (n); \ \ - s->time_divisor = SPEED_BLOCK_SIZE; \ + s->time_divisor = j; \ return t; \ }
_______________________________________________ gmp-devel mailing list gmp-devel@gmplib.org https://gmplib.org/mailman/listinfo/gmp-devel