shithub: femtolisp

Download patch

ref: 467826415fe2cc2846097dfd37ec879b9d62cf62
parent: 076b3b75c0f116421c17e7c2afd52b3079521ef4
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Sat Nov 16 20:52:20 EST 2024

mp: import a test and fix the issues it found

--- a/3rd/mp/mpaux.c
+++ b/3rd/mp/mpaux.c
@@ -33,8 +33,10 @@
 void
 mpsetminbits(int n)
 {
-	if(n < 0)
-		sysfatal("mpsetminbits: n < 0");
+	if(n < 0){
+		fprintf(stderr, "mpsetminbits: n < 0\n");
+		exit(2);
+	}
 	if(n == 0)
 		n = 1;
 	mpmindigits = DIGITS(n);
@@ -46,15 +48,19 @@
 {
 	mpint *b;
 
-	if(n < 0)
-		sysfatal("mpnew: n < 0");
+	if(n < 0){
+		fprintf(stderr, "mpnew: n < 0\n");
+		exit(2);
+	}
 
 	n = DIGITS(n);
 	if(n < mpmindigits)
 		n = mpmindigits;
 	b = calloc(1, sizeof(mpint) + n*Dbytes);
-	if(b == nil)
-		sysfatal("mpnew: %r");
+	if(b == nil){
+		fprintf(stderr, "mpnew: no memory\n");
+		exit(2);
+	}
 	b->p = (mpdigit*)&b[1];
 	b->size = n;
 	b->sign = 1;
@@ -76,14 +82,18 @@
 	} else {
 		if(b->p == (mpdigit*)&b[1]){
 			b->p = (mpdigit*)MEM_ALLOC(n*Dbytes);
-			if(b->p == nil)
-				sysfatal("mpbits: %r");
+			if(b->p == nil){
+				fprintf(stderr, "mpbits: no memory\n");
+				exit(2);
+			}
 			memmove(b->p, &b[1], Dbytes*b->top);
 			memset(&b[1], 0, Dbytes*b->size);
 		} else {
 			b->p = (mpdigit*)MEM_REALLOC(b->p, n*Dbytes);
-			if(b->p == nil)
-				sysfatal("mpbits: %r");
+			if(b->p == nil){
+				fprintf(stderr, "mpbits: no memory\n");
+				exit(2);
+			}
 		}
 		b->size = n;
 	}
@@ -97,8 +107,10 @@
 {
 	if(b == nil)
 		return;
-	if(b->flags & MPstatic)
-		sysfatal("freeing mp constant");
+	if(b->flags & MPstatic){
+		fprintf(stderr, "freeing mp constant\n");
+		exit(2);
+	}
 	memset(b->p, 0, b->size*Dbytes);
 	if(b->p != (mpdigit*)&b[1])
 		MEM_FREE(b->p);
--- a/3rd/mp/mpdiv.c
+++ b/3rd/mp/mpdiv.c
@@ -14,9 +14,11 @@
 	assert(quotient != remainder);
 	assert(divisor->flags & MPnorm);
 
-	// divide bv zero
-	if(divisor->top == 0)
-		abort();
+	// divide by zero
+	if(divisor->top == 0){
+		fprintf(stderr, "mpdiv: by zero\n");
+		exit(2);
+	}
 
 	// division by one or small powers of two
 	if(divisor->top == 1 && (divisor->p[0] & divisor->p[0]-1) == 0){
--- /dev/null
+++ b/3rd/mp/mpexp.c
@@ -1,0 +1,97 @@
+#include "platform.h"
+#include "mp.h"
+
+// res = b**e
+//
+// knuth, vol 2, pp 398-400
+
+enum {
+	Freeb=	0x1,
+	Freee=	0x2,
+	Freem=	0x4,
+};
+
+//int expdebug;
+
+void
+mpexp(mpint *b, mpint *e, mpint *m, mpint *res)
+{
+	mpint *t[2];
+	int tofree;
+	mpdigit d, bit;
+	int i, j;
+
+	assert(m == nil || m->flags & MPnorm);
+	assert((e->flags & MPtimesafe) == 0);
+	res->flags |= b->flags & MPtimesafe;
+
+	i = mpcmp(e,mpzero);
+	if(i==0){
+		mpassign(mpone, res);
+		return;
+	}
+	if(i < 0){
+		fprintf(stderr, "mpexp: negative exponent\n");
+		exit(2);
+	}
+
+	t[0] = mpcopy(b);
+	t[1] = res;
+
+	tofree = 0;
+	if(res == b){
+		b = mpcopy(b);
+		tofree |= Freeb;
+	}
+	if(res == e){
+		e = mpcopy(e);
+		tofree |= Freee;
+	}
+	if(res == m){
+		m = mpcopy(m);
+		tofree |= Freem;
+	}
+
+	// skip first bit
+	i = e->top-1;
+	d = e->p[i];
+	for(bit = mpdighi; (bit & d) == 0; bit >>= 1)
+		;
+	bit >>= 1;
+
+	j = 0;
+	for(;;){
+		for(; bit != 0; bit >>= 1){
+			if(m != nil)
+				mpmodmul(t[j], t[j], m, t[j^1]);
+			else
+				mpmul(t[j], t[j], t[j^1]);
+			if(bit & d) {
+				if(m != nil)
+					mpmodmul(t[j^1], b, m, t[j]);
+				else
+					mpmul(t[j^1], b, t[j]);
+			} else
+				j ^= 1;
+		}
+		if(--i < 0)
+			break;
+		bit = mpdighi;
+		d = e->p[i];
+	}
+	if(t[j] == res){
+		mpfree(t[j^1]);
+	} else {
+		mpassign(t[j], res);
+		mpfree(t[j]);
+	}
+
+	if(tofree){
+		if(tofree & Freeb)
+			mpfree(b);
+		if(tofree & Freee)
+			mpfree(e);
+		if(tofree & Freem)
+			mpfree(m);
+	}
+}
--- /dev/null
+++ b/3rd/mp/mpextendedgcd.c
@@ -1,0 +1,115 @@
+#include "platform.h"
+#include "mp.h"
+
+#define iseven(a)	(((a)->p[0] & 1) == 0)
+
+// extended binary gcd
+//
+// For a and b it solves, v = gcd(a,b) and finds x and y s.t.
+// ax + by = v
+//
+// Handbook of Applied Cryptography, Menezes et al, 1997, pg 608.  
+void
+mpextendedgcd(mpint *a, mpint *b, mpint *v, mpint *x, mpint *y)
+{
+	mpint *u, *A, *B, *C, *D;
+	int g;
+
+	if(v == nil){
+		v = mpnew(0);
+		mpextendedgcd(a, b, v, x, y);
+		mpfree(v);
+		return;
+	}
+	assert(x == nil || (x->flags & MPtimesafe) == 0);
+	assert(y == nil || (y->flags & MPtimesafe) == 0);
+	assert((a->flags&b->flags) & MPnorm);
+	assert(((a->flags|b->flags|v->flags) & MPtimesafe) == 0);
+
+	if(a->sign < 0 || b->sign < 0){
+		mpassign(mpzero, v);
+		mpassign(mpzero, y);
+		mpassign(mpzero, x);
+		return;
+	}
+
+	if(a->top == 0){
+		mpassign(b, v);
+		mpassign(mpone, y);
+		mpassign(mpzero, x);
+		return;
+	}
+	if(b->top == 0){
+		mpassign(a, v);
+		mpassign(mpone, x);
+		mpassign(mpzero, y);
+		return;
+	}
+
+	g = 0;
+	a = mpcopy(a);
+	b = mpcopy(b);
+
+	while(iseven(a) && iseven(b)){
+		mpright(a, 1, a);
+		mpright(b, 1, b);
+		g++;
+	}
+
+	u = mpcopy(a);
+	mpassign(b, v);
+	A = mpcopy(mpone);
+	B = mpcopy(mpzero);
+	C = mpcopy(mpzero);
+	D = mpcopy(mpone);
+
+	for(;;) {
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		while(iseven(u)){
+			mpright(u, 1, u);
+			if(!iseven(A) || !iseven(B)) {
+				mpadd(A, b, A);
+				mpsub(B, a, B);
+			}
+			mpright(A, 1, A);
+			mpright(B, 1, B);
+		}
+	
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		while(iseven(v)){
+			mpright(v, 1, v);
+			if(!iseven(C) || !iseven(D)) {
+				mpadd(C, b, C);
+				mpsub(D, a, D);
+			}
+			mpright(C, 1, C);
+			mpright(D, 1, D);
+		}
+	
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		if(mpcmp(u, v) >= 0){
+			mpsub(u, v, u);
+			mpsub(A, C, A);
+			mpsub(B, D, B);
+		} else {
+			mpsub(v, u, v);
+			mpsub(C, A, C);
+			mpsub(D, B, D);
+		}
+
+		if(u->top == 0)
+			break;
+
+	}
+	mpassign(C, x);
+	mpassign(D, y);
+	mpleft(v, g, v);
+
+	mpfree(A);
+	mpfree(B);
+	mpfree(C);
+	mpfree(D);
+	mpfree(u);
+	mpfree(a);
+	mpfree(b);
+}
--- a/3rd/mp/mpfmt.c
+++ b/3rd/mp/mpfmt.c
@@ -13,7 +13,7 @@
 	sn = 1<<s;
 	out = buf;
 	eout = buf+len;
-	for(p = &b->p[b->top-1]; p >= b->p; p--){
+	for(p = b->p+b->top-1; p >= b->p; p--){
 		x = *p;
 		for(i = Dbits-s; i >= 0; i -= s){
 			j = x >> i & sn - 1;
@@ -89,7 +89,8 @@
 {
 	mpdigit x, y;
 	char *out;
-	uint32_t i, j;
+	uint32_t j;
+	int i;
 
 	if(len < 2)
 		return -1;
@@ -107,7 +108,8 @@
 			x = y;
 		i += Dbits;
 		while(i >= 3){
-Digout:			i -= 3;
+Digout:
+			i -= 3;
 			if(out > buf)
 				out--;
 			else if(x != 0)
@@ -174,8 +176,8 @@
 		rv = topow2(b, out, len, 1);
 		break;
 	default:
-		abort();
-		return nil;
+		fprintf(stderr, "mptoa: invalid base: %d\n", base);
+		exit(2);
 	}
 	if(rv < 0){
 		if(alloced)
--- /dev/null
+++ b/3rd/mp/mpinvert.c
@@ -1,0 +1,19 @@
+#include "platform.h"
+#include "mp.h"
+
+// use extended gcd to find the multiplicative inverse
+// res = b**-1 mod m
+void
+mpinvert(mpint *b, mpint *m, mpint *res)
+{
+	mpint *v;
+
+	v = mpnew(0);
+	mpextendedgcd(b, m, v, res, nil);
+	if(mpcmp(v, mpone) != 0){
+		fprintf(stderr, "mpinvert: impossible\n");
+		exit(2);
+	}
+	mpfree(v);
+	mpmod(res, m, res);
+}
--- /dev/null
+++ b/3rd/mp/mpmod.c
@@ -1,0 +1,19 @@
+#include "platform.h"
+#include "mp.h"
+
+void
+mpmod(mpint *x, mpint *n, mpint *r)
+{
+	int sign;
+	mpint *ns;
+
+	sign = x->sign;
+	ns = sign < 0 && n == r ? mpcopy(n) : n;
+	if((n->flags & MPfield) == 0
+	|| ((Mfield*)n)->reduce((Mfield*)n, x, r) != 0)
+		mpdiv(x, n, nil, r);
+	if(sign < 0){
+		mpmagsub(ns, r, r);
+		if(ns != n) mpfree(ns);
+	}
+}
--- /dev/null
+++ b/3rd/mp/mpmodop.c
@@ -1,0 +1,95 @@
+#include "platform.h"
+#include "mp.h"
+
+/* operands need to have m->top+1 digits of space and satisfy 0 ≤ a ≤ m-1 */
+static mpint*
+modarg(mpint *a, mpint *m)
+{
+	if(a->size <= m->top || a->sign < 0 || mpmagcmp(a, m) >= 0){
+		a = mpcopy(a);
+		mpmod(a, m, a);
+		mpbits(a, Dbits*(m->top+1));
+		a->top = m->top;
+	} else if(a->top < m->top){
+		memset(&a->p[a->top], 0, (m->top - a->top)*Dbytes);
+	}
+	return a;
+}
+
+void
+mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum)
+{
+	mpint *a, *b;
+	mpdigit d;
+	uint32_t i, j;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	sum->flags |= (a->flags | b->flags) & MPtimesafe;
+	mpbits(sum, Dbits*2*(m->top+1));
+
+	mpvecadd(a->p, m->top, b->p, m->top, sum->p);
+	mpvecsub(sum->p, m->top+1, m->p, m->top, sum->p+m->top+1);
+
+	d = sum->p[2*m->top+1];
+	for(i = 0, j = m->top+1; i < m->top; i++, j++)
+		sum->p[i] = (sum->p[i] & d) | (sum->p[j] & ~d);
+
+	sum->top = m->top;
+	sum->sign = 1;
+	mpnorm(sum);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
+
+void
+mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff)
+{
+	mpint *a, *b;
+	mpdigit d;
+	uint32_t i, j;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	diff->flags |= (a->flags | b->flags) & MPtimesafe;
+	mpbits(diff, Dbits*2*(m->top+1));
+
+	a->p[m->top] = 0;
+	mpvecsub(a->p, m->top+1, b->p, m->top, diff->p);
+	mpvecadd(diff->p, m->top, m->p, m->top, diff->p+m->top+1);
+
+	d = ~diff->p[m->top];
+	for(i = 0, j = m->top+1; i < m->top; i++, j++)
+		diff->p[i] = (diff->p[i] & d) | (diff->p[j] & ~d);
+
+	diff->top = m->top;
+	diff->sign = 1;
+	mpnorm(diff);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
+
+void
+mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod)
+{
+	mpint *a, *b;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	mpmul(a, b, prod);
+	mpmod(prod, m, prod);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
--- a/3rd/mp/mpmul.c
+++ b/3rd/mp/mpmul.c
@@ -39,8 +39,10 @@
 
 	// room for the partial products
 	t = calloc(1, Dbytes*5*(2*n+1));
-	if(t == nil)
-		sysfatal("mpkaratsuba: %r");
+	if(t == nil){
+		fprintf(stderr, "mpkaratsuba: no memory\n");
+		exit(2);
+	}
 	u0v0 = t;
 	u1v1 = t + (2*n+1);
 	diffprod = t + 2*(2*n+1);
--- /dev/null
+++ b/3rd/mp/mprand.c
@@ -1,0 +1,23 @@
+#include "platform.h"
+#include "mp.h"
+
+mpint *
+mprand(int bits, void (*gen)(uint8_t*, int), mpint *b)
+{
+	mpdigit mask;
+
+	if(b == nil)
+		b = mpnew(bits);
+	else
+		mpbits(b, bits);
+
+	b->sign = 1;
+	b->top = DIGITS(bits);
+	(*gen)((uint8_t*)b->p, b->top*Dbytes);
+
+	mask = ((mpdigit)1 << (bits%Dbits))-1;
+	if(mask != 0)
+		b->p[b->top-1] &= mask;
+
+	return mpnorm(b);
+}
--- a/3rd/mp/mptobe.c
+++ b/3rd/mp/mptobe.c
@@ -14,8 +14,10 @@
 	if(p == nil){
 		n = m;
 		p = MEM_ALLOC(n);
-		if(p == nil)
-			sysfatal("mptobe: %r");
+		if(p == nil){
+			fprintf(stderr, "mptobe: no memory\n");
+			exit(2);
+		}
 	} else {
 		if(n < m)
 			return -1;
--- a/3rd/mp/strtomp.c
+++ b/3rd/mp/strtomp.c
@@ -108,9 +108,8 @@
 	int sign;
 	const char *e;
 
-	if(b == nil){
+	if(b == nil)
 		b = mpnew(0);
-	}
 
 	while(*a==' ' || *a=='\t')
 		a++;
@@ -131,10 +130,10 @@
 			if(a[1] == 'x' || a[1] == 'X') {
 				a += 2;
 				base = 16;
-			} else if(a[1] == 'b' || a[1] == 'B') {
+			}else if(a[1] == 'b' || a[1] == 'B') {
 				a += 2;
 				base = 2;
-			} else if(a[1] >= '0' && a[1] <= '7') {
+			}else if(a[1] >= '0' && a[1] <= '7') {
 				a++;
 				base = 8;
 			}
--- /dev/null
+++ b/3rd/mp/test.c
@@ -1,0 +1,500 @@
+#include "platform.h"
+#include "mp.h"
+#include "ieee754.h"
+
+double D_PNAN, D_NNAN, D_PINF, D_NINF;
+float F_PNAN, F_NNAN, F_PINF, F_NINF;
+
+static int loops = 1;
+static char str[16][8192];
+static int istr = -1;
+
+static char *
+MPB(int base, mpint *m)
+{
+	char *s, *b;
+	bool minus;
+	istr = (istr+1) % nelem(str);
+	b = str[istr];
+	s = mptoa(m, base, b+2, sizeof(str[istr])-2);
+	if(base == 10)
+		return s;
+	minus = s[0] == '-';
+	if(minus)
+		s++;
+	if(base == 2){
+		*--s = 'b';
+		*--s = '0';
+	}else if(base == 8){
+		*--s = '0';
+	}else if(base == 16){
+		*--s = 'x';
+		*--s = '0';
+	}
+	if(minus)
+		*--s = '-';
+	return s;
+}
+
+static char *
+MP(mpint *m)
+{
+	char *b;
+	istr = (istr+1) % nelem(str);
+	b = str[istr];
+	return mptoa(m, 10, b, sizeof(str[istr]));
+}
+
+static int64_t
+nsec(void)
+{
+	return 0;
+}
+
+static void
+prng(uint8_t *p, int n)
+{
+	while(n-- > 0)
+		*p++ = rand();
+}
+
+static void
+testconv(char *str)
+{
+	int i, base[] = {2,8,10,16/*,32,64*/};
+	mpint *b;
+	char *p;
+
+	printf("testconv \"%s\":\n", str);
+	b = strtomp(str, nil, 16, nil);
+
+	for(i=0; i < nelem(base); i++){
+		p = mptoa(b, base[i], nil, 0);
+		if(p == nil){
+			fprintf(stderr, "mptoa(base=%d) -> nil\n", base[i]);
+			exit(2);
+		}
+		printf("base%d: %s = ", base[i], p);
+		if(strtomp(p, nil, base[i], b) == nil){
+			fprintf(stderr, "A strtomp(%s) -> nil\n", p);
+			exit(2);
+		}
+		free(p);
+		printf("%s\n", MPB(base[i], b));
+
+		switch(base[i]){
+		case 2:
+		case 8:
+		case 10:
+		case 16:
+			asprintf(&p, "%s", MPB(base[i], b));
+			printf("# %s = ", p);
+			if(strtomp(p, nil, 0, b) == nil){
+				fprintf(stderr, "B strtomp(%s) -> nil\n", p);
+				exit(2);
+			}
+			free(p);
+			printf("%s\n", MPB(base[i], b));
+			break;
+		}
+
+	}
+
+	mpfree(b);
+}
+
+static void
+testshift(char *str)
+{
+	mpint *b1, *b2;
+	int i;
+
+	b1 = strtomp(str, nil, 16, nil);
+	b2 = mpnew(0);
+	for(i = 0; i < 64; i++){
+		mpleft(b1, i, b2);
+		printf("%2.2d %s\n", i, MP(b2));
+	}
+	for(i = 0; i < 64; i++){
+		mpright(b2, i, b1);
+		printf("%2.2d %s\n", i, MP(b1));
+	}
+	mpfree(b1);
+	mpfree(b2);
+}
+
+static void
+testaddsub(char *str)
+{
+	mpint *b1, *b2;
+	int i;
+
+	b1 = strtomp(str, nil, 16, nil);
+	b2 = mpnew(0);
+	for(i = 0; i < 16; i++){
+		mpadd(b1, b2, b2);
+		printf("%2.2d %s\n", i, MP(b2));
+	}
+	for(i = 0; i < 16; i++){
+		mpsub(b2, b1, b2);
+		printf("%2.2d %s\n", i, MP(b2));
+	}
+	mpfree(b1);
+	mpfree(b2);
+}
+
+static void
+testvecdigmuladd(char *str, mpdigit d)
+{
+	mpint *b, *b2;
+	int i;
+	int64_t now;
+
+	b = strtomp(str, nil, 16, nil);
+	b2 = mpnew(0);
+
+	mpbits(b2, (b->top+1)*Dbits);
+	now = nsec();
+	for(i = 0; i < loops; i++){
+		memset(b2->p, 0, b2->top*Dbytes);
+		mpvecdigmuladd(b->p, b->top, d, b2->p);
+	}
+	if(loops > 1)
+		printf("%"PRId64" ns for a %d*%d vecdigmul\n", (nsec()-now)/loops, b->top*Dbits, Dbits);
+	mpnorm(b2);
+	printf("0 + %s * %x = %s\n", MP(b), d, MP(b2));
+
+	mpfree(b);
+	mpfree(b2);
+}
+
+static void
+testvecdigmulsub(char *str, mpdigit d)
+{
+	mpint *b, *b2;
+	int i;
+	int64_t now;
+
+	b = strtomp(str, nil, 16, nil);
+	b2 = mpnew(0);
+
+	mpbits(b2, (b->top+1)*Dbits);
+	now = nsec();
+	for(i = 0; i < loops; i++){
+		memset(b2->p, 0, b2->top*Dbytes);
+		mpvecdigmulsub(b->p, b->top, d, b2->p);
+	}
+	if(loops > 1)
+		printf("%"PRId64" ns for a %d*%d vecdigmul\n", (nsec()-now)/loops, b->top*Dbits, Dbits);
+	mpnorm(b2);
+	printf("0 - %s * %x = %s\n", MP(b), d, MP(b2));
+
+	mpfree(b);
+	mpfree(b2);
+}
+
+static void
+testmul(char *str)
+{
+	mpint *b, *b1, *b2;
+	int64_t now;
+	int i;
+
+	b = strtomp(str, nil, 16, nil);
+	b1 = mpcopy(b);
+	b2 = mpnew(0);
+
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpmul(b, b1, b2);
+	if(loops > 1)
+		printf("%"PRId64" µs for a %d*%d mult\n", (nsec()-now)/(loops*1000),
+			b->top*Dbits, b1->top*Dbits);
+	printf("%s * %s = %s\n", MP(b), MP(b1), MP(b2));
+
+	mpfree(b);
+	mpfree(b1);
+	mpfree(b2);
+}
+
+static void
+testmul2(mpint *b, mpint *b1)
+{
+	mpint *b2;
+	int64_t now;
+	int i;
+
+	b2 = mpnew(0);
+
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpmul(b, b1, b2);
+	if(loops > 1)
+		printf("%"PRId64" µs for a %d*%d mult\n", (nsec()-now)/(loops*1000), b->top*Dbits, b1->top*Dbits);
+	printf("%s * ", MP(b));
+	printf("%s = ", MP(b1));
+	printf("%s\n", MP(b2));
+
+	mpfree(b2);
+}
+
+static void
+testdigdiv(char *str, mpdigit d)
+{
+	mpint *b;
+	mpdigit q;
+	int i;
+	int64_t now;
+
+	b = strtomp(str, nil, 16, nil);
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpdigdiv(b->p, d, &q);
+	if(loops > 1)
+		printf("%"PRId64" ns for a %d / %d div\n", (nsec()-now)/loops, 2*Dbits, Dbits);
+	printf("%s / %x = %x\n", MP(b), d, q);
+	mpfree(b);
+}
+
+static void
+testdiv(mpint *x, mpint *y)
+{
+	mpint *b2, *b3;
+	int64_t now;
+	int i;
+
+	b2 = mpnew(0);
+	b3 = mpnew(0);
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpdiv(x, y, b2, b3);
+	if(loops > 1)
+		printf("%"PRId64" µs for a %d/%d div\n", (nsec()-now)/(1000*loops),
+			x->top*Dbits, y->top*Dbits);
+	printf("%s / %s = %s %s\n", MP(x), MP(y), MP(b2), MP(b3));
+	mpfree(b2);
+	mpfree(b3);
+}
+
+static void
+testmod(mpint *x, mpint *y)
+{
+	mpint *r;
+	int64_t now;
+	int i;
+
+	r = mpnew(0);
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpmod(x, y, r);
+	if(loops > 1)
+		printf("%"PRId64" µs for a %d/%d mod\n", (nsec()-now)/(1000*loops),
+			x->top*Dbits, y->top*Dbits);
+	printf("%s mod %s = %s\n", MP(x), MP(y), MP(r));
+	mpfree(r);
+}
+
+static void
+testinvert(mpint *x, mpint *y)
+{
+	mpint *r, *d1, *d2;
+	int64_t now;
+	int i;
+
+	r = mpnew(0);
+	d1 = mpnew(0);
+	d2 = mpnew(0);
+	now = nsec();
+	mpextendedgcd(x, y, r, d1, d2);
+	mpdiv(x, r, x, d1);
+	mpdiv(y, r, y, d1);
+	for(i = 0; i < loops; i++)
+		mpinvert(x, y, r);
+	if(loops > 1)
+		printf("%"PRId64" µs for a %d in %d invert\n", (nsec()-now)/(1000*loops),
+			x->top*Dbits, y->top*Dbits);
+	printf("%s**-1 mod %s = %s\n", MP(x), MP(y), MP(r));
+	mpmul(r, x, d1);
+	mpmod(d1, y, d2);
+	printf("%s*%s mod %s = %s\n", MP(x), MP(r), MP(y), MP(d2));
+	mpfree(r);
+	mpfree(d1);
+	mpfree(d2);
+}
+
+static void
+testsub1(char *a, char *b)
+{
+	mpint *b1, *b2, *b3;
+
+	b1 = strtomp(a, nil, 16, nil);
+	b2 = strtomp(b, nil, 16, nil);
+	b3 = mpnew(0);
+	mpsub(b1, b2, b3);
+	printf("%s - %s = %s\n", MP(b1), MP(b2), MP(b3));
+}
+
+static void
+testmul1(char *a, char *b)
+{
+	mpint *b1, *b2, *b3;
+
+	b1 = strtomp(a, nil, 16, nil);
+	b2 = strtomp(b, nil, 16, nil);
+	b3 = mpnew(0);
+	mpmul(b1, b2, b3);
+	printf("%s * %s = %s\n", MP(b1), MP(b2), MP(b3));
+}
+
+static void
+testexp(char *base, char *exp, char *mod)
+{
+	mpint *b, *e, *m, *res;
+	int i;
+	uint64_t now;
+
+	b = strtomp(base, nil, 16, nil);
+	e = strtomp(exp, nil, 16, nil);
+	res = mpnew(0);
+	if(mod != nil)
+		m = strtomp(mod, nil, 16, nil);
+	else
+		m = nil;
+	now = nsec();
+	for(i = 0; i < loops; i++)
+		mpexp(b, e, m, res);
+	if(loops > 1)
+		printf("%"PRIu64"µs for a %d to the %d bit exp\n", (nsec()-now)/(loops*1000),
+			b->top*Dbits, e->top*Dbits);
+	if(m != nil)
+		printf("%s ^ %s mod %s == %s\n", MP(b), MP(e), MP(m), MP(res));
+	else
+		printf("%s ^ %s == %s\n", MP(b), MP(e), MP(res));
+	mpfree(b);
+	mpfree(e);
+	mpfree(res);
+	if(m != nil)
+		mpfree(m);
+}
+
+static void
+testgcd(void)
+{
+	mpint *a, *b, *d, *x, *y, *t1, *t2;
+	int i;
+	uint64_t now, then;
+	uint64_t etime;
+
+	d = mpnew(0);
+	x = mpnew(0);
+	y = mpnew(0);
+	t1 = mpnew(0);
+	t2 = mpnew(0);
+
+	etime = 0;
+
+	a = strtomp("4EECAB3E04C4E6BC1F49D438731450396BF272B4D7B08F91C38E88ADCD281699889AFF872E2204C80CCAA8E460797103DE539D5DF8335A9B20C0B44886384F134C517287202FCA914D8A5096446B40CD861C641EF9C2730CB057D7B133F4C2B16BBD3D75FDDBD9151AAF0F9144AAA473AC93CF945DBFE0859FB685D5CBD0A8B3", nil, 16, nil);
+	b = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBDB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A863A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D93D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil);
+	mpextendedgcd(a, b, d, x, y);
+	printf("gcd %s*%s+%s*%s = %s?\n", MP(a), MP(x), MP(b), MP(y), MP(d));
+	mpfree(a);
+	mpfree(b);
+
+	for(i = 0; i < loops; i++){
+		a = mprand(2048, prng, nil);
+		b = mprand(2048, prng, nil);
+		then = nsec();
+		mpextendedgcd(a, b, d, x, y);
+		now = nsec();
+		etime += now-then;
+		mpmul(a, x, t1);
+		mpmul(b, y, t2);
+		mpadd(t1, t2, t2);
+		if(mpcmp(d, t2) != 0)
+			printf("%d gcd %s*%s+%s*%s != %s\n", i, MP(a), MP(x), MP(b), MP(y), MP(d));
+//		else
+//			printf("%d euclid %s*%s+%s*%s == %s\n", i, MP(a), MP(x), MP(b), MP(y), MP(d));
+		mpfree(a);
+		mpfree(b);
+	}
+
+	mpfree(x);
+	mpfree(y);
+	mpfree(d);
+	mpfree(t1);
+	mpfree(t2);
+
+	if(loops > 1)
+		printf("binary %"PRIu64"\n", etime);
+}
+
+int
+main(int argc, char **argv)
+{
+	mpint *x, *y;
+
+	if(argc == 3 && strcmp(argv[1], "-n") == 0)
+		loops = atoi(argv[2]);
+
+	memset(str, 0, sizeof(str));
+	D_PNAN = D_NNAN = strtod("+NaN", nil);
+	D_PINF = D_NINF = strtod("+Inf", nil);
+
+	union ieee754_double *d;
+	d = (union ieee754_double *)&D_NNAN;
+	d->ieee.negative = 1;
+	d = (union ieee754_double *)&D_NINF;
+	d->ieee.negative = 1;
+
+	srand(0);
+	mpsetminbits(2*Dbits);
+	testshift("1111111111111111");
+	testaddsub("fffffffffffffffff");
+	testdigdiv("1234567812345678", 0x76543218);
+	testdigdiv("1ffff", 0xffff);
+	testdigdiv("ffff", 0xffff);
+	testdigdiv("fff", 0xffff);
+	testdigdiv("effffffff", 0xffff);
+	testdigdiv("ffffffff", 0x1);
+	testdigdiv("ffffffff", 0);
+	testdigdiv("200000000", 2);
+	testdigdiv("ffffff00fffffff1", 0xfffffff1);
+	testvecdigmuladd("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 2);
+	testconv("0");
+	testconv("-abc0123456789abcedf");
+	testconv("abc0123456789abcedf");
+	testconv("ffffffff");
+	testconv("aaaaaaaaaaaaaaaaa");
+	testconv("1111111111111111");
+	testconv("33333333333333333333333333333333");
+
+	testvecdigmulsub("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 2);
+	testsub1("1FFFFFFFE00000000", "FFFFFFFE00000001");
+	testmul1("ffffffff", "f");
+	testmul("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+	testmul1("100000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000004FFFFFFFFFFFFFFFE0000000200000000000000000000000000000003FFFFFFFFFFFFFFFE0000000200000000000000000000000000000002FFFFFFFFFFFFFFFE0000000200000000000000000000000000000001FFFFFFFFFFFFFFFE0000000200000000000000000000000000000000FFFFFFFFFFFFFFFE0000000200000000FFFFFFFE00000001", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+	testmul1("1000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001", "1000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001");
+	testmul1("1000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001", "1000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001000000000000000000000001");
+	testmul1("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+	x = mprand(256, prng, nil);
+	y = mprand(128, prng, nil);
+	testdiv(x, y);
+	x = mprand(2048, prng, nil);
+	y = mprand(1024, prng, nil);
+	testdiv(x, y);
+	x = mprand(4*1024, prng, nil);
+	y = mprand(4*1024, prng, nil);
+	testmul2(x, y);
+	testsub1("677132C9", "-A26559B6");
+	testgcd();
+	x = mprand(512, prng, nil);
+	x->sign = -1;
+	y = mprand(256, prng, nil);
+	testdiv(x, y);
+	testmod(x, y);
+	x->sign = 1;
+	testinvert(y, x);
+	testexp("111111111", "222", "1000000000000000000000");
+	testexp("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+	return 0;
+}
--- a/meson.build
+++ b/meson.build
@@ -25,17 +25,22 @@
 	language: 'c',
 )
 
-src = [
-	'3rd/mt19937-64.c',
+mp = [
 	'3rd/mp/mpadd.c',
 	'3rd/mp/mpaux.c',
 	'3rd/mp/mpcmp.c',
 	'3rd/mp/mpdigdiv.c',
 	'3rd/mp/mpdiv.c',
+	'3rd/mp/mpextendedgcd.c',
+	'3rd/mp/mpexp.c',
 	'3rd/mp/mpfmt.c',
+	'3rd/mp/mpinvert.c',
 	'3rd/mp/mpleft.c',
 	'3rd/mp/mplogic.c',
+	'3rd/mp/mpmod.c',
+	'3rd/mp/mpmodop.c',
 	'3rd/mp/mpmul.c',
+	'3rd/mp/mprand.c',
 	'3rd/mp/mpright.c',
 	'3rd/mp/mpsub.c',
 	'3rd/mp/mptobe.c',
@@ -52,11 +57,18 @@
 	'3rd/mp/mpvectscmp.c',
 	'3rd/mp/strtomp.c',
 	'3rd/mp/u16.c',
-	'3rd/spooky.c',
+]
+
+utf = [
 	'3rd/utf/rune.c',
 	'3rd/utf/runeistype.c',
 	'3rd/utf/runetotype.c',
 	'3rd/utf/utfnlen.c',
+]
+
+src = [
+	'3rd/mt19937-64.c',
+	'3rd/spooky.c',
 	'bitvector.c',
 	'builtins.c',
 	'cvalues.c',
@@ -145,6 +157,8 @@
 	'flisp',
 	sources: [
 		src,
+		mp,
+		utf,
 		boot,
 		builtins,
 	],
@@ -158,6 +172,20 @@
 		'posix',
 	),
 )
+
+mptest = executable(
+	'mptest',
+	sources: [
+		'3rd/mp/test.c',
+		mp,
+	],
+	include_directories: include_directories(
+		'3rd',
+		'3rd/mp',
+		'posix',
+	),
+)
+test('mp', mptest)
 
 tests_dir = join_paths(meson.current_source_dir(), 'test')
 
--- a/posix/mp.h
+++ b/posix/mp.h
@@ -17,8 +17,6 @@
 	};
 };
 
-#define sysfatal(s) do{ fprintf(stderr, "%s\n", s); abort(); }while(0)
-
 #define mpdighi  (mpdigit)(1U<<(Dbits-1))
 #define DIGITS(x) ((x) >= -(Dbits-1) ? ((Dbits - 1 + (x))/Dbits) : 0)