/* -*-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
*
/*----- 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.
*
* 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)
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 --- */
/* --- 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);
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);
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);
}
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);
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);
}
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);
}
/* --- 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);
} 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);
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);
}
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);
}