/* -*-c-*-
*
- * $Id: mpx.c,v 1.8 2000/06/25 12:59:02 mdw Exp $
+ * $Id: mpx.c,v 1.15 2002/10/20 01:12:31 mdw Exp $
*
* Low-level multiprecision arithmetic
*
/*----- Revision history --------------------------------------------------*
*
* $Log: mpx.c,v $
+ * Revision 1.15 2002/10/20 01:12:31 mdw
+ * Two's complement I/O fixes.
+ *
+ * Revision 1.14 2002/10/19 18:55:08 mdw
+ * Fix overflows in shift primitives.
+ *
+ * Revision 1.13 2002/10/19 17:56:50 mdw
+ * Fix bit operations. Test them (a bit) better.
+ *
+ * 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.
+ *
+ * Revision 1.10 2000/10/08 12:06:12 mdw
+ * Provide @mpx_ueq@ for rapidly testing equality of two integers.
+ *
+ * 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.
*
#include "mptypes.h"
#include "mpx.h"
+#include "bitops.h"
/*----- Loading and storing -----------------------------------------------*/
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@ --- *
/* --- 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 --- *
/* --- 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 --- */
size_t nr = MPW_BITS - nb;
av += nw;
- w = *av++;
+ w = av < avl ? *av++ : 0;
while (av < avl) {
mpw t;
if (dv >= dvl)
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@ --- *
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
}
return (0);
}
-
+
/* --- @mpx_uadd@ --- *
*
* Arguments: @mpw *dv, *dvl@ = destination vector base and limit
yh -= d;
if (yl < dd)
yh--;
- yl -= dd;
+ yl = MPW(yl - dd);
}
}
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;
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);
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);
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);
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);
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);
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);
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);
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 } },
return (0);
}
-
#endif
/*----- That's all, folks -------------------------------------------------*/