mpmul.[ch]: Move internal `HWM' and `LWM' constants to implementation.
[u/mdw/catacomb] / mp-jacobi.c
index 2a1e109..3674f22 100644 (file)
@@ -1,13 +1,13 @@
 /* -*-c-*-
  *
- * $Id: mp-jacobi.c,v 1.2 1999/12/10 23:19:02 mdw Exp $
+ * $Id$
  *
  * Compute Jacobi symbol
  *
  * (c) 1999 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of Catacomb.
  *
  * it under the terms of the GNU Library General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
- * 
+ *
  * Catacomb 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 Library General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Library General Public
  * License along with Catacomb; if not, write to the Free
  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  * MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------* 
- *
- * $Log: mp-jacobi.c,v $
- * Revision 1.2  1999/12/10 23:19:02  mdw
- * Improve error-checking.
- *
- * Revision 1.1  1999/11/22 20:50:37  mdw
- * Add support for computing Jacobi symbols.
- *
- */
-
 /*----- Header files ------------------------------------------------------*/
 
 #include "mp.h"
 
 /* --- @mp_jacobi@ --- *
  *
- * Arguments:  @mp *a@ = an integer less than @n@
- *             @mp *n@ = an odd integer
+ * Arguments:  @mp *a@ = an integer
+ *             @mp *n@ = another integer
  *
  * Returns:    @-1@, @0@ or @1@ -- the Jacobi symbol %$J(a, n)$%.
  *
- * Use:                Computes the Jacobi symbol.  If @n@ is prime, this is the
- *             Legendre symbol and is equal to 1 if and only if @a@ is a
- *             quadratic residue mod @n@.  The result is zero if and only if
- *             @a@ and @n@ have a common factor greater than one.
+ * Use:                Computes the Kronecker symbol %$\jacobi{a}{n}$%.  If @n@ is
+ *             prime, this is the Legendre symbol and is equal to 1 if and
+ *             only if @a@ is a quadratic residue mod @n@.  The result is
+ *             zero if and only if @a@ and @n@ have a common factor greater
+ *             than one.
+ *
+ *             If @n@ is composite, then this computes the Kronecker symbol
+ *
+ *               %$\jacobi{a}{n}=\jacobi{a}{u}\prod_i\jacobi{a}{p_i}^{e_i}$%
+ *
+ *             where %$n = u p_0^{e_0} \ldots p_{n-1}^{e_{n-1}}$% is the
+ *             prime factorization of %$n$%.  The missing bits are:
+ *
+ *               * %$\jacobi{a}{1} = 1$%;
+ *               * %$\jacobi{a}{-1} = 1$% if @a@ is negative, or 1 if
+ *                 positive;
+ *               * %$\jacobi{a}{0} = 0$%;
+ *               * %$\jacobi{a}{2}$ is 0 if @a@ is even, 1 if @a@ is
+ *                 congruent to 1 or 7 (mod 8), or %$-1$% otherwise.
+ *
+ *             If %$n$% is positive and odd, then this is the Jacobi
+ *             symbol.  (The Kronecker symbol is a consistant domain
+ *             extension; the Jacobi symbol was implemented first, and the
+ *             name stuck.)
  */
 
 int mp_jacobi(mp *a, mp *n)
 {
   int s = 1;
+  size_t p2;
+
+  /* --- Handle zero specially --- *
+   *
+   * I can't find any specific statement for what to do when %$n = 0$%; PARI
+   * opts to set %$\jacobi{\pm1}{0} = \pm 1$% and %$\jacobi{a}{0} = 0$% for
+   * other %$a$%.
+   */
+
+  if (MP_ZEROP(n)) {
+    if (MP_EQ(a, MP_ONE)) return (+1);
+    else if (MP_EQ(a, MP_MONE)) return (-1);
+    else return (0);
+  }
 
-  /* --- Take copies of the arguments --- */
-
-  a = MP_COPY(a);
-  n = MP_COPY(n);
-
-  /* --- Main recursive mess, flattened out into something nice --- */
-
-  for (;;) {
-
-    /* --- Some simple special cases --- */
-
-    MP_SHRINK(a);
+  /* --- Deal with powers of two --- *
+   *
+   * This implicitly takes a copy of %$n$%.  Copy %$a$% at the same time to
+   * make cleanup easier.
+   */
 
-    if (MP_LEN(a) == 0) {
+  MP_COPY(a);
+  n = mp_odd(MP_NEW, n, &p2);
+  if (p2) {
+    if (MP_EVENP(a)) {
       s = 0;
       goto done;
-    }
+    } else if ((p2 & 1) && ((a->v[0] & 7) == 3 || (a->v[0] & 7) == 5))
+      s = -s;
+  }
 
-    /* --- Find the power-of-two factor in @a@ --- */
+  /* --- Deal with negative %$n$% --- */
 
-    {
-      mpscan sc;
-      mpw nn;
-      unsigned e;
+  if (MP_NEGP(n)) {
+    n = mp_neg(n, n);
+    if (MP_NEGP(a))
+      s = -s;
+  }
 
-      /* --- Scan for a set bit --- */
+  /* --- Check for unit %$n$% --- */
 
-      MP_SCAN(&sc, a);
-      e = 0;
-      while (MP_STEP(&sc) && !MP_BIT(&sc))
-       e++;
+  if (MP_EQ(n, MP_ONE))
+    goto done;
 
-      /* --- Do the shift --- */
+  /* --- Reduce %$a$% modulo %$n$% --- */
 
-      if (e)
-       a = mp_lsr(a, a, e);
+  if (MP_NEGP(a) || MP_CMP(a, >=, n))
+    mp_div(0, &a, a, n);
 
-      /* --- Maybe adjust the sign of @s@ --- */
+  /* --- Main recursive mess, flattened out into something nice --- */
 
-      nn = n->v[0] & 7;
-      if ((e & 1) && (nn == 3 || nn == 5))
-       s = -s;
+  for (;;) {
+    mpw nn;
+    size_t e;
 
-      if (MP_LEN(a) == 1 && a->v[0] == 1)
-       goto done;
+    /* --- Some simple special cases --- */
 
-      if ((nn & 3) == 3 && (a->v[0] & 3) == 3)
-       s = -s;
+    MP_SHRINK(a);
+    if (MP_ZEROP(a)) {
+      s = 0;
+      goto done;
     }
 
+    /* --- Main case with powers of two --- */
+
+    a = mp_odd(a, a, &e);
+    nn = n->v[0] & 7;
+    if ((e & 1) && (nn == 3 || nn == 5))
+      s = -s;
+    if (MP_LEN(a) == 1 && a->v[0] == 1)
+      goto done;
+    if ((nn & 3) == 3 && (a->v[0] & 3) == 3)
+      s = -s;
+
     /* --- Reduce and swap --- */
 
     mp_div(0, &n, n, a);