[libmath-prime-util-perl] 39/54: Updates for AKS

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:52:10 UTC 2015


This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.38
in repository libmath-prime-util-perl.

commit a3f4d0735098a6220c76935f36e3d0c8334ec3f5
Author: Dana Jacobsen <dana at acm.org>
Date:   Fri Feb 21 17:13:36 2014 -0800

    Updates for AKS
---
 Changes |   3 ++
 aks.c   | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 99 insertions(+), 20 deletions(-)

diff --git a/Changes b/Changes
index eb94700..26238c2 100644
--- a/Changes
+++ b/Changes
@@ -10,6 +10,9 @@ Revision history for Perl module Math::Prime::Util
 
     - Factoring powers is much faster.
 
+    - Add Bernstein+Voloch improvements to AKS.
+      Much faster, though of course still much slower than BPSW.
+
     [OTHER]
 
     - Added some Project Euler examples.
diff --git a/aks.c b/aks.c
index 9205955..d74401c 100644
--- a/aks.c
+++ b/aks.c
@@ -8,6 +8,11 @@
  * The AKS v6 algorithm, for native integers.  Based on the AKS v6 paper.
  * As with most AKS implementations, it's really slow.
  *
+ * If we know there is a lgamma function (C99), then this uses the
+ * improvements from Folkmar Bornemann's 2002 Pari implementation.  This
+ * includes Bernstein and Voloch's much, much better r/s selection.  The
+ * performance difference is huge.
+ *
  * When n < 2^(wordbits/2)-1, we can do a straightforward intermediate:
  *      r = (r + a * b) % n
  * If n is larger, then these are replaced with:
@@ -19,18 +24,40 @@
  *
  * This is all much easier in GMP.
  *
- * Copyright 2012, Dana Jacobsen.
+ * Copyright 2012-2014, Dana Jacobsen.
  */
 
 #define SQRTN_SHORTCUT 1
 
+/* Use the improvements from Bornemann's 2002 implementation */
+#if (defined __USE_MISC || defined __USE_XOPEN || defined __USE_ISOC99) && !defined(_MSC_VER)
+#define IMPL_BORNEMANN 1
+#else
+#define IMPL_BORNEMANN 0
+#endif
+
 #include "ptypes.h"
 #include "aks.h"
 #define FUNC_isqrt 1
 #include "util.h"
 #include "cache.h"
 #include "mulmod.h"
+#include "factor.h"
 
+#if IMPL_BORNEMANN
+static int is_primitive_root(UV n, UV r)
+{
+  UV fac[MPU_MAX_FACTORS+1];
+  int i, nfacs;
+  /* if ( (r&1) & powmod(n, (r-1)>>1, r) == 1 )  return 0; */
+  nfacs = factor_exp(r-1, fac, 0);
+  for (i = 0; i < nfacs; i++) {
+    UV m = powmod(n, (r-1)/fac[i], r);
+    if (m == 1) return 0;
+  }
+  return 1;
+}
+#else
 /* Naive znorder.  Works well here because limit will be very small. */
 static UV order(UV r, UV n, UV limit) {
   UV j;
@@ -42,6 +69,7 @@ static UV order(UV r, UV n, UV limit) {
   }
   return j;
 }
+#endif
 
 #if 0
 static void poly_print(UV* poly, UV r)
@@ -178,10 +206,16 @@ static int test_anr(UV a, UV n, UV r)
   return retval;
 }
 
+/*
+ * Avanzi and Mihǎilescu, 2007
+ * http://www.uni-math.gwdg.de/preda/mihailescu-papers/ouraks3.pdf
+ * "As a consequence, one cannot expect the present variants of AKS to
+ *  compete with the earlier primality proving methods like ECPP and
+ *  cyclotomy." - conclusion regarding memory consumption
+ */
 int _XS_is_aks_prime(UV n)
 {
-  UV sqrtn, limit, r, rlimit, a;
-  double log2n;
+  UV r, s, a;
   int verbose;
 
   if (n < 2)
@@ -192,32 +226,74 @@ int _XS_is_aks_prime(UV n)
   if (is_power(n, 0))
     return 0;
 
-  sqrtn = isqrt(n);
-  log2n = log(n) / log(2);   /* C99 has a log2() function */
-  limit = (UV) floor(log2n * log2n);
+  if (n > 11 && ( !(n%2) || !(n%3) || !(n%5) || !(n%7) || !(n%11) )) return 0;
+  /* if (!is_prob_prime(n)) return 0; */
 
   verbose = _XS_get_verbose();
-  if (verbose) { printf("# aks limit is %lu\n", (unsigned long) limit); }
+#if IMPL_BORNEMANN == 0
+  {
+    UV sqrtn = isqrt(n);
+    double log2n = log(n) / log(2);   /* C99 has a log2() function */
+    UV limit = (UV) floor(log2n * log2n);
 
-  for (r = 2; r < n; r++) {
-    if ((n % r) == 0)
-      return 0;
+    if (verbose) { printf("# aks limit is %lu\n", (unsigned long) limit); }
+
+    for (r = 2; r < n; r++) {
+      if ((n % r) == 0)
+        return 0;
 #if SQRTN_SHORTCUT
-    if (r > sqrtn)
-      return 1;
+      if (r > sqrtn)
+        return 1;
 #endif
-    if (order(r, n, limit) > limit)
-      break;
-  }
+      if (order(r, n, limit) > limit)
+        break;
+    }
 
-  if (r >= n)
-    return 1;
+    if (r >= n)
+      return 1;
+
+    s = (UV) floor(sqrt(r-1) * log2n);
 
-  rlimit = (UV) floor(sqrt(r-1) * log2n);
+    if (verbose) { printf("# aks r = %lu  s = %lu\n", (unsigned long) r, (unsigned long) s); }
+  }
+#else
+  {
+    UV fac[MPU_MAX_FACTORS+1];
+    UV slim;
+    double c1, c2;
+    double const t = 48;
+    double const t1 = (1.0/((t+1)*log(t+1)-t*log(t)));
+    r = next_prime( (UV) (t1*t1 * log(n)*log(n)) );
+    while (!is_primitive_root(n,r))
+      r = next_prime(r);
 
-  if (verbose) { printf("# aks r = %lu  rlimit = %lu\n", (unsigned long) r, (unsigned long) rlimit); }
+    slim = (UV) (2*t*(r-1));
+    c1 = lgamma(r-1);
+    c2 = log(n) * floor(sqrt(r));
+    { /* Binary search for first s in [1,slim] where x >= 0 */
+      UV i = 1;
+      UV j = slim;
+      while (i < j) {
+        s = i + (j-i)/2;
+        double x = (lgamma(r-1+s) - c1 - lgamma(s+1)) / c2 - 1.0;
+        if (x < 0)  i = s+1;
+        else        j = s;
+      }
+      s = i-1;
+    }
+    s = (s+3) >> 1;
+    /* Check for any factors less than (s-1)^2 */
+    slim = (s-1)*(s-1);
+    if (verbose > 1) printf("# aks trial to %lu\n", slim);
+    if (trial_factor(n, fac, slim) > 1)
+      return 0;
+    if (slim >= HALF_WORD || (slim*slim) >= n)
+      return 1;
+  }
+  if (verbose) { printf("# aks r = %lu  s = %lu\n", (unsigned long) r, (unsigned long) s); }
+#endif
 
-  for (a = 1; a <= rlimit; a++) {
+  for (a = 1; a <= s; a++) {
     if (! test_anr(a, n, r) )
       return 0;
     if (verbose>1) { printf("."); fflush(stdout); }

-- 
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