Change interface for suggested destinations.
[u/mdw/catacomb] / mp-gcd.c
index 19192f5..c772512 100644 (file)
--- a/mp-gcd.c
+++ b/mp-gcd.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: mp-gcd.c,v 1.2 1999/11/22 20:49:56 mdw Exp $
+ * $Id: mp-gcd.c,v 1.3 1999/12/10 23:18:39 mdw Exp $
  *
  * Extended GCD calculation
  *
@@ -30,6 +30,9 @@
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: mp-gcd.c,v $
+ * Revision 1.3  1999/12/10 23:18:39  mdw
+ * Change interface for suggested destinations.
+ *
  * Revision 1.2  1999/11/22 20:49:56  mdw
  * Fix bug which failed to favour `x' when `y' wasn't wanted and the two
  * arguments needed swapping.
@@ -54,9 +57,7 @@
  *
  * Use:                Calculates @gcd(a, b)@, and two numbers @x@ and @y@ such that
  *             @ax + by = gcd(a, b)@.  This is useful for computing modular
- *             inverses.  Neither @a@ nor @b@ may be zero.  Note that,
- *             unlike @mp_div@ for example, it is not possible to specify
- *             explicit destinations -- new MPs are always allocated.
+ *             inverses.
  */
 
 void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
@@ -65,14 +66,73 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
   mp *x = MP_ZERO, *y = MP_ONE;
   mp *u, *v;
   size_t shift = 0;
-  int ext = xx || yy;
-  int swap = 0;
+  unsigned f = 0;
+
+  enum {
+    f_swap = 1u,
+    f_aneg = 2u,
+    f_bneg = 4u,
+    f_ext = 8u
+  };
+
+  /* --- Sort out some initial flags --- */
 
-  /* --- Ensure that @a@ is larger than @b@ --- */
+  if (xx || yy)
+    f |= f_ext;
 
-  if (MP_CMP(a, <, b)) {
+  if (a->f & MP_NEG)
+    f |= f_aneg;
+  if (b->f & MP_NEG)
+    f |= f_bneg;
+
+  /* --- Ensure that @a@ is larger than @b@ --- *
+   *
+   * Use absolute values here!
+   */
+
+  if (MPX_UCMP(a->v, a->vl, <, b->v, b->vl)) {
     { mp *t = a; a = b; b = t; }
-    swap = 1;
+    f |= f_swap;
+  }
+
+  /* --- Check for zeroness --- */
+
+  if (MP_CMP(b, ==, MP_ZERO)) {
+
+    /* --- Store %$|a|$% as the GCD --- */
+
+    if (gcd) {
+      if (*gcd) MP_DROP(*gcd);
+      a = MP_COPY(a);
+      if (a->f & MP_NEG) {
+       MP_SPLIT(a);
+       a->f &= ~MP_NEG;
+       f |= f_aneg;
+      }
+      *gcd = a;
+    }
+
+    /* --- Store %$1$% and %$0$% in the appropriate bins --- */
+
+    if (f & f_ext) {
+      if (f & f_swap) {
+       mp **t = xx; xx = yy; yy = t;
+      }
+      if (xx) {
+       if (*xx) MP_DROP(*xx);
+       if (MP_CMP(a, ==, MP_ZERO))
+         *xx = MP_ZERO;
+       else if (f & f_aneg)
+         *xx = MP_MONE;
+       else
+         *xx = MP_ONE;
+      }
+      if (yy) {
+       if (*yy) MP_DROP(*yy);
+       *yy = MP_ZERO;
+      }
+    }
+    return;
   }
 
   /* --- Take a reference to the arguments --- */
@@ -82,13 +142,14 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
 
   /* --- Make sure @a@ and @b@ are not both even --- */
 
+  MP_SPLIT(a); a->f &= ~MP_NEG;
+  MP_SPLIT(b); b->f &= ~MP_NEG;
+
   if (((a->v[0] | b->v[0]) & 1) == 0) {
     mpscan asc, bsc;
 
     /* --- Break off my copies --- */
 
-    MP_SPLIT(a);
-    MP_SPLIT(b);
     MP_SCAN(&asc, a);
     MP_SCAN(&bsc, b);
 
@@ -130,7 +191,7 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
        MP_STEP(&xsc); MP_STEP(&ysc);
        if (MP_BIT(&sc))
          break;
-       if (ext && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
+       if ((f & f_ext) && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
          if (n) {
            X = mp_lsr(X, X, n);
            Y = mp_lsr(Y, Y, n);
@@ -147,7 +208,7 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
 
       if (nn) {
        u = mp_lsr(u, u, nn);
-       if (ext && n) {
+       if ((f & f_ext) && n) {
          X = mp_lsr(X, X, n);
          Y = mp_lsr(Y, Y, n);
        }
@@ -167,7 +228,7 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
        MP_STEP(&xsc); MP_STEP(&ysc);
        if (MP_BIT(&sc))
          break;
-       if (ext && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
+       if ((f & f_ext) && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
          if (n) {
            x = mp_lsr(x, x, n);
            y = mp_lsr(y, y, n);
@@ -184,7 +245,7 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
 
       if (nn) {
        v = mp_lsr(v, v, nn);
-       if (ext && n) {
+       if ((f & f_ext) && n) {
          x = mp_lsr(x, x, n);
          y = mp_lsr(y, y, n);
        }
@@ -195,13 +256,13 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
 
     if (MP_CMP(u, >=, v)) {
       u = mp_sub(u, u, v);
-      if (ext) {
+      if (f & f_ext) {
        X = mp_sub(X, X, x);
        Y = mp_sub(Y, Y, y);
       }
     } else {
       v = mp_sub(v, v, u);
-      if (ext) {
+      if (f & f_ext) {
        x = mp_sub(x, x, X);
        y = mp_sub(y, y, Y);
       }
@@ -213,22 +274,34 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
 
   /* --- Write the results out --- */
 
-  if (gcd)
-    *gcd = mp_lsl(v, v, shift);
-  else
+  if (!gcd)
     MP_DROP(v);
+  else {
+    if (*gcd) MP_DROP(*gcd);
+    *gcd = mp_lsl(v, v, shift);
+  }
 
   /* --- Perform a little normalization --- *
    *
    * Ensure that the coefficient returned is positive, if there is only one.
-   * If there are two, favour @y@.
+   * If there are two, favour @y@.  Of course, if the original arguments were
+   * negative then I'll need to twiddle their signs as well.
    */
 
-  if (ext) {
-    if (swap) {
+  if (f & f_ext) {
+
+    /* --- If @a@ and @b@ got swapped, swap the coefficients back --- */
+
+    if (f & f_swap) {
       mp *t = x; x = y; y = t;
       t = a; a = b; b = t;
     }
+
+    /* --- Sort out the signs --- *
+     *
+     * Note that %$ax + by = a(x - b) + b(y + a)$%.
+     */
+
     if (yy) {
       if (y->f & MP_NEG) {
        y = mp_add(y, y, a);
@@ -237,8 +310,28 @@ void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
     } else if (x->f & MP_NEG)
       x = mp_add(x, x, b);
 
-    if (xx) *xx = x; else MP_DROP(x);
-    if (yy) *yy = y; else MP_DROP(y);
+    /* --- Twiddle the signs --- */
+
+    if (f & f_aneg)
+      x->f ^= MP_NEG;
+    if (f & f_bneg)
+      y->f ^= MP_NEG;
+
+    /* --- Store the results --- */
+
+    if (!xx)
+      MP_DROP(x);
+    else {
+      if (*xx) MP_DROP(*xx);
+      *xx = x;
+    }
+
+    if (!yy)
+      MP_DROP(y);
+    else {
+      if (*yy) MP_DROP(*yy);
+      *yy = y;
+    }
   }
 
   MP_DROP(u);
@@ -259,7 +352,7 @@ static int gcd(dstr *v)
   mp *x = *(mp **)v[3].buf;
   mp *y = *(mp **)v[4].buf;
 
-  mp *gg, *xx, *yy;
+  mp *gg = MP_NEW, *xx = MP_NEW, *yy = MP_NEW;
   mp_gcd(&gg, &xx, &yy, a, b);
   if (MP_CMP(x, !=, xx)) {
     fputs("\n*** mp_gcd(x) failed", stderr);
@@ -301,6 +394,7 @@ static int gcd(dstr *v)
   }
   MP_DROP(a); MP_DROP(b); MP_DROP(g); MP_DROP(x); MP_DROP(y);
   MP_DROP(gg); MP_DROP(xx); MP_DROP(yy);
+  assert(mparena_count(MPARENA_GLOBAL) == 0);
   return (ok);
 }