Pile of changes for supporting two's complement properly.
[u/mdw/catacomb] / mpx.c
diff --git a/mpx.c b/mpx.c
index f89a89b..4097b3e 100644 (file)
--- a/mpx.c
+++ b/mpx.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: mpx.c,v 1.11 2001/04/03 19:36:05 mdw Exp $
+ * $Id: mpx.c,v 1.12 2002/10/06 22:52:50 mdw Exp $
  *
  * Low-level multiprecision arithmetic
  *
@@ -30,6 +30,9 @@
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: mpx.c,v $
+ * Revision 1.12  2002/10/06 22:52:50  mdw
+ * Pile of changes for supporting two's complement properly.
+ *
  * Revision 1.11  2001/04/03 19:36:05  mdw
  * Add some simple bitwise operations so that Perl can use them.
  *
@@ -233,6 +236,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 = !b;
+    *p++ = b;
+  }
+  while (p < q) {
+    b = U8(~b + 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 = !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 = !b;
+    *--q = b;
+  }
+  while (q > p) {
+    b = ~b + 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 = !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@ --- *
@@ -405,7 +588,7 @@ done:;
 
 /*----- Bitwise operations ------------------------------------------------*/
 
-/* --- @mpx_and@, @mpx_or@, @mpx_xor@, @mpx_not@ --- *
+/* --- @mpx_bitop@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination vector
  *             @const mpw *av, *avl@ = first source vector
@@ -413,13 +596,13 @@ done:;
  *
  * Returns:    ---
  *
- * Use;                Does the obvious bitwise operations.
+ * Use;                Provides the dyadic boolean functions.
  */
 
-#define MPX_BITBINOP(name, op)                                         \
+#define MPX_BITBINOP(string)                                           \
                                                                        \
-void mpx_##name(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,      \
-               const mpw *bv, const mpw *bvl)                          \
+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);                                                 \
@@ -428,13 +611,11 @@ void mpx_##name(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, \
     mpw a, b;                                                          \
     a = (av < avl) ? *av++ : 0;                                                \
     b = (bv < bvl) ? *bv++ : 0;                                                \
-    *dv++ = a op b;                                                    \
+    *dv++ = MPX_B##string(a, b);                                       \
   }                                                                    \
 }
 
-MPX_BITBINOP(and, &)
-MPX_BITBINOP(or, |)
-MPX_BITBINOP(xor, ^)
+MPX_DOBIN(MPX_BITBINOP)
 
 void mpx_not(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl)
 {
@@ -1130,6 +1311,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;
@@ -1320,6 +1579,8 @@ 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 } },
   { "lsr", lsr, { &type_hex, &type_int, &type_hex, 0 } },
   { "uadd", uadd, { &type_hex, &type_hex, &type_hex, 0 } },