I wrote up an initial implementation, tests, and documentation
https://github.com/sethtroisi/libgmp/pull/10/files

On Tue, Sep 3, 2019 at 3:19 AM Pedro Gimeno <gmpde...@formauri.es> wrote:

> Seth Troisi wrote on 2019-09-03 08:59:
>
> > 3. add mpz_nextprime_seq(a, b) => next prime a + k*b with k >= 1
>
> Personally I would prefer it named mpz_nextprime_step or
> mpz_nextprime_stride or mpz_nextprime_strided. Or even
> mpz_nexprime_with_step, if that doesn't violate some naming convention I'm
> not aware of. It's not all that clear to me what sequence does 'seq' refer
> to in the name.
>
From 353f07c97a7fa142c32119c87ffba0fe925e70bb Mon Sep 17 00:00:00 2001
From: Seth Troisi <sethtro...@google.com>
Date: Wed, 4 Sep 2019 23:46:10 -0700
Subject: [PATCH 1/3] Add mpz_prevprime

---
 gmp-h.in        |   3 ++
 mpz/nextprime.c | 129 +++++++++++++++++++++++++++++++++---------------
 2 files changed, 93 insertions(+), 39 deletions(-)

diff --git a/gmp-h.in b/gmp-h.in
index f448b4e..a1208d8 100644
--- a/gmp-h.in
+++ b/gmp-h.in
@@ -947,6 +947,9 @@ __GMP_DECLSPEC void mpz_neg (mpz_ptr, mpz_srcptr);
 #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 --git a/mpz/nextprime.c b/mpz/nextprime.c
index 4c5ca57..90f03a4 100644
--- a/mpz/nextprime.c
+++ b/mpz/nextprime.c
@@ -33,6 +33,49 @@ see https://www.gnu.org/licenses/.  */
 #include "gmp-impl.h"
 #include "longlong.h"
 
+#define INCR_LIMIT 0x10000	/* deep science */
+
+/* Loop to find next/prev prime */
+#define next_prime_helper(op, function)                     		\
+  do {					                		\
+  /* compute residues modulo small odd primes */         		\
+  moduli = TMP_SALLOC_TYPE (prime_limit, unsigned short); 		\
+                                                                        \
+  for (;;)				                		\
+    {                                                                   \
+      prime = 3;                                                        \
+      for (i = 0; i < prime_limit; i++)                                 \
+	{                                                               \
+	  moduli[i] = mpz_tdiv_ui (p, prime);                           \
+	  prime += primegap[i];                                         \
+	}                                                               \
+      for (difference = incr = 0; incr < INCR_LIMIT; difference += 2)   \
+	{                                                               \
+	  /* First check residues */                                    \
+	  prime = 3;                                                    \
+	  for (i = 0; i < prime_limit; i++)                             \
+	    {                                                           \
+	      signed r;                                                 \
+              signed t = moduli[i] op incr;                             \
+	      r = t % ((int) prime);                                    \
+	      prime += primegap[i];                                     \
+	      if (r == 0)                                               \
+		goto next;                                              \
+	    }                                                           \
+	  function (p, p, difference);                                  \
+	  difference = 0;                                               \
+                                                                        \
+	  /* Miller-Rabin test */                                       \
+	  if (mpz_millerrabin (p, 25))                                  \
+	    goto done;                                                  \
+	next:;                                                          \
+	  incr += 2;                                                    \
+	}                                                               \
+      function (p, p, difference);                                      \
+      difference = 0;                                                   \
+    }									\
+  } while (0)
+
 static const unsigned char primegap[] =
 {
   2,2,4,2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,14,4,6,
@@ -79,50 +122,58 @@ mpz_nextprime (mpz_ptr p, mpz_srcptr n)
 
   TMP_SMARK;
 
-  /* Compute residues modulo small odd primes */
-  moduli = TMP_SALLOC_TYPE (prime_limit * sizeof moduli[0], unsigned short);
+  /* work forwards looking for number with moduli == 0 and prime */
+  next_prime_helper (+, mpz_add_ui);
+
+ done:
+  TMP_SFREE;
+}
+
+int
+mpz_prevprime (mpz_ptr p, mpz_srcptr n)
+{
+  unsigned short *moduli;
+  unsigned long difference;
+  int i;
+  unsigned prime_limit;
+  unsigned long prime;
+  mp_size_t pn;
+  mp_bitcnt_t nbits;
+  unsigned incr;
+  TMP_SDECL;
+
+  /* handle numbers with no previous prime. */
+  if (mpz_cmp_ui (n, 2) <= 0)
+    return 0;
 
-  for (;;)
+  if (mpz_cmp_ui (n, 3) <= 0)
     {
-      /* FIXME: Compute lazily? */
-      prime = 3;
-      for (i = 0; i < prime_limit; i++)
-	{
-	  moduli[i] = mpz_tdiv_ui (p, prime);
-	  prime += primegap[i];
-	}
+      /* previous prime for 3 == 2 */
+      mpz_set_ui (p, 2);
+      return 2;
+    }
 
-#define INCR_LIMIT 0x10000	/* deep science */
+  /* First odd less than n */
+  mpz_sub_ui (p, n, 2);
+  mpz_setbit (p, 0);
 
-      for (difference = incr = 0; incr < INCR_LIMIT; difference += 2)
-	{
-	  /* First check residues */
-	  prime = 3;
-	  for (i = 0; i < prime_limit; i++)
-	    {
-	      unsigned r;
-	      /* FIXME: Reduce moduli + incr and store back, to allow for
-		 division-free reductions.  Alternatively, table primes[]'s
-		 inverses (mod 2^16).  */
-	      r = (moduli[i] + incr) % prime;
-	      prime += primegap[i];
-
-	      if (r == 0)
-		goto next;
-	    }
-
-	  mpz_add_ui (p, p, difference);
-	  difference = 0;
-
-	  /* Miller-Rabin test */
-	  if (mpz_millerrabin (p, 25))
-	    goto done;
-	next:;
-	  incr += 2;
-	}
-      mpz_add_ui (p, p, difference);
-      difference = 0;
+  if (mpz_cmp_ui (p, 7) <= 0)
+    {
+      return 2;
     }
+
+  pn = SIZ(p);
+  MPN_SIZEINBASE_2EXP(nbits, PTR(p), pn, 1);
+  if (nbits / 2 >= NUMBER_OF_PRIMES)
+    prime_limit = NUMBER_OF_PRIMES - 1;
+  else
+    prime_limit = nbits / 2;
+
+  TMP_SMARK;
+
+  /* work backwards looking for number with moduli == 0 and prime */
+  next_prime_helper (-, mpz_sub_ui);
+
  done:
   TMP_SFREE;
 }

From 15bccec04a7f4f43021f59f247ee2fc2a9e43d2a Mon Sep 17 00:00:00 2001
From: Seth Troisi <sethtro...@google.com>
Date: Wed, 4 Sep 2019 23:48:19 -0700
Subject: [PATCH 2/3] Added tests

---
 tests/mpz/t-nextprime.c | 245 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 215 insertions(+), 30 deletions(-)

diff --git a/tests/mpz/t-nextprime.c b/tests/mpz/t-nextprime.c
index 5607aea..0f82be9 100644
--- a/tests/mpz/t-nextprime.c
+++ b/tests/mpz/t-nextprime.c
@@ -32,6 +32,23 @@ refmpz_nextprime (mpz_ptr p, mpz_srcptr t)
     mpz_add_ui (p, p, 1L);
 }
 
+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
 run (const char *start, int reps, const char *end, short diffs[])
 {
@@ -58,8 +75,8 @@ run (const char *start, int reps, const char *end, short diffs[])
 
   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 ();
     }
 
@@ -67,24 +84,87 @@ run (const char *start, int reps, const char *end, short diffs[])
   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\n");
+	  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);
+      abort ();
+    }
+
+  mpz_clear (y);
+  mpz_clear (x);
+}
+
+void
+test_ref (gmp_randstate_ptr rands, int reps, void (*func)(mpz_t, mpz_t), void(*ref_func)(mpz_t, mpz_t))
+{
+  int i;
+  mpz_t bs, x, test_p, ref_p;
+  unsigned long size_range;
+
+  mpz_inits (bs, x, test_p, ref_p, NULL);
+
+  mpz_set_ui(test_p, 0);
+  mpz_set_ui(ref_p, 0);
+
+  for (i = 0; i < reps; i++)
+    {
+      mpz_urandomb (bs, rands, 32);
+      size_range = mpz_get_ui (bs) % 8 + 2; /* 0..1024 bit operands */
+
+      mpz_urandomb (bs, rands, size_range);
+      mpz_rrandomb (x, rands, mpz_get_ui (bs));
+
+      func (test_p, x);
+      ref_func (ref_p, x);
+      if (mpz_cmp (test_p, ref_p) != 0)
+        {
+          gmp_printf ("op   %Zd\n", x);
+          gmp_printf ("got  %Zd\n", test_p);
+          gmp_printf ("want %Zd\n", ref_p);
+	  abort ();
+        }
+    }
+
+  mpz_clears (bs, x, test_p, ref_p, NULL);
+}
+
 extern short diff1[];
 extern short diff3[];
 extern short diff4[];
 extern short diff5[];
 extern short diff6[];
 
-int
-main (int argc, char **argv)
+void
+test_nextprime (gmp_randstate_ptr rands, int reps)
 {
-  int i;
-  int reps = 20;
-  gmp_randstate_ptr rands;
-  mpz_t bs, x, nxtp, ref_nxtp;
-  unsigned long size_range;
-
-  tests_start();
-  rands = RANDS;
-
   run ("2", 1000, "0x1ef7", diff1);
 
   run ("3", 1000 - 1, "0x1ef7", NULL);
@@ -101,33 +181,138 @@ main (int argc, char **argv)
   run ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80", 50, /* 2^128 - 128 */
        "0x10000000000000000000000000000155B", diff6);
 
-  mpz_init (bs);
+  test_ref(rands, reps, mpz_nextprime, refmpz_nextprime);
+}
+
+void
+test_prevprime (gmp_randstate_ptr rands, int reps)
+{
+  int i, retval;
+  mpz_t x, 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);
+
+  test_ref(rands, reps, mpz_prevprime, refmpz_prevprime);
+
+  // Test mpz_prevprime(x <= 2) returns 0, leaves rop unchanged.
   mpz_init (x);
-  mpz_init (nxtp);
-  mpz_init (ref_nxtp);
+  mpz_init (prvp);
+  mpz_set_ui (prvp, 123);
 
-  TESTS_REPS (reps, argv, argc);
+  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(%Zd) return (%d) rop (%Zd)\n", x, prvp );
+	  abort ();
+        }
+    }
 
-  for (i = 0; i < reps; i++)
+  mpz_clear (x);
+  mpz_clear (prvp);
+}
+
+void
+test_largegap (mpz_t low, const gap)
+{
+  mpz_t t, nxt, prv;
+
+  mpz_inits (t, nxt, prv, NULL);
+
+  mpz_nextprime(nxt, low);
+  mpz_sub(t, nxt, low);
+
+  if (mpz_cmp_ui(t, gap) != 0)
     {
-      mpz_urandomb (bs, rands, 32);
-      size_range = mpz_get_ui (bs) % 8 + 2; /* 0..1024 bit operands */
+      gmp_printf ("prime gap %Zd != %d\n", t, gap);
+      abort ();
+    }
 
-      mpz_urandomb (bs, rands, size_range);
-      mpz_rrandomb (x, rands, mpz_get_ui (bs));
+  mpz_prevprime(prv, nxt);
+
+  if (mpz_cmp(prv, low))
+    {
+      gmp_printf ("mpz_prevprime(mpz_nextprime(x)) != x\n");
+      abort ();
+    }
+
+  mpz_clears (t, nxt, prv, NULL);
+}
+
+void
+test_largegaps ()
+{
+  mpz_t x, t;
+
+  mpz_init (x);
+  mpz_init (t);
 
-/*      gmp_printf ("%ld: %Zd\n", mpz_sizeinbase (x, 2), x); */
+  /*
+  // This takes ~90 seconds, it test the deep science magic constant in
+  // nextprime.c but takes too long to be always enabled.
 
-      mpz_nextprime (nxtp, x);
-      refmpz_nextprime (ref_nxtp, x);
-      if (mpz_cmp (nxtp, ref_nxtp) != 0)
-	abort ();
+  // Gap 66520 from P816 = 1931 * 1933# / 7230 - 30244
+  mpz_set_ui (x, 1931);
+  mpz_set_ui (t, 2);
+  while (mpz_cmp_ui(t, 1933) <= 0)
+    {
+      mpz_mul (x, x, t);
+      mpz_nextprime (t, t);
     }
+  mpz_divexact_ui (x, x, 7230);
+  mpz_sub_ui (x, x, 30244);
+
+  test_largegap(x, 66520);
+  */
+
+  // Gap 33008 from P454 = 55261931 * 1063#/210 - 13116
+  mpz_set_ui (x, 55261931);
+  mpz_set_ui (t, 2);
+  while (mpz_cmp_ui(t, 1063) <= 0)
+    {
+      mpz_mul (x, x, t);
+      mpz_nextprime (t, t);
+    }
+  mpz_divexact_ui (x, x, 210);
+  mpz_sub_ui (x, x, 13116);
+
+  test_largegap(x, 33008);
+
 
-  mpz_clear (bs);
   mpz_clear (x);
-  mpz_clear (nxtp);
-  mpz_clear (ref_nxtp);
+  mpz_clear (t);
+}
+int
+main (int argc, char **argv)
+{
+  gmp_randstate_ptr rands;
+  int reps = 500;
+
+  tests_start();
+
+  rands = RANDS;
+  TESTS_REPS (reps, argv, argc);
+
+  test_nextprime(rands, reps);
+  test_prevprime(rands, reps);
+
+  test_largegaps();
 
   tests_end ();
   return 0;

From 7f563a4494038d6efb7312c16550423a3097f5e6 Mon Sep 17 00:00:00 2001
From: Seth Troisi <sethtro...@google.com>
Date: Wed, 4 Sep 2019 23:48:33 -0700
Subject: [PATCH 3/3] Added documentation

---
 doc/gmp.texi | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/doc/gmp.texi b/doc/gmp.texi
index 60c2634..da412a1 100644
--- a/doc/gmp.texi
+++ b/doc/gmp.texi
@@ -3559,8 +3559,19 @@ and 50.
 @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}.
+@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.
 
-This function uses a probabilistic algorithm to identify primes.  For
+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
_______________________________________________
gmp-devel mailing list
gmp-devel@gmplib.org
https://gmplib.org/mailman/listinfo/gmp-devel

Reply via email to