X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/d03ab969116fe715d569304c1c474749b2f64529..2bdb833f485d3c985a0f930865452fcaafdf95dd:/mpx.c diff --git a/mpx.c b/mpx.c index 7ac5a8b..36b424c 100644 --- a/mpx.c +++ b/mpx.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: mpx.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * $Id: mpx.c,v 1.7 1999/12/22 15:49:07 mdw Exp $ * * Low-level multiprecision arithmetic * @@ -30,6 +30,26 @@ /*----- Revision history --------------------------------------------------* * * $Log: mpx.c,v $ + * 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. * @@ -37,6 +57,7 @@ /*----- Header files ------------------------------------------------------*/ +#include #include #include #include @@ -51,7 +72,7 @@ /* --- @mpx_storel@ --- * * * Arguments: @const mpw *v, *vl@ = base and limit of source vector - * @octet *p@ = pointer to octet array + * @void *pp@ = pointer to octet array * @size_t sz@ = size of octet array * * Returns: --- @@ -61,10 +82,10 @@ * isn't enough space for them. */ -void mpx_storel(const mpw *v, const mpw *vl, octet *p, size_t sz) +void mpx_storel(const mpw *v, const mpw *vl, void *pp, size_t sz) { mpw n, w = 0; - octet *q = p + sz; + octet *p = pp, *q = p + sz; unsigned bits = 0; while (p < q) { @@ -89,7 +110,7 @@ void mpx_storel(const mpw *v, const mpw *vl, octet *p, size_t sz) /* --- @mpx_loadl@ --- * * * Arguments: @mpw *v, *vl@ = base and limit of destination vector - * @const octet *p@ = pointer to octet array + * @const void *pp@ = pointer to octet array * @size_t sz@ = size of octet array * * Returns: --- @@ -99,10 +120,11 @@ void mpx_storel(const mpw *v, const mpw *vl, octet *p, size_t sz) * space for them. */ -void mpx_loadl(mpw *v, const mpw *vl, const octet *p, size_t sz) +void mpx_loadl(mpw *v, mpw *vl, const void *pp, size_t sz) { unsigned n; - const octet *q = p + sz; + mpw w = 0; + const octet *p = pp, *q = p + sz; unsigned bits = 0; if (v >= vl) @@ -126,7 +148,7 @@ void mpx_loadl(mpw *v, const mpw *vl, const octet *p, size_t sz) /* --- @mpx_storeb@ --- * * * Arguments: @const mpw *v, *vl@ = base and limit of source vector - * @octet *p@ = pointer to octet array + * @void *pp@ = pointer to octet array * @size_t sz@ = size of octet array * * Returns: --- @@ -136,10 +158,10 @@ void mpx_loadl(mpw *v, const mpw *vl, const octet *p, size_t sz) * isn't enough space for them. */ -void mpx_storeb(const mpw *v, const mpw *vl, octet *p, size_t sz); +void mpx_storeb(const mpw *v, const mpw *vl, void *pp, size_t sz) { mpw n, w = 0; - octet *q = p + sz; + octet *p = pp, *q = p + sz; unsigned bits = 0; while (q > p) { @@ -164,7 +186,7 @@ void mpx_storeb(const mpw *v, const mpw *vl, octet *p, size_t sz); /* --- @mpx_loadb@ --- * * * Arguments: @mpw *v, *vl@ = base and limit of destination vector - * @const octet *p@ = pointer to octet array + * @const void *pp@ = pointer to octet array * @size_t sz@ = size of octet array * * Returns: --- @@ -174,10 +196,11 @@ void mpx_storeb(const mpw *v, const mpw *vl, octet *p, size_t sz); * space for them. */ -void mpx_loadb(mpw *v, const mpw *vl, const octet *p, size_t sz) +void mpx_loadb(mpw *v, mpw *vl, const void *pp, size_t sz) { unsigned n; - const octet *q = p + sz; + mpw w = 0; + const octet *p = pp, *q = p + sz; unsigned bits = 0; if (v >= vl) @@ -237,6 +260,7 @@ void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n) goto done; *dv++ = MPW(w); MPX_ZERO(dv, dvl); + goto done; } /* --- Break out word and bit shifts for more sophisticated work --- */ @@ -251,33 +275,42 @@ void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n) memset(dv, 0, MPWS(nw)); } - /* --- And finally the difficult case --- */ + /* --- 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 (dv + nw >= dvl) { + if (dvn <= nw) { MPX_ZERO(dv, dvl); goto done; } - memset(dv, 0, MPWS(nw)); - dv += nw; - w = *av++; - while (av < avl) { - mpw t; - if (dv >= dvl) - goto done; - t = *av++; - *dv++ = MPW((w >> nr) | (t << nb)); - w = t; + 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; } - if (dv < dvl) { - *dv++ = MPW(w >> nr); - MPX_ZERO(dv, dvl); + while (avl > av) { + mpw t = *--avl; + *--dvl = (t >> nr) | w; + w = t << nb; } + + *--dvl = w; + MPX_ZERO(dv, dvl); } done:; @@ -320,6 +353,7 @@ void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n) goto done; *dv++ = MPW(w); MPX_ZERO(dv, dvl); + goto done; } /* --- Break out word and bit shifts for more sophisticated work --- */ @@ -359,6 +393,30 @@ done:; /*----- Unsigned arithmetic -----------------------------------------------*/ +/* --- @mpx_2c@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector + * @const mpw *v, *vl@ = source vector + * + * Returns: --- + * + * Use: Calculates the two's complement of @v@. + */ + +void mpx_2c(mpw *dv, mpw *dvl, const mpw *v, const mpw *vl) +{ + mpw c = 0; + while (dv < dvl && v < vl) + *dv++ = c = MPW(~*v++); + if (dv < dvl) { + if (c > MPW_MAX / 2) + c = MPW(~0); + while (dv < dvl) + *dv++ = c; + } + MPX_UADDN(dv, dvl, 1); +} + /* --- @mpx_ucmp@ --- * * * Arguments: @const mpw *av, *avl@ = first argument vector base and limit @@ -428,6 +486,18 @@ void mpx_uadd(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, } } +/* --- @mpx_uaddn@ --- * + * + * Arguments: @mpw *dv, *dvl@ = source and destination base and limit + * @mpw n@ = other addend + * + * Returns: --- + * + * Use: Adds a small integer to a multiprecision number. + */ + +void mpx_uaddn(mpw *dv, mpw *dvl, mpw n) { MPX_UADDN(dv, dvl, n); } + /* --- @mpx_usub@ --- * * * Arguments: @mpw *dv, *dvl@ = destination vector base and limit @@ -457,16 +527,31 @@ void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, return; a = (av < avl) ? *av++ : 0; b = (bv < bvl) ? *bv++ : 0; - x = (mpd)a - (mpd)b + c; + x = (mpd)a - (mpd)b - c; *dv++ = MPW(x); - if (c >> MPW_BITS) - c = MPW(~0u); + if (x >> MPW_BITS) + c = 1; + else + c = 0; } - c = c ? ~0u : 0; + if (c) + c = MPW_MAX; while (dv < dvl) - *dv++ = c + *dv++ = c; } +/* --- @mpx_usubn@ --- * + * + * Arguments: @mpw *dv, *dvl@ = source and destination base and limit + * @n@ = subtrahend + * + * Returns: --- + * + * Use: Subtracts a small integer from a multiprecision number. + */ + +void mpx_usubn(mpw *dv, mpw *dvl, mpw n) { MPX_USUBN(dv, dvl, n); } + /* --- @mpx_umul@ --- * * * Arguments: @mpw *dv, *dvl@ = destination vector base and limit @@ -492,7 +577,7 @@ void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, /* --- Deal with a multiply by zero --- */ if (bv == bvl) { - MPX_COPY(dv, dvl, bv, bvl); + MPX_ZERO(dv, dvl); return; } @@ -502,9 +587,9 @@ void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, /* --- Do the remaining multiply/accumulates --- */ - while (bv < bvl) { + while (dv < dvl && bv < bvl) { mpw m = *bv++; - mpw c = ; + mpw c = 0; const mpw *avv = av; mpw *dvv = ++dv; @@ -512,21 +597,114 @@ void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, mpd x; if (dvv >= dvl) goto next; - x = *dvv + m * *av++ + c; - *dv++ = MPW(x); + x = (mpd)*dvv + (mpd)m * (mpd)*avv++ + c; + *dvv++ = MPW(x); c = x >> MPW_BITS; } - if (dvv < dvl) - *dvv++ = MPW(c); + MPX_UADDN(dvv, dvl, c); next:; } } +/* --- @mpx_umuln@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = multiplicand vector base and limit + * @mpw m@ = multiplier + * + * Returns: --- + * + * Use: Multiplies a multiprecision integer by a single-word value. + * The destination and source may be equal. The destination + * is completely cleared after use. + */ + +void mpx_umuln(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, mpw m) +{ + MPX_UMULN(dv, dvl, av, avl, m); +} + +/* --- @mpx_umlan@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination/accumulator base and limit + * @const mpw *av, *avl@ = multiplicand vector base and limit + * @mpw m@ = multiplier + * + * Returns: --- + * + * Use: Multiplies a multiprecision integer by a single-word value + * and adds the result to an accumulator. + */ + +void mpx_umlan(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, mpw m) +{ + MPX_UMLAN(dv, dvl, av, avl, m); +} + +/* --- @mpx_usqr@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *av@ = source vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer squaring. The result vector must + * not overlap the source vector in any way. + */ + +void mpx_usqr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl) +{ + MPX_ZERO(dv, dvl); + + /* --- Main loop --- */ + + while (av < avl) { + const mpw *avv = av; + mpw *dvv = dv; + mpw a = *av; + mpd c; + + /* --- Stop if I've run out of destination --- */ + + if (dvv >= dvl) + break; + + /* --- Work out the square at this point in the proceedings --- */ + + { + mpd x = (mpd)a * (mpd)a + *dvv; + *dvv++ = MPW(x); + c = MPW(x >> MPW_BITS); + } + + /* --- Now fix up the rest of the vector upwards --- */ + + avv++; + while (dvv < dvl && avv < avl) { + mpd x = (mpd)a * (mpd)*avv++; + mpd y = ((x << 1) & MPW_MAX) + c + *dvv; + c = (x >> (MPW_BITS - 1)) + (y >> MPW_BITS); + *dvv++ = MPW(y); + } + while (dvv < dvl && c) { + mpd x = c + *dvv; + *dvv++ = MPW(x); + c = x >> MPW_BITS; + } + + /* --- Get ready for the next round --- */ + + av++; + dv += 2; + } +} + /* --- @mpx_udiv@ --- * * * Arguments: @mpw *qv, *qvl@ = quotient vector base and limit * @mpw *rv, *rvl@ = dividend/remainder vector base and limit * @const mpw *dv, *dvl@ = divisor vector base and limit + * @mpw *sv, *svl@ = scratch workspace * * Returns: --- * @@ -536,13 +714,14 @@ void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, * may not overlap in any way. Yes, I know it's a bit odd * requiring the dividend to be in the result position but it * does make some sense really. The remainder must have - * headroom for at least two extra words. + * headroom for at least two extra words. The scratch space + * must be at least one word larger than the divisor. */ void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl, - const mpw *dv, const mpw *dvl) + const mpw *dv, const mpw *dvl, + mpw *sv, mpw *svl) { - mpw spare[2]; unsigned norm = 0; size_t scale; mpw d, dd; @@ -551,41 +730,50 @@ void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl, MPX_ZERO(qv, qvl); + /* --- Perform some sanity checks --- */ + + MPX_SHRINK(dv, dvl); + assert(((void)"division by zero in mpx_udiv", dv < dvl)); + /* --- Normalize the divisor --- * * * The algorithm requires that the divisor be at least two digits long. * This is easy to fix. */ - MPX_SHRINK(dv, dvl); - - assert(((void)"division by zero in mpx_udiv", dv < dvl)); - - d = dvl[-1]; - if (dv + 1 == dvl) { - spare[0] = 0; - spare[1] = d; - dv = spare; - dvl = spare + 2; - norm += MPW_BITS; - } + { + unsigned b; - while (d < MPW_MAX / 2) { - d <<= 1; - norm += 1; + d = dvl[-1]; + for (b = MPW_BITS / 2; b; b >>= 1) { + if (d < (MPW_MAX >> b)) { + d <<= b; + norm += b; + } + } + if (dv + 1 == dvl) + norm += MPW_BITS; } - dd = dvl[-2]; /* --- Normalize the dividend/remainder to match --- */ - mpx_lsl(rv, rvl, rv, rvl, norm); + if (norm) { + mpx_lsl(rv, rvl, rv, rvl, norm); + mpx_lsl(sv, svl, dv, dvl, norm); + dv = sv; + dvl = svl; + MPX_SHRINK(dv, dvl); + } + MPX_SHRINK(rv, rvl); + d = dvl[-1]; + dd = dvl[-2]; /* --- Work out the relative scales --- */ { size_t rvn = rvl - rv; - size_t dvn = dvn - dv; + size_t dvn = dvl - dv; /* --- If the divisor is clearly larger, notice this --- */ @@ -613,32 +801,465 @@ void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl, /* --- Now for the main loop --- */ { - mpw *rvv; - mpw r; - - scale--; - rvv = rvl - 2; - r = rvv[1]; + mpw *rvv = rvl - 2; while (scale) { - mpw q, rr; + mpw q; + mpd rh; /* --- Get an estimate for the next quotient digit --- */ - rr = *rvv--; + mpw r = rvv[1]; + mpw rr = rvv[0]; + mpw rrr = *--rvv; + + scale--; + rh = ((mpd)r << MPW_BITS) | rr; if (r == d) q = MPW_MAX; - else { - mpd rx = (r << MPW_BITS) | rr; - q = MPW(rx / d); - } + else + q = MPW(rh / d); /* --- Refine the estimate --- */ { mpd yh = (mpd)d * q; mpd yl = (mpd)dd * q; - + + if (yl > MPW_MAX) { + yh += yl >> MPW_BITS; + yl &= MPW_MAX; + } + + while (yh > rh || (yh == rh && yl > rrr)) { + q--; + yh -= d; + if (yl < dd) { + yh++; + yl += MPW_MAX; + } + yl -= dd; + } + } + + /* --- Remove a chunk from the dividend --- */ + + { + mpw *svv; + const mpw *dvv; + mpw mc = 0, sc = 0; + + /* --- Calculate the size of the chunk --- * + * + * This does the whole job of calculating @r >> scale - qd@. + */ + + for (svv = rv + scale, dvv = dv; + dvv < dvl && svv < rvl; + svv++, dvv++) { + mpd x = (mpd)*dvv * (mpd)q + mc; + mc = x >> MPW_BITS; + x = (mpd)*svv - MPW(x) - sc; + *svv = MPW(x); + if (x >> MPW_BITS) + sc = 1; + else + sc = 0; + } + + if (svv < rvl) { + mpd x = (mpd)*svv - mc - sc; + *svv++ = MPW(x); + if (x >> MPW_BITS) + sc = MPW_MAX; + else + sc = 0; + while (svv < rvl) + *svv++ = sc; + } + + /* --- Fix if the quotient was too large --- * + * + * This doesn't seem to happen very often. + */ + + if (rvl[-1] > MPW_MAX / 2) { + mpx_uadd(rv + scale, rvl, rv + scale, rvl, dv, dvl); + q--; + } + } + + /* --- Done for another iteration --- */ + + if (qvl - qv > scale) + qv[scale] = q; + r = rr; + rr = rrr; + } + } + + /* --- Now fiddle with unnormalizing and things --- */ + + mpx_lsr(rv, rvl, rv, rvl, norm); } +/* --- @mpx_udivn@ --- * + * + * Arguments: @mpw *qv, *qvl@ = storage for the quotient (may overlap + * dividend) + * @const mpw *rv, *rvl@ = dividend + * @mpw d@ = single-precision divisor + * + * Returns: Remainder after divison. + * + * Use: Performs a single-precision division operation. + */ + +mpw mpx_udivn(mpw *qv, mpw *qvl, const mpw *rv, const mpw *rvl, mpw d) +{ + size_t i; + size_t ql = qvl - qv; + mpd r = 0; + + i = rvl - rv; + while (i > 0) { + i--; + r = (r << MPW_BITS) | rv[i]; + if (i < ql) + qv[i] = r / d; + r %= d; + } + return (MPW(r)); +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include +#include +#include +#include + +#include "mpscan.h" + +#define ALLOC(v, vl, sz) do { \ + size_t _sz = (sz); \ + mpw *_vv = xmalloc(MPWS(_sz)); \ + mpw *_vvl = _vv + _sz; \ + (v) = _vv; \ + (vl) = _vvl; \ +} while (0) + +#define LOAD(v, vl, d) do { \ + const dstr *_d = (d); \ + mpw *_v, *_vl; \ + ALLOC(_v, _vl, MPW_RQ(_d->len)); \ + mpx_loadb(_v, _vl, _d->buf, _d->len); \ + (v) = _v; \ + (vl) = _vl; \ +} while (0) + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +static void dumpbits(const char *msg, const void *pp, size_t sz) +{ + const octet *p = pp; + fputs(msg, stderr); + for (; sz; sz--) + fprintf(stderr, " %02x", *p++); + fputc('\n', stderr); +} + +static void dumpmp(const char *msg, const mpw *v, const mpw *vl) +{ + fputs(msg, stderr); + MPX_SHRINK(v, vl); + while (v < vl) + fprintf(stderr, " %08lx", (unsigned long)*--vl); + fputc('\n', stderr); +} + +static int chkscan(const mpw *v, const mpw *vl, + const void *pp, size_t sz, int step) +{ + mpscan mps; + const octet *p = pp; + unsigned bit = 0; + int ok = 1; + + mpscan_initx(&mps, v, vl); + while (sz) { + unsigned x = *p; + int i; + p += step; + for (i = 0; i < 8 && MPSCAN_STEP(&mps); i++) { + if (MPSCAN_BIT(&mps) != (x & 1)) { + fprintf(stderr, + "\n*** error, step %i, bit %u, expected %u, found %u\n", + step, bit, x & 1, MPSCAN_BIT(&mps)); + ok = 0; + } + x >>= 1; + bit++; + } + sz--; + } + + return (ok); +} + +static int loadstore(dstr *v) +{ + dstr d = DSTR_INIT; + size_t sz = MPW_RQ(v->len) * 2, diff; + mpw *m, *ml; + int ok = 1; + + dstr_ensure(&d, v->len); + m = xmalloc(MPWS(sz)); + + for (diff = 0; diff < sz; diff += 5) { + size_t oct; + + ml = m + sz - diff; + + mpx_loadl(m, ml, v->buf, v->len); + if (!chkscan(m, ml, v->buf, v->len, +1)) + ok = 0; + MPX_OCTETS(oct, m, ml); + mpx_storel(m, ml, d.buf, d.sz); + if (memcmp(d.buf, v->buf, oct) != 0) { + dumpbits("\n*** storel failed", d.buf, d.sz); + ok = 0; + } + + mpx_loadb(m, ml, v->buf, v->len); + if (!chkscan(m, ml, v->buf + v->len - 1, v->len, -1)) + ok = 0; + MPX_OCTETS(oct, m, ml); + mpx_storeb(m, ml, d.buf, d.sz); + if (memcmp(d.buf + d.sz - oct, v->buf + v->len - oct, oct) != 0) { + dumpbits("\n*** storeb failed", d.buf, d.sz); + ok = 0; + } + } + + if (!ok) + dumpbits("input data", v->buf, v->len); + + free(m); + dstr_destroy(&d); + return (ok); +} + +static int lsl(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_lsl(d, dl, a, al, n); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** lsl(%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; + 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 + 1); + + mpx_lsr(d, dl, a, al, n); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** lsr(%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 uadd(dstr *v) +{ + mpw *a, *al; + mpw *b, *bl; + mpw *c, *cl; + mpw *d, *dl; + int ok = 1; + + LOAD(a, al, &v[0]); + LOAD(b, bl, &v[1]); + LOAD(c, cl, &v[2]); + ALLOC(d, dl, MAX(al - a, bl - b) + 1); + + mpx_uadd(d, dl, a, al, b, bl); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** uadd failed\n"); + dumpmp(" a", a, al); + dumpmp(" b", b, bl); + dumpmp("expected", c, cl); + dumpmp(" result", d, dl); + ok = 0; + } + + free(a); free(b); free(c); free(d); + return (ok); +} + +static int usub(dstr *v) +{ + mpw *a, *al; + mpw *b, *bl; + mpw *c, *cl; + mpw *d, *dl; + int ok = 1; + + LOAD(a, al, &v[0]); + LOAD(b, bl, &v[1]); + LOAD(c, cl, &v[2]); + ALLOC(d, dl, al - a); + + mpx_usub(d, dl, a, al, b, bl); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** usub failed\n"); + dumpmp(" a", a, al); + dumpmp(" b", b, bl); + dumpmp("expected", c, cl); + dumpmp(" result", d, dl); + ok = 0; + } + + free(a); free(b); free(c); free(d); + return (ok); +} + +static int umul(dstr *v) +{ + mpw *a, *al; + mpw *b, *bl; + mpw *c, *cl; + mpw *d, *dl; + int ok = 1; + + LOAD(a, al, &v[0]); + LOAD(b, bl, &v[1]); + LOAD(c, cl, &v[2]); + ALLOC(d, dl, (al - a) + (bl - b)); + + mpx_umul(d, dl, a, al, b, bl); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** umul failed\n"); + dumpmp(" a", a, al); + dumpmp(" b", b, bl); + dumpmp("expected", c, cl); + dumpmp(" result", d, dl); + ok = 0; + } + + free(a); free(b); free(c); free(d); + return (ok); +} + +static int usqr(dstr *v) +{ + mpw *a, *al; + mpw *c, *cl; + mpw *d, *dl; + int ok = 1; + + LOAD(a, al, &v[0]); + LOAD(c, cl, &v[1]); + ALLOC(d, dl, 2 * (al - a)); + + mpx_usqr(d, dl, a, al); + if (MPX_UCMP(d, dl, !=, c, cl)) { + fprintf(stderr, "\n*** usqr failed\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 udiv(dstr *v) +{ + mpw *a, *al; + mpw *b, *bl; + mpw *q, *ql; + mpw *r, *rl; + mpw *qq, *qql; + mpw *s, *sl; + int ok = 1; + + ALLOC(a, al, MPW_RQ(v[0].len) + 2); mpx_loadb(a, al, v[0].buf, v[0].len); + LOAD(b, bl, &v[1]); + LOAD(q, ql, &v[2]); + LOAD(r, rl, &v[3]); + ALLOC(qq, qql, al - a); + 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)) { + fprintf(stderr, "\n*** udiv failed\n"); + dumpmp(" divisor", b, bl); + dumpmp("expect r", r, rl); + dumpmp("result r", a, al); + dumpmp("expect q", q, ql); + dumpmp("result q", qq, qql); + ok = 0; + } + + free(a); free(b); free(r); free(q); free(s); free(qq); + return (ok); +} + +static test_chunk defs[] = { + { "load-store", loadstore, { &type_hex, 0 } }, + { "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 } }, + { "usub", usub, { &type_hex, &type_hex, &type_hex, 0 } }, + { "umul", umul, { &type_hex, &type_hex, &type_hex, 0 } }, + { "usqr", usqr, { &type_hex, &type_hex, 0 } }, + { "udiv", udiv, { &type_hex, &type_hex, &type_hex, &type_hex, 0 } }, + { 0, 0, { 0 } } +}; + +int main(int argc, char *argv[]) +{ + test_run(argc, argv, defs, SRCDIR"/tests/mpx"); + return (0); +} + + +#endif + /*----- That's all, folks -------------------------------------------------*/