General utilities cleanup. Add signature support to catcrypt. Throw in
[u/mdw/catacomb] / mpx.c
diff --git a/mpx.c b/mpx.c
index 327699c..e122760 100644 (file)
--- a/mpx.c
+++ b/mpx.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: mpx.c,v 1.9 2000/06/26 07:52:50 mdw Exp $
+ * $Id: mpx.c,v 1.20 2004/04/08 01:36:15 mdw Exp $
  *
  * Low-level multiprecision arithmetic
  *
  * MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------* 
- *
- * $Log: mpx.c,v $
- * Revision 1.9  2000/06/26 07:52:50  mdw
- * Portability fix for the bug fix.
- *
- * Revision 1.8  2000/06/25 12:59:02  mdw
- * (mpx_udiv): Fix bug in quotient digit estimation.
- *
- * Revision 1.7  1999/12/22 15:49:07  mdw
- * New function for division by a small integer.
- *
- * Revision 1.6  1999/11/20 22:43:44  mdw
- * Integrate testing for MPX routines.
- *
- * Revision 1.5  1999/11/20 22:23:27  mdw
- * Add function versions of some low-level macros with wider use.
- *
- * Revision 1.4  1999/11/17 18:04:09  mdw
- * Add two's-complement functionality.  Improve mpx_udiv a little by
- * performing the multiplication of the divisor by q with the subtraction
- * from r.
- *
- * Revision 1.3  1999/11/13 01:57:31  mdw
- * Remove stray debugging code.
- *
- * Revision 1.2  1999/11/13 01:50:59  mdw
- * Multiprecision routines finished and tested.
- *
- * Revision 1.1  1999/09/03 08:41:12  mdw
- * Initial import.
- *
- */
-
 /*----- Header files ------------------------------------------------------*/
 
 #include <assert.h>
@@ -72,6 +38,7 @@
 
 #include "mptypes.h"
 #include "mpx.h"
+#include "bitops.h"
 
 /*----- Loading and storing -----------------------------------------------*/
 
@@ -227,6 +194,186 @@ void mpx_loadb(mpw *v, mpw *vl, const void *pp, size_t sz)
   MPX_ZERO(v, vl);
 }
 
+/* --- @mpx_storel2cn@ --- *
+ *
+ * Arguments:  @const mpw *v, *vl@ = base and limit of source vector
+ *             @void *pp@ = pointer to octet array
+ *             @size_t sz@ = size of octet array
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores a negative MP in an octet array, least significant
+ *             octet first, as two's complement.  High-end octets are
+ *             silently discarded if there isn't enough space for them.
+ *             This obviously makes the output bad.
+ */
+
+void mpx_storel2cn(const mpw *v, const mpw *vl, void *pp, size_t sz)
+{
+  unsigned c = 1;
+  unsigned b = 0;
+  mpw n, w = 0;
+  octet *p = pp, *q = p + sz;
+  unsigned bits = 0;
+
+  while (p < q) {
+    if (bits < 8) {
+      if (v >= vl) {
+       b = w;
+       break;
+      }
+      n = *v++;
+      b = w | n << bits;
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      b = w;
+      w >>= 8;
+      bits -= 8;
+    }
+    b = U8(~b + c);
+    c = c && !b;
+    *p++ = b;
+  }
+  while (p < q) {
+    b = U8(~b + c);
+    c = c && !b;
+    *p++ = b;
+    b = 0;
+  }
+}
+
+/* --- @mpx_loadl2cn@ --- *
+ *
+ * Arguments:  @mpw *v, *vl@ = base and limit of destination vector
+ *             @const void *pp@ = pointer to octet array
+ *             @size_t sz@ = size of octet array
+ *
+ * Returns:    ---
+ *
+ * Use:                Loads a negative MP in an octet array, least significant
+ *             octet first, as two's complement.  High-end octets are
+ *             ignored if there isn't enough space for them.  This probably
+ *             means you made the wrong choice coming here.
+ */
+
+void mpx_loadl2cn(mpw *v, mpw *vl, const void *pp, size_t sz)
+{
+  unsigned n;
+  unsigned c = 1;
+  mpw w = 0;
+  const octet *p = pp, *q = p + sz;
+  unsigned bits = 0;
+
+  if (v >= vl)
+    return;
+  while (p < q) {
+    n = U8(~(*p++) + c);
+    c = c && !n;
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = MPW(w);
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+      if (v >= vl)
+       return;
+    }
+  }
+  *v++ = w;
+  MPX_ZERO(v, vl);
+}
+
+/* --- @mpx_storeb2cn@ --- *
+ *
+ * Arguments:  @const mpw *v, *vl@ = base and limit of source vector
+ *             @void *pp@ = pointer to octet array
+ *             @size_t sz@ = size of octet array
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores a negative MP in an octet array, most significant
+ *             octet first, as two's complement.  High-end octets are
+ *             silently discarded if there isn't enough space for them,
+ *             which probably isn't what you meant.
+ */
+
+void mpx_storeb2cn(const mpw *v, const mpw *vl, void *pp, size_t sz)
+{
+  mpw n, w = 0;
+  unsigned b = 0;
+  unsigned c = 1;
+  octet *p = pp, *q = p + sz;
+  unsigned bits = 0;
+
+  while (q > p) {
+    if (bits < 8) {
+      if (v >= vl) {
+       b = w;
+       break;
+      }
+      n = *v++;
+      b = w | n << bits;
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      b = w;
+      w >>= 8;
+      bits -= 8;
+    }
+    b = U8(~b + c);
+    c = c && !b;
+    *--q = b;
+  }
+  while (q > p) {
+    b = ~b + c;
+    c = c && !(b & 0xff);
+    *--q = b;
+    b = 0;
+  }
+}
+
+/* --- @mpx_loadb2cn@ --- *
+ *
+ * Arguments:  @mpw *v, *vl@ = base and limit of destination vector
+ *             @const void *pp@ = pointer to octet array
+ *             @size_t sz@ = size of octet array
+ *
+ * Returns:    ---
+ *
+ * Use:                Loads a negative MP in an octet array, most significant octet
+ *             first as two's complement.  High-end octets are ignored if
+ *             there isn't enough space for them.  This probably means you
+ *             chose this function wrongly.
+ */
+
+void mpx_loadb2cn(mpw *v, mpw *vl, const void *pp, size_t sz)
+{
+  unsigned n;
+  unsigned c = 1;
+  mpw w = 0;
+  const octet *p = pp, *q = p + sz;
+  unsigned bits = 0;
+
+  if (v >= vl)
+    return;
+  while (q > p) {
+    n = U8(~(*--q) + c);
+    c = c && !n;
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = MPW(w);
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+      if (v >= vl)
+       return;
+    }
+  }
+  *v++ = w;
+  MPX_ZERO(v, vl);
+}
+
 /*----- Logical shifting --------------------------------------------------*/
 
 /* --- @mpx_lsl@ --- *
@@ -277,8 +424,12 @@ void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
   /* --- Handle a shift by a multiple of the word size --- */
 
   if (nb == 0) {
-    MPX_COPY(dv + nw, dvl, av, avl);
-    memset(dv, 0, MPWS(nw));
+    if (nw >= dvl - dv)
+      MPX_ZERO(dv, dvl);
+    else {
+      MPX_COPY(dv + nw, dvl, av, avl);
+      memset(dv, 0, MPWS(nw));
+    }
   }
 
   /* --- And finally the difficult case --- *
@@ -322,6 +473,104 @@ void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
 done:;
 }
 
+/* --- @mpx_lslc@ --- *
+ *
+ * Arguments:  @mpw *dv, *dvl@ = destination vector base and limit
+ *             @const mpw *av, *avl@ = source vector base and limit
+ *             @size_t n@ = number of bit positions to shift by
+ *
+ * Returns:    ---
+ *
+ * Use:                Performs a logical shift left operation on an integer, only
+ *             it fills in the bits with ones instead of zeroes.
+ */
+
+void mpx_lslc(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
+{
+  size_t nw;
+  unsigned nb;
+
+  /* --- Trivial special case --- */
+
+  if (n == 0)
+    MPX_COPY(dv, dvl, av, avl);
+
+  /* --- Single bit shifting --- */
+
+  else if (n == 1) {
+    mpw w = 1;
+    while (av < avl) {
+      mpw t;
+      if (dv >= dvl)
+       goto done;
+      t = *av++;
+      *dv++ = MPW((t << 1) | w);
+      w = t >> (MPW_BITS - 1);
+    }
+    if (dv >= dvl)
+      goto done;
+    *dv++ = MPW(w);
+    MPX_ZERO(dv, dvl);
+    goto done;
+  }
+
+  /* --- Break out word and bit shifts for more sophisticated work --- */
+       
+  nw = n / MPW_BITS;
+  nb = n % MPW_BITS;
+
+  /* --- Handle a shift by a multiple of the word size --- */
+
+  if (nb == 0) {
+    if (nw >= dvl - dv)
+      MPX_ONE(dv, dvl);
+    else {
+      MPX_COPY(dv + nw, dvl, av, avl);
+      MPX_ONE(dv, dv + nw);
+    }
+  }
+
+  /* --- And finally the difficult case --- *
+   *
+   * This is a little convoluted, because I have to start from the end and
+   * work backwards to avoid overwriting the source, if they're both the same
+   * block of memory.
+   */
+
+  else {
+    mpw w;
+    size_t nr = MPW_BITS - nb;
+    size_t dvn = dvl - dv;
+    size_t avn = avl - av;
+
+    if (dvn <= nw) {
+      MPX_ONE(dv, dvl);
+      goto done;
+    }
+
+    if (dvn > avn + nw) {
+      size_t off = avn + nw + 1;
+      MPX_ZERO(dv + off, dvl);
+      dvl = dv + off;
+      w = 0;
+    } else {
+      avl = av + dvn - nw;
+      w = *--avl << nb;
+    }
+
+    while (avl > av) {
+      mpw t = *--avl;
+      *--dvl = (t >> nr) | w;
+      w = t << nb;
+    }
+
+    *--dvl = (MPW_MAX >> nr) | w;
+    MPX_ONE(dv, dvl);
+  }
+
+done:;
+}
+
 /* --- @mpx_lsr@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination vector base and limit
@@ -346,7 +595,7 @@ void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
   /* --- Single bit shifting --- */
 
   else if (n == 1) {
-    mpw w = *av++ >> 1;
+    mpw w = av < avl ? *av++ >> 1 : 0;
     while (av < avl) {
       mpw t;
       if (dv >= dvl)
@@ -369,8 +618,12 @@ void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
 
   /* --- Handle a shift by a multiple of the word size --- */
 
-  if (nb == 0)
-    MPX_COPY(dv, dvl, av + nw, avl);
+  if (nb == 0) {
+    if (nw >= avl - av)
+      MPX_ZERO(dv, dvl);
+    else
+      MPX_COPY(dv, dvl, av + nw, avl);
+  }
 
   /* --- And finally the difficult case --- */
 
@@ -379,7 +632,7 @@ void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
     size_t nr = MPW_BITS - nb;
 
     av += nw;
-    w = *av++;
+    w = av < avl ? *av++ : 0;
     while (av < avl) {
       mpw t;
       if (dv >= dvl)
@@ -397,6 +650,48 @@ void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
 done:;
 }
 
+/*----- Bitwise operations ------------------------------------------------*/
+
+/* --- @mpx_bitop@ --- *
+ *
+ * Arguments:  @mpw *dv, *dvl@ = destination vector
+ *             @const mpw *av, *avl@ = first source vector
+ *             @const mpw *bv, *bvl@ = second source vector
+ *
+ * Returns:    ---
+ *
+ * Use;                Provides the dyadic boolean functions.
+ */
+
+#define MPX_BITBINOP(string)                                           \
+                                                                       \
+void mpx_bit##string(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, \
+                    const mpw *bv, const mpw *bvl)                     \
+{                                                                      \
+  MPX_SHRINK(av, avl);                                                 \
+  MPX_SHRINK(bv, bvl);                                                 \
+                                                                       \
+  while (dv < dvl) {                                                   \
+    mpw a, b;                                                          \
+    a = (av < avl) ? *av++ : 0;                                                \
+    b = (bv < bvl) ? *bv++ : 0;                                                \
+    *dv++ = B##string(a, b);                                           \
+  }                                                                    \
+}
+
+MPX_DOBIN(MPX_BITBINOP)
+
+void mpx_not(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl)
+{
+  MPX_SHRINK(av, avl);
+
+  while (dv < dvl) {
+    mpw a;
+    a = (av < avl) ? *av++ : 0;
+    *dv++ = ~a;
+  }
+}
+
 /*----- Unsigned arithmetic -----------------------------------------------*/
 
 /* --- @mpx_2c@ --- *
@@ -423,6 +718,29 @@ void mpx_2c(mpw *dv, mpw *dvl, const mpw *v, const mpw *vl)
   MPX_UADDN(dv, dvl, 1);
 }
 
+/* --- @mpx_ueq@ --- *
+ *
+ * Arguments:  @const mpw *av, *avl@ = first argument vector base and limit
+ *             @const mpw *bv, *bvl@ = second argument vector base and limit
+ *
+ * Returns:    Nonzero if the two vectors are equal.
+ *
+ * Use:                Performs an unsigned integer test for equality.
+ */
+
+int mpx_ueq(const mpw *av, const mpw *avl, const mpw *bv, const mpw *bvl)
+{
+  MPX_SHRINK(av, avl);
+  MPX_SHRINK(bv, bvl);
+  if (avl - av != bvl - bv)
+    return (0);
+  while (av < avl) {
+    if (*av++ != *bv++)
+      return (0);
+  }
+  return (1);
+}
+
 /* --- @mpx_ucmp@ --- *
  *
  * Arguments:  @const mpw *av, *avl@ = first argument vector base and limit
@@ -453,7 +771,7 @@ int mpx_ucmp(const mpw *av, const mpw *avl, const mpw *bv, const mpw *bvl)
   }
   return (0);
 }
-  
+
 /* --- @mpx_uadd@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination vector base and limit
@@ -504,6 +822,30 @@ void mpx_uadd(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
 
 void mpx_uaddn(mpw *dv, mpw *dvl, mpw n) { MPX_UADDN(dv, dvl, n); }
 
+/* --- @mpx_uaddnlsl@ --- *
+ *
+ * Arguments:  @mpw *dv, *dvl@ = destination and first argument vector
+ *             @mpw a@ = second argument
+ *             @unsigned o@ = offset in bits
+ *
+ * Returns:    ---
+ *
+ * Use:                Computes %$d + 2^o a$%.  If the result overflows then
+ *             high-order bits are discarded, as usual.  We must have
+ *             @0 < o < MPW_BITS@.
+ */
+
+void mpx_uaddnlsl(mpw *dv, mpw *dvl, mpw a, unsigned o)
+{
+  mpd x = (mpd)a << o;
+
+  while (x && dv < dvl) {
+    x += *dv;
+    *dv++ = MPW(x);
+    x >>= MPW_BITS;
+  }
+}
+
 /* --- @mpx_usub@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination vector base and limit
@@ -558,6 +900,33 @@ void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
 
 void mpx_usubn(mpw *dv, mpw *dvl, mpw n) { MPX_USUBN(dv, dvl, n); }
 
+/* --- @mpx_uaddnlsl@ --- *
+ *
+ * Arguments:  @mpw *dv, *dvl@ = destination and first argument vector
+ *             @mpw a@ = second argument
+ *             @unsigned o@ = offset in bits
+ *
+ * Returns:    ---
+ *
+ * Use:                Computes %$d + 2^o a$%.  If the result overflows then
+ *             high-order bits are discarded, as usual.  We must have
+ *             @0 < o < MPW_BITS@.
+ */
+
+void mpx_usubnlsl(mpw *dv, mpw *dvl, mpw a, unsigned o)
+{
+  mpw b = a >> (MPW_BITS - o);
+  a <<= o;
+
+  if (dv < dvl) {
+    mpd x = (mpd)*dv - (mpd)a;
+    *dv++ = MPW(x);
+    if (x >> MPW_BITS)
+      b++;
+    MPX_USUBN(dv, dvl, b);
+  }
+}
+
 /* --- @mpx_umul@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination vector base and limit
@@ -752,7 +1121,7 @@ void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl,
 
     d = dvl[-1];
     for (b = MPW_BITS / 2; b; b >>= 1) {
-      if (d < (MPW_MAX >> b)) {
+      if (d <= (MPW_MAX >> b)) {
        d <<= b;
        norm += b;
       }
@@ -1057,6 +1426,84 @@ static int loadstore(dstr *v)
   return (ok);
 }
 
+static int twocl(dstr *v)
+{
+  dstr d = DSTR_INIT;
+  mpw *m, *ml;
+  size_t sz;
+  int ok = 1;
+
+  sz = v[0].len; if (v[1].len > sz) sz = v[1].len;
+  dstr_ensure(&d, sz);
+
+  sz = MPW_RQ(sz);
+  m = xmalloc(MPWS(sz));
+  ml = m + sz;
+
+  mpx_loadl(m, ml, v[0].buf, v[0].len);
+  mpx_storel2cn(m, ml, d.buf, v[1].len);
+  if (memcmp(d.buf, v[1].buf, v[1].len)) {
+    dumpbits("\n*** storel2cn failed", d.buf, v[1].len);
+    ok = 0;
+  }
+
+  mpx_loadl2cn(m, ml, v[1].buf, v[1].len);
+  mpx_storel(m, ml, d.buf, v[0].len);
+  if (memcmp(d.buf, v[0].buf, v[0].len)) {
+    dumpbits("\n*** loadl2cn failed", d.buf, v[0].len);
+    ok = 0;
+  }
+
+  if (!ok) {
+    dumpbits("pos", v[0].buf, v[0].len);
+    dumpbits("neg", v[1].buf, v[1].len);
+  }
+
+  free(m);
+  dstr_destroy(&d);
+
+  return (ok);
+}
+
+static int twocb(dstr *v)
+{
+  dstr d = DSTR_INIT;
+  mpw *m, *ml;
+  size_t sz;
+  int ok = 1;
+
+  sz = v[0].len; if (v[1].len > sz) sz = v[1].len;
+  dstr_ensure(&d, sz);
+
+  sz = MPW_RQ(sz);
+  m = xmalloc(MPWS(sz));
+  ml = m + sz;
+
+  mpx_loadb(m, ml, v[0].buf, v[0].len);
+  mpx_storeb2cn(m, ml, d.buf, v[1].len);
+  if (memcmp(d.buf, v[1].buf, v[1].len)) {
+    dumpbits("\n*** storeb2cn failed", d.buf, v[1].len);
+    ok = 0;
+  }
+
+  mpx_loadb2cn(m, ml, v[1].buf, v[1].len);
+  mpx_storeb(m, ml, d.buf, v[0].len);
+  if (memcmp(d.buf, v[0].buf, v[0].len)) {
+    dumpbits("\n*** loadb2cn failed", d.buf, v[0].len);
+    ok = 0;
+  }
+
+  if (!ok) {
+    dumpbits("pos", v[0].buf, v[0].len);
+    dumpbits("neg", v[1].buf, v[1].len);
+  }
+
+  free(m);
+  dstr_destroy(&d);
+
+  return (ok);
+}
+
 static int lsl(dstr *v)
 {
   mpw *a, *al;
@@ -1070,7 +1517,7 @@ static int lsl(dstr *v)
   ALLOC(d, dl, al - a + (n + MPW_BITS - 1) / MPW_BITS);
 
   mpx_lsl(d, dl, a, al, n);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** lsl(%i) failed\n", n);
     dumpmp("       a", a, al);
     dumpmp("expected", c, cl);
@@ -1082,6 +1529,31 @@ static int lsl(dstr *v)
   return (ok);
 }
 
+static int lslc(dstr *v)
+{
+  mpw *a, *al;
+  int n = *(int *)v[1].buf;
+  mpw *c, *cl;
+  mpw *d, *dl;
+  int ok = 1;
+
+  LOAD(a, al, &v[0]);
+  LOAD(c, cl, &v[2]);
+  ALLOC(d, dl, al - a + (n + MPW_BITS - 1) / MPW_BITS);
+
+  mpx_lslc(d, dl, a, al, n);
+  if (!mpx_ueq(d, dl, c, cl)) {
+    fprintf(stderr, "\n*** lslc(%i) failed\n", n);
+    dumpmp("       a", a, al);
+    dumpmp("expected", c, cl);
+    dumpmp("  result", d, dl);
+    ok = 0;
+  }
+
+  free(a); free(c); free(d);
+  return (ok);
+}
+
 static int lsr(dstr *v)
 {
   mpw *a, *al;
@@ -1095,7 +1567,7 @@ static int lsr(dstr *v)
   ALLOC(d, dl, al - a + (n + MPW_BITS - 1) / MPW_BITS + 1);
 
   mpx_lsr(d, dl, a, al, n);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** lsr(%i) failed\n", n);
     dumpmp("       a", a, al);
     dumpmp("expected", c, cl);
@@ -1121,7 +1593,7 @@ static int uadd(dstr *v)
   ALLOC(d, dl, MAX(al - a, bl - b) + 1);
 
   mpx_uadd(d, dl, a, al, b, bl);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** uadd failed\n");
     dumpmp("       a", a, al);
     dumpmp("       b", b, bl);
@@ -1148,7 +1620,7 @@ static int usub(dstr *v)
   ALLOC(d, dl, al - a);
 
   mpx_usub(d, dl, a, al, b, bl);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** usub failed\n");
     dumpmp("       a", a, al);
     dumpmp("       b", b, bl);
@@ -1175,7 +1647,7 @@ static int umul(dstr *v)
   ALLOC(d, dl, (al - a) + (bl - b));
 
   mpx_umul(d, dl, a, al, b, bl);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** umul failed\n");
     dumpmp("       a", a, al);
     dumpmp("       b", b, bl);
@@ -1200,7 +1672,7 @@ static int usqr(dstr *v)
   ALLOC(d, dl, 2 * (al - a));
 
   mpx_usqr(d, dl, a, al);
-  if (MPX_UCMP(d, dl, !=, c, cl)) {
+  if (!mpx_ueq(d, dl, c, cl)) {
     fprintf(stderr, "\n*** usqr failed\n");
     dumpmp("       a", a, al);
     dumpmp("expected", c, cl);
@@ -1230,8 +1702,8 @@ static int udiv(dstr *v)
   ALLOC(s, sl, (bl - b) + 1);
 
   mpx_udiv(qq, qql, a, al, b, bl, s, sl);
-  if (MPX_UCMP(qq, qql, !=, q, ql) ||
-      MPX_UCMP(a, al, !=, r, rl)) {
+  if (!mpx_ueq(qq, qql, q, ql) ||
+      !mpx_ueq(a, al, r, rl)) {
     fprintf(stderr, "\n*** udiv failed\n");
     dumpmp(" divisor", b, bl);
     dumpmp("expect r", r, rl);
@@ -1247,7 +1719,10 @@ static int udiv(dstr *v)
 
 static test_chunk defs[] = {
   { "load-store", loadstore, { &type_hex, 0 } },
+  { "2cl", twocl, { &type_hex, &type_hex, } },
+  { "2cb", twocb, { &type_hex, &type_hex, } },
   { "lsl", lsl, { &type_hex, &type_int, &type_hex, 0 } },
+  { "lslc", lslc, { &type_hex, &type_int, &type_hex, 0 } },
   { "lsr", lsr, { &type_hex, &type_int, &type_hex, 0 } },
   { "uadd", uadd, { &type_hex, &type_hex, &type_hex, 0 } },
   { "usub", usub, { &type_hex, &type_hex, &type_hex, 0 } },
@@ -1263,7 +1738,6 @@ int main(int argc, char *argv[])
   return (0);
 }
 
-
 #endif
 
 /*----- That's all, folks -------------------------------------------------*/