[libmath-prime-util-perl] 43/54: Custom lgamma so improved AKS works on all platforms

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 3783549f9d032d48354d74620c8ca4b2a8c2ebf8
Author: Dana Jacobsen <dana at acm.org>
Date:   Thu Feb 27 13:34:36 2014 -0800

    Custom lgamma so improved AKS works on all platforms
---
 Changes             |  4 ++--
 aks.c               | 34 +++++++++++++++++++++++++---------
 t/22-aks-prime.t    |  6 +-----
 xt/primality-aks.pl | 16 +++++++++++-----
 4 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/Changes b/Changes
index 26238c2..78b1691 100644
--- a/Changes
+++ b/Changes
@@ -10,8 +10,8 @@ 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.
+    - Add Bernstein+Voloch improvements to AKS.  Much faster than the v6
+      implementation, though still terribly slow vs. BPSW or other proofs.
 
     [OTHER]
 
diff --git a/aks.c b/aks.c
index ed754b5..4154a8b 100644
--- a/aks.c
+++ b/aks.c
@@ -29,13 +29,8 @@
 
 #define SQRTN_SHORTCUT 1
 
-/* Use improvements from Bornemann's 2002 implementation if we have lgamma */
-#if !defined(_MSC_VER) && \
-    (defined(__USE_ISOC99) || defined(_ISOC99_SOURCE) || defined(_NETBSD_SOURCE) || defined(__cplusplus) || !defined(__STRICT_ANSI__) || __STDC_VERSION__ >= 199901L || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
+/* Use improvements from Bornemann's 2002 implementation */
 #define IMPL_BORNEMANN 1
-#else
-#define IMPL_BORNEMANN 0
-#endif
 
 #include "ptypes.h"
 #include "aks.h"
@@ -58,6 +53,28 @@ static int is_primitive_root(UV n, UV r)
   }
   return 1;
 }
+/* We could use lgamma, but it isn't in MSVC and not in pre-C99.  The only
+ * sure way to find if it is available is test compilation (ala autoconf).
+ * Instead, we'll just use our own implementation.
+ * See http://mrob.com/pub/ries/lanczos-gamma.html for alternates. */
+static double lanczos_coef[8+1] =
+{ 0.99999999999980993, 676.5203681218851, -1259.1392167224028,
+  771.32342877765313, -176.61502916214059, 12.507343278686905,
+  -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7 };
+static double log_sqrt_two_pi =  0.91893853320467274178;
+static double log_gamma(double x)
+{
+  double base = x + 7 + 0.5;
+  double sum = 0;
+  int i;
+  for (i = 8; i >= 1; i--)
+    sum += lanczos_coef[i] / (x + (double)i);
+  sum += lanczos_coef[0];
+  sum = log_sqrt_two_pi + logl(sum/x) + ( (x+0.5)*logl(base) - base );
+  return sum;
+}
+#undef lgamma
+#define lgamma(x) log_gamma(x)
 #else
 /* Naive znorder.  Works well here because limit will be very small. */
 static UV order(UV r, UV n, UV limit) {
@@ -254,8 +271,6 @@ int _XS_is_aks_prime(UV n)
       return 1;
 
     s = (UV) floor(sqrt(r-1) * log2n);
-
-    if (verbose) { printf("# aks r = %lu  s = %lu\n", (unsigned long) r, (unsigned long) s); }
   }
 #else
   {
@@ -293,9 +308,10 @@ int _XS_is_aks_prime(UV n)
     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
 
+  if (verbose) { printf("# aks r = %lu  s = %lu\n", (unsigned long) r, (unsigned long) s); }
+
   for (a = 1; a <= s; a++) {
     if (! test_anr(a, n, r) )
       return 0;
diff --git a/t/22-aks-prime.t b/t/22-aks-prime.t
index 58089aa..7418599 100644
--- a/t/22-aks-prime.t
+++ b/t/22-aks-prime.t
@@ -37,11 +37,7 @@ SKIP: {
   # If we're pure Perl, then this is definitely too slow.
   # Arguably we should check to see if they have the GMP code.
   skip "Skipping PP AKS on PP -- just too slow.", 1 if $ispp;
-  # If we have 64-bit available in the compiler (e.g. uint64_t), this can
-  # still be quite fast.  However for pretty much everyone else, this is
-  # just far too slow for running in a test suite.
-  skip "Skipping PP AKS on 32-bit -- just too slow.", 1 if !$use64;
-  # The first number that makes it past the sqrt test to actually run.
+  # The least number that performs the full test with either implementation.
   is( is_aks_prime(69197), 1, "is_aks_prime(69197) is true" );
 }
 
diff --git a/xt/primality-aks.pl b/xt/primality-aks.pl
index c0cf7f5..4107acc 100755
--- a/xt/primality-aks.pl
+++ b/xt/primality-aks.pl
@@ -5,16 +5,22 @@ $| = 1;  # fast pipes
 
 my $limit = shift || 10_000_000;
 
-use Math::Prime::Util qw/is_aks_prime/;
-use Math::Prime::FastSieve;
-my $sieve = Math::Prime::FastSieve::Sieve->new($limit + 10_000);
+use Math::Prime::Util qw/is_aks_prime next_prime/;
+
+# It doesn't really matter for this use, but try to use independent code for
+# next_prime.  Math::Prime::FastSieve works well.
+my $sieve;
+if (eval { require Math::Prime::FastSieve; Math::Prime::FastSieve->import(); require Math::Prime::FastSieve::Sieve; 1; }) {
+  $sieve = Math::Prime::FastSieve::Sieve->new($limit + 10_000);
+}
 
 if (1) {
   my $n = 2;
+  my $i = 1;
   while ($n <= $limit) {
-    print "$n\n" if $n > 69000; # unless $i++ % 262144;
+    print "$n\n" unless $i++ % 1000;
     die "$n should be prime" unless is_aks_prime($n);
-    my $next = $sieve->nearest_ge( $n+1 );
+    my $next = (defined $sieve) ? $sieve->nearest_ge( $n+1 ) : next_prime($n);
     my $diff = ($next - $n) >> 1;
     if ($diff > 1) {
       foreach my $d (1 .. $diff-1) {

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