[libmath-prime-util-perl] 49/181: Add kronecker, totient, carmichael_lambda, znprimroot

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


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

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

commit 272b94859bd28b9a36c9d01fbfda7d73e27e10e5
Author: Dana Jacobsen <dana at acm.org>
Date:   Fri Dec 27 18:21:50 2013 -0800

    Add kronecker, totient, carmichael_lambda, znprimroot
---
 util.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 util.h |  18 ++++++++++-
 2 files changed, 132 insertions(+), 1 deletion(-)

diff --git a/util.c b/util.c
index 510426e..dd05c95 100644
--- a/util.c
+++ b/util.c
@@ -57,11 +57,14 @@
 
 #include "ptypes.h"
 #define FUNC_isqrt 1
+#define FUNC_lcm_ui 1
 #include "util.h"
 #include "sieve.h"
 #include "primality.h"
 #include "cache.h"
 #include "lmo.h"
+#include "factor.h"
+#include "mulmod.h"
 
 static int _verbose = 0;
 void _XS_set_verbose(int v) { _verbose = v; }
@@ -885,6 +888,118 @@ IV _XS_mertens(UV n) {
   return sum;
 }
 
+
+static int padic2(UV n) {  /* How many times does 2 divide n? */
+  UV p = 0;
+  while (n && n % 2 == 0) { n >>= 1;  p++; }
+  return p;
+}
+#define IS_MOD8_3OR5(x)  (((x)&7)==3 || ((x)&7)==5)
+
+static int kronecker_uu_sign(UV a, UV b, int s) {
+  while (a) {
+    int r = padic2(a);
+    if (r) {
+      if ((r&1)  &&  IS_MOD8_3OR5(b))  s = -s;
+      a >>= r;
+    }
+    if (a & b & 2)  s = -s;
+    { UV t = b % a;  b = a;  a = t; }
+  }
+  return (b == 1) ? s : 0;
+}
+
+int kronecker_uu(UV a, UV b) {
+  int r, s;
+  if (b & 1)   return kronecker_uu_sign(a, b, 1);
+  if (!(a&1))  return 0;
+  s = 1;
+  r = padic2(b);
+  if (r) {
+    if ((r&1) && IS_MOD8_3OR5(a))  s = -s;
+    b >>= r;
+  }
+  return kronecker_uu_sign(a, b, s);
+}
+
+int kronecker_su(IV a, UV b) {
+  int r, s;
+  if (a >= 0)  return kronecker_uu(a, b);
+  if (b == 0)  return (a == 1 || a == -1) ? 1 : 0;
+  s = 1;
+  r = padic2(b);
+  if (r) {
+    if (!(a&1))  return 0;
+    if ((r&1) && IS_MOD8_3OR5(a))  s = -s;
+    b >>= r;
+  }
+  a %= (IV) b;
+  if (a < 0)  a += b;
+  return kronecker_uu_sign(a, b, s);
+}
+
+int kronecker_ss(IV a, IV b) {
+  if (a >= 0 && b >= 0)
+    return (b & 1)  ?  kronecker_uu_sign(a, b, 1)  :  kronecker_uu(a,b);
+  if (b >= 0)
+    return kronecker_su(a, b);
+  return kronecker_su(a, -b) * ((a < 0) ? -1 : 1);
+}
+
+UV totient(UV n) {
+  UV i, facs[MPU_MAX_FACTORS+1];
+  UV nfacs = factor(n, facs);
+  UV totient = 1;
+  UV lastf = 0;
+  if (n <= 1) return n;
+  for (i = 0; i < nfacs; i++) {
+    UV f = facs[i];
+    if (f == lastf) { totient *= f;               }
+    else            { totient *= f-1;  lastf = f; }
+  }
+  return totient;
+}
+
+UV carmichael_lambda(UV n) {
+  UV fac[MPU_MAX_FACTORS+1];
+  UV exp[MPU_MAX_FACTORS+1];
+  int i, j, nfactors;
+  UV lambda = 1;
+
+  if (n < 8) return totient(n);
+  if ((n & (n-1)) == 0)  return totient(n)/2;
+
+  nfactors = factor_exp(n, fac, exp);
+  if (fac[0] == 2 && exp[0] > 2)  exp[0]--;
+  for (i = 0; i < nfactors; i++) {
+    UV pk = fac[i]-1;
+    for (j = 1; j < exp[i]; j++)
+      pk *= fac[i];
+    lambda = lcm_ui(lambda, pk);
+  }
+  return lambda;
+}
+  
+UV znprimroot(UV n) {
+  UV fac[MPU_MAX_FACTORS+1];
+  UV exp[MPU_MAX_FACTORS+1];
+  UV a, phi;
+  int i, nfactors;
+  if (n <= 4) return (n == 0) ? 0 : n-1;
+  phi = totient(n);
+  nfactors = factor_exp(phi, fac, exp);
+  for (i = 0; i < nfactors; i++)
+    exp[i] = phi / fac[i];  /* exp[i] = phi(n) / i-th-factor-of-phi(n) */
+  for (a = 2; a < n; a++) {
+    if (kronecker_uu(a, n) == 0)  continue;
+    for (i = 0; i < nfactors; i++) {
+      if (powmod(a, exp[i], n) == 1) break;
+    }
+    if (i == nfactors) return a;
+  }
+}
+
+
 double _XS_chebyshev_theta(UV n)
 {
   KAHAN_INIT(sum);
diff --git a/util.h b/util.h
index 4dbcc6e..b420c20 100644
--- a/util.h
+++ b/util.h
@@ -27,6 +27,15 @@ extern long double ld_riemann_zeta(long double x);
 extern double _XS_RiemannR(double x);
 extern UV _XS_Inverse_Li(UV x);
 
+extern int kronecker_uu(UV a, UV b);
+extern int kronecker_su(IV a, UV b);
+extern int kronecker_ss(IV a, IV b);
+
+extern UV totient(UV n);
+extern UV carmichael_lambda(UV n);
+extern UV znprimroot(UV n);
+extern UV znorder(UV n);
+
 /* Above this value, is_prime will do deterministic Miller-Rabin */
 /* With 64-bit math, we can do much faster mulmods from 2^16-2^32 */
 #if (BITS_PER_WORD == 64) || HAVE_STD_U64
@@ -72,7 +81,7 @@ static UV icbrt(UV n) {
 }
 #endif
 
-#ifdef FUNC_gcd_ui
+#if defined(FUNC_gcd_ui) || defined(FUNC_lcm_ui)
 static UV gcd_ui(UV x, UV y) {
   UV t;
   if (y < x) { t = x; x = y; y = t; }
@@ -83,4 +92,11 @@ static UV gcd_ui(UV x, UV y) {
 }
 #endif
 
+#ifdef FUNC_lcm_ui
+static UV lcm_ui(UV x, UV y) {
+  /* Can overflow if lcm(x,y) > 2^64 (e.g. two primes each > 2^32) */
+  return x * (y / gcd_ui(x,y));
+}
+#endif
+
 #endif

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