shithub: femtolisp

Download patch

ref: bdb4b15192e2452ec6019675517599b970755c7c
parent: 7c57c0393fd368602203e8478817ab03861fc2c5
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Wed Mar 29 16:37:18 EDT 2023

initial (unfinished) implementation of mpint number type

--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@
 instructions.lsp
 builtins.lsp
 builtin_fns.h
+*.core
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,5 @@
+9front portions (especially libmp) is under MIT license.
+
 Copyright (c) 2008 Jeff Bezanson
 
 All rights reserved.
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@
 TARG=flisp
 LLT=llt/libllt.a
 CFLAGS?=-O2 -g
-CFLAGS+=-Wall -Wextra -falign-functions -Wno-strict-aliasing -std=c99 -D_DEFAULT_SOURCE
+CFLAGS+=-Wall -Wextra -Wno-shift-op-parentheses -Wno-bitwise-op-parentheses -falign-functions -Wno-strict-aliasing -std=c99 -D_DEFAULT_SOURCE
 LDFLAGS?=
 
 OBJS=\
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@
  * "boot" image is built into the executable
  * vm opcode definitions and tables are generated from a single file
  * fixed bootstrap (makes it work properly when opcodes change)
+ * bigints (though not completely finished yet)
 
 ## Characteristics
 
--- a/cvalues.c
+++ b/cvalues.c
@@ -7,7 +7,7 @@
 #endif
 
 value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
-value_t int64sym, uint64sym;
+value_t int64sym, uint64sym, mpintsym;
 value_t longsym, ulongsym, bytesym, wcharsym;
 value_t floatsym, doublesym;
 value_t gftypesym, stringtypesym, wcstringtypesym;
@@ -23,6 +23,7 @@
 static fltype_t *int32type, *uint32type;
 static fltype_t *int64type, *uint64type;
 static fltype_t *longtype, *ulongtype;
+static fltype_t *mpinttype;
 static fltype_t *floattype, *doubletype;
        fltype_t *bytetype, *wchartype;
        fltype_t *stringtype, *wcstringtype;
@@ -127,7 +128,7 @@
     cvalue_t *pcv;
     int str=0;
 
-    if (valid_numtype(type->numtype))
+    if (valid_numtype(type->numtype) && type->numtype != T_MPINT)
         return cprim(type, sz);
 
     if (type->eltype == bytetype) {
@@ -308,6 +309,50 @@
 num_ctor(float, float, T_FLOAT)
 num_ctor(double, double, T_DOUBLE)
 
+static int cvalue_mpint_init(fltype_t *type, value_t arg, void *dest)
+{
+    mpint *n;
+    USED(type);
+    if (isfixnum(arg)) {
+        n = vtomp(numval(arg), nil);
+    }
+    else if (iscprim(arg)) {
+        cprim_t *cp = (cprim_t*)ptr(arg);
+        void *p = cp_data(cp);
+        n = conv_to_mpint(p, cp_numtype(cp));
+    }
+    else {
+        return 1;
+    }
+    *((mpint**)dest) = n;
+    return 0;
+}
+
+/* */ BUILTIN("mpint", mpint)
+{
+    if (nargs==0) { PUSH(fixnum(0)); args = &Stack[SP-1]; }
+    value_t cv = cvalue(mpinttype, sizeof(mpint*));
+    if (cvalue_mpint_init(mpinttype, args[0], cv_data((cvalue_t*)ptr(cv))))
+        type_error("number", args[0]);
+    return cv;
+}
+
+value_t mk_mpint(mpint *n)
+{
+    value_t cv = cvalue(mpinttype, sizeof(mpint*));
+    *(mpint**)cvalue_data(cv) = n;
+    return cv;
+}
+
+static void free_mpint(value_t self)
+{
+    mpint **s = value2c(mpint**, self);
+    if (*s != mpzero && *s != mpone && *s != mptwo)
+        mpfree(*s);
+}
+
+static cvtable_t mpint_vtable = { nil, nil, free_mpint, nil };
+
 value_t size_wrap(size_t sz)
 {
     if (fits_fixnum(sz))
@@ -354,7 +399,7 @@
                 return 0;
             }
         }
-        lerrorf(ArgError, "enum: invalid enum value");
+        lerrorf(ArgError, "invalid enum value");
     }
     if (isfixnum(arg)) {
         n = (int)numval(arg);
@@ -367,7 +412,7 @@
         type_error("number", arg);
     }
     if ((unsigned)n >= vector_size(syms))
-        lerrorf(ArgError, "enum: value out of range");
+        lerrorf(ArgError, "value out of range");
     *(int*)dest = n;
     return 0;
 }
@@ -435,7 +480,7 @@
             arg = cdr_(arg);
         }
         if (i != cnt)
-            lerrorf(ArgError, "array: size mismatch");
+            lerrorf(ArgError, "size mismatch");
         return 0;
     }
     else if (iscvalue(arg)) {
@@ -446,12 +491,12 @@
                 if (cv_len(cv) == sz)
                     memmove(dest, cv_data(cv), sz);
                 else
-                    lerrorf(ArgError, "array: size mismatch");
+                    lerrorf(ArgError, "size mismatch");
                 return 0;
             }
             else {
                 // TODO: initialize array from different type elements
-                lerrorf(ArgError, "array: element type mismatch");
+                lerrorf(ArgError, "element type mismatch");
             }
         }
     }
@@ -553,7 +598,7 @@
         if (hed == arraysym) {
             value_t t = car(cdr_(type));
             if (!iscons(cdr_(cdr_(type))))
-                lerrorf(ArgError, "sizeof: incomplete type");
+                lerrorf(ArgError, "incomplete type");
             value_t n = car_(cdr_(cdr_(type)));
             size_t sz = toulong(n);
             return sz * ctype_sizeof(t, palign);
@@ -570,7 +615,7 @@
         }
     }
 
-    lerrorf(ArgError, "sizeof: invalid c type");
+    lerrorf(ArgError, "invalid c type");
 }
 
 extern fltype_t *iostreamtype;
@@ -687,11 +732,11 @@
 {
     argcount(nargs, 1);
     if (iscons(args[0]) || isvector(args[0]))
-        lerrorf(ArgError, "copy: argument must be a leaf atom");
+        lerrorf(ArgError, "argument must be a leaf atom");
     if (!iscvalue(args[0]))
         return args[0];
     if (!cv_isPOD((cvalue_t*)ptr(args[0])))
-        lerrorf(ArgError, "copy: argument must be a plain-old-data type");
+        lerrorf(ArgError, "argument must be a plain-old-data type");
     return cvalue_copy(args[0]);
 }
 
@@ -708,7 +753,7 @@
     cvinitfunc_t f=type->init;
 
     if (f == nil)
-        lerrorf(ArgError, "c-value: invalid c type");
+        lerrorf(ArgError, "invalid c type");
 
     f(type, v, dest);
 }
@@ -912,7 +957,12 @@
     double Faccum=0;
     int32_t inexact = 0;
     uint32_t i;
+    int64_t i64;
     value_t arg;
+    mpint *Maccum = nil, *x;
+    numerictype_t pt;
+    fixnum_t pi;
+    void *a;
 
     FOR_ARGS(i,0,arg,args) {
         if (isfixnum(arg)) {
@@ -919,17 +969,14 @@
             Saccum += numval(arg);
             continue;
         }
-        else if (iscprim(arg)) {
-            cprim_t *cp = (cprim_t*)ptr(arg);
-            void *a = cp_data(cp);
-            int64_t i64;
-            switch(cp_numtype(cp)) {
+        if (num_to_ptr(arg, &pi, &pt, &a)) {
+            switch(pt) {
             case T_INT8:   Saccum += *(int8_t*)a; break;
-            case T_UINT8:  Saccum += *(uint8_t*)a; break;
+            case T_UINT8:  Uaccum += *(uint8_t*)a; break;
             case T_INT16:  Saccum += *(int16_t*)a; break;
-            case T_UINT16: Saccum += *(uint16_t*)a; break;
+            case T_UINT16: Uaccum += *(uint16_t*)a; break;
             case T_INT32:  Saccum += *(int32_t*)a; break;
-            case T_UINT32: Saccum += *(uint32_t*)a; break;
+            case T_UINT32: Uaccum += *(uint32_t*)a; break;
             case T_INT64:
                 i64 = *(int64_t*)a;
                 if (i64 > 0)
@@ -938,6 +985,11 @@
                     Saccum += i64;
                 break;
             case T_UINT64: Uaccum += *(uint64_t*)a; break;
+            case T_MPINT:
+                if (Maccum == nil)
+                    Maccum = mpnew(0);
+                mpadd(Maccum, *(mpint**)a, Maccum);
+                break;
             case T_FLOAT:  Faccum += *(float*)a; inexact = 1; break;
             case T_DOUBLE: Faccum += *(double*)a; inexact = 1; break;
             default:
@@ -945,15 +997,30 @@
             }
             continue;
         }
-    add_type_error:
+
+add_type_error:
+        mpfree(Maccum);
         type_error("number", arg);
     }
     if (inexact) {
         Faccum += Uaccum;
         Faccum += Saccum;
+        if (Maccum != nil) {
+            Faccum += mptod(Maccum);
+            mpfree(Maccum);
+        }
         return mk_double(Faccum);
     }
-    else if (Saccum < 0) {
+    if (Maccum != nil) {
+        /* FIXME - check if it fits into fixnum first, etc */
+        x = vtomp(Saccum, nil);
+        mpadd(Maccum, x, Maccum);
+        x = uvtomp(Uaccum, x);
+        mpadd(Maccum, x, Maccum);
+        mpfree(x);
+        return mk_mpint(Maccum);
+    }
+    if (Saccum < 0) {
         uint64_t negpart = (uint64_t)(-Saccum);
         if (negpart > Uaccum) {
             Saccum += (int64_t)Uaccum;
@@ -977,20 +1044,23 @@
 
 static value_t fl_neg(value_t n)
 {
+    uint32_t ui32;
+    int32_t i32;
+    int64_t i64;
+    mpint *mp;
+    numerictype_t pt;
+    fixnum_t pi;
+    void *a;
+
     if (isfixnum(n)) {
         fixnum_t s = fixnum(-numval(n));
         if (__unlikely((value_t)s == n))
             return mk_xlong(-numval(n));
-        else
-            return s;
+        return s;
     }
-    else if (iscprim(n)) {
-        cprim_t *cp = (cprim_t*)ptr(n);
-        void *a = cp_data(cp);
-        uint32_t ui32;
-        int32_t i32;
-        int64_t i64;
-        switch(cp_numtype(cp)) {
+
+    if (num_to_ptr(n, &pi, &pt, &a)) {
+        switch(pt) {
         case T_INT8:   return fixnum(-(int32_t)*(int8_t*)a);
         case T_UINT8:  return fixnum(-(int32_t)*(uint8_t*)a);
         case T_INT16:  return fixnum(-(int32_t)*(int16_t*)a);
@@ -1010,11 +1080,15 @@
                 return mk_uint64((uint64_t)BIT63);
             return mk_int64(-i64);
         case T_UINT64: return mk_int64(-(int64_t)*(uint64_t*)a);
+        case T_MPINT:
+            mp = mpcopy(*(mpint**)a);
+            mpsub(mpzero, mp, mp);
+            return mk_mpint(mp);
         case T_FLOAT:  return mk_float(-*(float*)a);
         case T_DOUBLE: return mk_double(-*(double*)a);
-            break;
         }
     }
+
     type_error("number", n);
 }
 
@@ -1023,8 +1097,13 @@
     uint64_t Uaccum=1;
     double Faccum=1;
     int32_t inexact = 0;
+    int64_t i64;
     uint32_t i;
     value_t arg;
+    mpint *Maccum=nil, *x;
+    numerictype_t pt;
+    fixnum_t pi;
+    void *a;
 
     FOR_ARGS(i,0,arg,args) {
         if (isfixnum(arg)) {
@@ -1031,17 +1110,14 @@
             Saccum *= numval(arg);
             continue;
         }
-        else if (iscprim(arg)) {
-            cprim_t *cp = (cprim_t*)ptr(arg);
-            void *a = cp_data(cp);
-            int64_t i64;
-            switch(cp_numtype(cp)) {
+        if (num_to_ptr(arg, &pi, &pt, &a)) {
+            switch(pt) {
             case T_INT8:   Saccum *= *(int8_t*)a; break;
-            case T_UINT8:  Saccum *= *(uint8_t*)a; break;
+            case T_UINT8:  Uaccum *= *(uint8_t*)a; break;
             case T_INT16:  Saccum *= *(int16_t*)a; break;
-            case T_UINT16: Saccum *= *(uint16_t*)a; break;
+            case T_UINT16: Uaccum *= *(uint16_t*)a; break;
             case T_INT32:  Saccum *= *(int32_t*)a; break;
-            case T_UINT32: Saccum *= *(uint32_t*)a; break;
+            case T_UINT32: Uaccum *= *(uint32_t*)a; break;
             case T_INT64:
                 i64 = *(int64_t*)a;
                 if (i64 > 0)
@@ -1050,6 +1126,11 @@
                     Saccum *= i64;
                 break;
             case T_UINT64: Uaccum *= *(uint64_t*)a; break;
+            case T_MPINT:
+                if (Maccum == nil)
+                    Maccum = mpcopy(mpone);
+                mpmul(Maccum, *(mpint**)a, Maccum);
+                break;
             case T_FLOAT:  Faccum *= *(float*)a; inexact = 1; break;
             case T_DOUBLE: Faccum *= *(double*)a; inexact = 1; break;
             default:
@@ -1057,15 +1138,29 @@
             }
             continue;
         }
-    mul_type_error:
+
+mul_type_error:
         type_error("number", arg);
     }
     if (inexact) {
         Faccum *= Uaccum;
         Faccum *= Saccum;
+        if (Maccum != nil) {
+            Faccum *= mptod(Maccum);
+            mpfree(Maccum);
+        }
         return mk_double(Faccum);
     }
-    else if (Saccum < 0) {
+    if (Maccum != nil) {
+        /* FIXME might still fit into a fixnum */
+        x = vtomp(Saccum, nil);
+        mpmul(Maccum, x, Maccum);
+        x = uvtomp(Uaccum, x);
+        mpmul(Maccum, x, Maccum);
+        mpfree(x);
+        return mk_mpint(Maccum);
+    }
+    if (Saccum < 0) {
         Saccum *= (int64_t)Uaccum;
         if (Saccum >= INT32_MIN) {
             if (fits_fixnum(Saccum)) {
@@ -1081,9 +1176,10 @@
     return return_from_uint64(Uaccum);
 }
 
-static int num_to_ptr(value_t a, fixnum_t *pi, numerictype_t *pt, void **pp)
+int num_to_ptr(value_t a, fixnum_t *pi, numerictype_t *pt, void **pp)
 {
     cprim_t *cp;
+    cvalue_t *cv;
     if (isfixnum(a)) {
         *pi = numval(a);
         *pp = pi;
@@ -1094,6 +1190,12 @@
         *pp = cp_data(cp);
         *pt = cp_numtype(cp);
     }
+    else if (iscvalue(a)) {
+        cv = (cvalue_t*)ptr(a);
+        *pp = cv_data(cv);
+        *pt = cv_class(cv)->numtype;
+        return valid_numtype(*pt);
+    }
     else {
         return 0;
     }
@@ -1465,6 +1567,11 @@
     mk_primtype(wchar, int32_t);
     mk_primtype(float, float);
     mk_primtype(double, double);
+
+    ctor_cv_intern(mpint, T_MPINT, mpint*);
+    mpinttype = get_type(mpintsym);
+    mpinttype->init = cvalue_mpint_init;
+    mpinttype->vtable = &mpint_vtable;
 
     stringtype = get_type(symbol_value(stringtypesym));
     wcstringtype = get_type(symbol_value(wcstringtypesym));
--- a/equal.c
+++ b/equal.c
@@ -55,6 +55,7 @@
 static value_t bounded_compare(value_t a, value_t b, int bound, int eq)
 {
     value_t d;
+    cvalue_t *cv;
 
  compare_top:
     if (a == b) return fixnum(0);
@@ -74,6 +75,11 @@
                 return fixnum(1);
             return fixnum(numeric_compare(a, b, eq, 1, 0));
         }
+        if (iscvalue(b)) {
+            cv = ptr(b);
+            if (valid_numtype(cv_class(cv)->numtype))
+                return fixnum(numeric_compare(a, b, eq, 1, 0));
+        }
         return fixnum(-1);
     case TAG_SYM:
         if (eq) return fixnum(1);
@@ -97,6 +103,11 @@
             return fixnum(c);
         break;
     case TAG_CVALUE:
+        cv = ptr(a);
+        if (valid_numtype(cv_class(cv)->numtype)) {
+            if((c = numeric_compare(a, b, eq, 1, 0)) != 2)
+                return fixnum(c);
+        }
         if (iscvalue(b)) {
             if (cv_isPOD((cvalue_t*)ptr(a)) && cv_isPOD((cvalue_t*)ptr(b)))
                 return cvalue_compare(a, b);
@@ -329,7 +340,14 @@
     case TAG_CVALUE:
         cv = (cvalue_t*)ptr(a);
         data = cv_data(cv);
-        return memhash(data, cv_len(cv));
+        if (cv->type == mpinttype) {
+            len = mptobe(*(mpint**)data, nil, 0, (uint8_t**)&data);
+            h = memhash(data, len);
+            free(data);
+        } else {
+            h = memhash(data, cv_len(cv));
+        }
+        return h;
 
     case TAG_VECTOR:
         if (bound <= 0) {
--- a/flisp.c
+++ b/flisp.c
@@ -711,11 +711,16 @@
 
 int fl_isnumber(value_t v)
 {
-    if (isfixnum(v)) return 1;
+    if (isfixnum(v))
+        return 1;
     if (iscprim(v)) {
-        cprim_t *c = (cprim_t*)ptr(v);
+        cprim_t *c = ptr(v);
         return c->type != wchartype;
     }
+    if (iscvalue(v)) {
+        cvalue_t *c = ptr(v);
+        return valid_numtype(cv_class(c)->numtype);
+    }
     return 0;
 }
 
@@ -813,9 +818,9 @@
     value_t s4 = Stack[SP-4];
     value_t s5 = Stack[SP-5];
     if (nargs < nreq)
-        lerrorf(ArgError, "apply: too few arguments");
+        lerrorf(ArgError, "too few arguments");
     if (extr > nelem(args))
-        lerrorf(ArgError, "apply: too many arguments");
+        lerrorf(ArgError, "too many arguments");
     for (i=0; i < extr; i++) args[i] = UNBOUND;
     for (i=nreq; i < nargs; i++) {
         v = Stack[bp+i];
@@ -855,7 +860,7 @@
  no_kw:
     nrestargs = nargs - i;
     if (!va && nrestargs > 0)
-        lerrorf(ArgError, "apply: too many arguments");
+        lerrorf(ArgError, "too many arguments");
     nargs = ntot + nrestargs;
     if (nrestargs)
         memmove(&Stack[bp+ntot], &Stack[bp+i], nrestargs*sizeof(value_t));
@@ -1722,7 +1727,7 @@
                 }
             }
             else if (s < 0) {
-                lerrorf(ArgError, "apply: too few arguments");
+                lerrorf(ArgError, "too few arguments");
             }
             else {
                 PUSH(0);
@@ -1745,10 +1750,10 @@
             i = GET_INT32(ip); ip+=4;
             n = GET_INT32(ip); ip+=4;
             if (nargs < i)
-                lerrorf(ArgError, "apply: too few arguments");
+                lerrorf(ArgError, "too few arguments");
             if ((int32_t)n > 0) {
                 if (nargs > n)
-                    lerrorf(ArgError, "apply: too many arguments");
+                    lerrorf(ArgError, "too many arguments");
             }
             else n = -n;
             if (n > nargs) {
@@ -1834,6 +1839,14 @@
 
 // builtins -------------------------------------------------------------------
 
+BUILTIN("gc", gc)
+{
+    USED(args);
+    argcount(nargs, 0);
+    gc(0);
+    return FL_T;
+}
+
 BUILTIN("function", function)
 {
     if (nargs == 1 && issymbol(args[0]))
@@ -1884,7 +1897,7 @@
             }
         }
         if (isgensym(fn->name))
-            lerrorf(ArgError, "function: name should not be a gensym");
+            lerrorf(ArgError, "name should not be a gensym");
     }
     return fv;
 }
@@ -1967,7 +1980,7 @@
 BUILTIN("map", map)
 {
     if (nargs < 2)
-        lerrorf(ArgError, "map: too few arguments");
+        lerrorf(ArgError, "too few arguments");
     if (!iscons(args[1])) return NIL;
     value_t first, last, v;
     int64_t argSP = args-Stack;
--- a/flisp.h
+++ b/flisp.h
@@ -7,6 +7,7 @@
     T_INT16, T_UINT16,
     T_INT32, T_UINT32,
     T_INT64, T_UINT64,
+    T_MPINT,
     T_FLOAT,
     T_DOUBLE,
 } numerictype_t;
@@ -98,6 +99,8 @@
 // doesn't lead to other values
 #define leafp(a) (((a)&3) != 3)
 
+int num_to_ptr(value_t a, fixnum_t *pi, numerictype_t *pt, void **pp);
+
 #define isforwarded(v) (((value_t*)ptr(v))[0] == TAG_FWD)
 #define forwardloc(v)  (((value_t*)ptr(v))[1])
 #define forward(v,to) do { (((value_t*)ptr(v))[0] = TAG_FWD); \
@@ -368,6 +371,7 @@
 
 double conv_to_double(void *data, numerictype_t tag);
 void conv_from_double(void *data, double d, numerictype_t tag);
+mpint *conv_to_mpint(void *data, numerictype_t tag);
 int64_t conv_to_int64(void *data, numerictype_t tag);
 uint64_t conv_to_uint64(void *data, numerictype_t tag);
 int32_t conv_to_int32(void *data, numerictype_t tag);
--- a/llt/Makefile
+++ b/llt/Makefile
@@ -14,6 +14,33 @@
 	random.o\
 	timefuncs.o\
 	utf8.o\
+    \
+	mpadd.o\
+	mpaux.o\
+	mpvecsub.o\
+	mpcmp.o\
+	mpdiv.o\
+	mpfmt.o\
+	mpmul.o\
+	mpsub.o\
+	mpleft.o\
+	mpright.o\
+	mptobe.o\
+	mptod.o\
+	mptoi.o\
+	mptoui.o\
+	mptouv.o\
+	mpdigdiv.o\
+	mpvecdigmuladd.o\
+	mptov.o\
+	mpvecadd.o\
+	strtomp.o\
+	u16.o\
+	u32.o\
+	u64.o\
+    mptober.o\
+    mpveccmp.o\
+    mpvectscmp.o\
 
 .PHONY: all default clean
 
--- /dev/null
+++ b/llt/mpadd.c
@@ -1,0 +1,56 @@
+#include "platform.h"
+
+// sum = abs(b1) + abs(b2), i.e., add the magnitudes
+void
+mpmagadd(mpint *b1, mpint *b2, mpint *sum)
+{
+	int m, n;
+	mpint *t;
+
+	sum->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
+	// get the sizes right
+	if(b2->top > b1->top){
+		t = b1;
+		b1 = b2;
+		b2 = t;
+	}
+	n = b1->top;
+	m = b2->top;
+	if(n == 0){
+		mpassign(mpzero, sum);
+		return;
+	}
+	if(m == 0){
+		mpassign(b1, sum);
+		sum->sign = 1;
+		return;
+	}
+	mpbits(sum, (n+1)*Dbits);
+	sum->top = n+1;
+
+	mpvecadd(b1->p, n, b2->p, m, sum->p);
+	sum->sign = 1;
+
+	mpnorm(sum);
+}
+
+// sum = b1 + b2
+void
+mpadd(mpint *b1, mpint *b2, mpint *sum)
+{
+	int sign;
+
+	if(b1->sign != b2->sign){
+		assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+		if(b1->sign < 0)
+			mpmagsub(b2, b1, sum);
+		else
+			mpmagsub(b1, b2, sum);
+	} else {
+		sign = b1->sign;
+		mpmagadd(b1, b2, sum);
+		if(sum->top != 0)
+			sum->sign = sign;
+	}
+}
--- /dev/null
+++ b/llt/mpaux.c
@@ -1,0 +1,201 @@
+#include "platform.h"
+
+static mpdigit _mptwodata[1] = { 2 };
+static mpint _mptwo =
+{
+	1, 1, 1,
+	_mptwodata,
+	MPstatic|MPnorm
+};
+mpint *mptwo = &_mptwo;
+
+static mpdigit _mponedata[1] = { 1 };
+static mpint _mpone =
+{
+	1, 1, 1,
+	_mponedata,
+	MPstatic|MPnorm
+};
+mpint *mpone = &_mpone;
+
+static mpdigit _mpzerodata[1] = { 0 };
+static mpint _mpzero =
+{
+	1, 1, 0,
+	_mpzerodata,
+	MPstatic|MPnorm
+};
+mpint *mpzero = &_mpzero;
+
+static int mpmindigits = 33;
+
+// set minimum digit allocation
+void
+mpsetminbits(int n)
+{
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
+	if(n == 0)
+		n = 1;
+	mpmindigits = DIGITS(n);
+}
+
+// allocate an n bit 0'd number 
+mpint*
+mpnew(int n)
+{
+	mpint *b;
+
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
+
+	n = DIGITS(n);
+	if(n < mpmindigits)
+		n = mpmindigits;
+	b = calloc(1, sizeof(mpint) + n*Dbytes);
+	if(b == nil)
+		sysfatal("mpnew: %r");
+	b->p = (mpdigit*)&b[1];
+	b->size = n;
+	b->sign = 1;
+	b->flags = MPnorm;
+
+	return b;
+}
+
+// guarantee at least n significant bits
+void
+mpbits(mpint *b, int m)
+{
+	int n;
+
+	n = DIGITS(m);
+	if(b->size >= n){
+		if(b->top >= n)
+			return;
+	} else {
+		if(b->p == (mpdigit*)&b[1]){
+			b->p = (mpdigit*)malloc(n*Dbytes);
+			if(b->p == nil)
+				sysfatal("mpbits: %r");
+			memmove(b->p, &b[1], Dbytes*b->top);
+			memset(&b[1], 0, Dbytes*b->size);
+		} else {
+			b->p = (mpdigit*)realloc(b->p, n*Dbytes);
+			if(b->p == nil)
+				sysfatal("mpbits: %r");
+		}
+		b->size = n;
+	}
+	memset(&b->p[b->top], 0, Dbytes*(n - b->top));
+	b->top = n;
+	b->flags &= ~MPnorm;
+}
+
+void
+mpfree(mpint *b)
+{
+	if(b == nil)
+		return;
+	if(b->flags & MPstatic)
+		sysfatal("freeing mp constant");
+	memset(b->p, 0, b->size*Dbytes);
+	if(b->p != (mpdigit*)&b[1])
+		free(b->p);
+	free(b);
+}
+
+mpint*
+mpnorm(mpint *b)
+{
+	int i;
+
+	if(b->flags & MPtimesafe){
+		assert(b->sign == 1);
+		b->flags &= ~MPnorm;
+		return b;
+	}
+	for(i = b->top-1; i >= 0; i--)
+		if(b->p[i] != 0)
+			break;
+	b->top = i+1;
+	if(b->top == 0)
+		b->sign = 1;
+	b->flags |= MPnorm;
+	return b;
+}
+
+mpint*
+mpcopy(mpint *old)
+{
+	mpint *new;
+
+	new = mpnew(Dbits*old->size);
+	new->sign = old->sign;
+	new->top = old->top;
+	new->flags = old->flags & ~(MPstatic|MPfield);
+	memmove(new->p, old->p, Dbytes*old->top);
+	return new;
+}
+
+void
+mpassign(mpint *old, mpint *new)
+{
+	if(new == nil || old == new)
+		return;
+	new->top = 0;
+	mpbits(new, Dbits*old->top);
+	new->sign = old->sign;
+	new->top = old->top;
+	new->flags &= ~MPnorm;
+	new->flags |= old->flags & ~(MPstatic|MPfield);
+	memmove(new->p, old->p, Dbytes*old->top);
+}
+
+// number of significant bits in mantissa
+int
+mpsignif(mpint *n)
+{
+	int i, j;
+	mpdigit d;
+
+	if(n->top == 0)
+		return 0;
+	for(i = n->top-1; i >= 0; i--){
+		d = n->p[i];
+		for(j = Dbits-1; j >= 0; j--){
+			if(d & (((mpdigit)1)<<j))
+				return i*Dbits + j + 1;
+		}
+	}
+	return 0;
+}
+
+// k, where n = 2**k * q for odd q
+int
+mplowbits0(mpint *n)
+{
+	int k, bit, digit;
+	mpdigit d;
+
+	assert(n->flags & MPnorm);
+	if(n->top==0)
+		return 0;
+	k = 0;
+	bit = 0;
+	digit = 0;
+	d = n->p[0];
+	for(;;){
+		if(d & (1<<bit))
+			break;
+		k++;
+		bit++;
+		if(bit==Dbits){
+			if(++digit >= n->top)
+				return 0;
+			d = n->p[digit];
+			bit = 0;
+		}
+	}
+	return k;
+}
--- /dev/null
+++ b/llt/mpcmp.c
@@ -1,0 +1,28 @@
+#include "platform.h"
+
+// return neg, 0, pos as abs(b1)-abs(b2) is neg, 0, pos
+int
+mpmagcmp(mpint *b1, mpint *b2)
+{
+	int i;
+
+	i = b1->flags | b2->flags;
+	if(i & MPtimesafe)
+		return mpvectscmp(b1->p, b1->top, b2->p, b2->top);
+	if(i & MPnorm){
+		i = b1->top - b2->top;
+		if(i)
+			return i;
+	}
+	return mpveccmp(b1->p, b1->top, b2->p, b2->top);
+}
+
+// return neg, 0, pos as b1-b2 is neg, 0, pos
+int
+mpcmp(mpint *b1, mpint *b2)
+{
+	int sign;
+
+	sign = (b1->sign - b2->sign) >> 1;	// -1, 0, 1
+	return sign | (sign&1)-1 & mpmagcmp(b1, b2)*b1->sign;
+}
--- /dev/null
+++ b/llt/mpdigdiv.c
@@ -1,0 +1,54 @@
+#include "platform.h"
+
+//
+//	divide two digits by one and return quotient
+//
+void
+mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient)
+{
+	mpdigit hi, lo, q, x, y;
+	int i;
+
+	hi = dividend[1];
+	lo = dividend[0];
+
+	// return highest digit value if the result >= 2**32
+	if(hi >= divisor || divisor == 0){
+		divisor = 0;
+		*quotient = ~divisor;
+		return;
+	}
+
+	// very common case
+	if(~divisor == 0){
+		lo += hi;
+		if(lo < hi){
+			hi++;
+			lo++;
+		}
+		if(lo+1 == 0)
+			hi++;
+		*quotient = hi;
+		return;
+	}
+
+	// at this point we know that hi < divisor
+	// just shift and subtract till we're done
+	q = 0;
+	x = divisor;
+	for(i = Dbits-1; hi > 0 && i >= 0; i--){
+		x >>= 1;
+		if(x > hi)
+			continue;
+		y = divisor<<i;
+		if(x == hi && y > lo)
+			continue;
+		if(y > lo)
+			hi--;
+		lo -= y;
+		hi -= x;
+		q |= 1U<<i;
+	}
+	q += lo/divisor;
+	*quotient = q;
+}
--- /dev/null
+++ b/llt/mpdiv.c
@@ -1,0 +1,140 @@
+#include "platform.h"
+
+// division ala knuth, seminumerical algorithms, pp 237-238
+// the numbers are stored backwards to what knuth expects so j
+// counts down rather than up.
+
+void
+mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
+{
+	int j, s, vn, sign, qsign, rsign;
+	mpdigit qd, *up, *vp, *qp;
+	mpint *u, *v, *t;
+
+	assert(quotient != remainder);
+	assert(divisor->flags & MPnorm);
+
+	// divide bv zero
+	if(divisor->top == 0)
+		abort();
+
+	// division by one or small powers of two
+	if(divisor->top == 1 && (divisor->p[0] & divisor->p[0]-1) == 0){
+		vlong r = 0;
+		if(dividend->top > 0)
+			r = (vlong)dividend->sign * (dividend->p[0] & divisor->p[0]-1);
+		if(quotient != nil){
+			sign = divisor->sign;
+			for(s = 0; ((divisor->p[0] >> s) & 1) == 0; s++)
+				;
+			mpright(dividend, s, quotient);
+			if(sign < 0)
+				quotient->sign ^= (-mpmagcmp(quotient, mpzero) >> 31) << 1;
+		}
+		if(remainder != nil){
+			remainder->flags |= dividend->flags & MPtimesafe;
+			vtomp(r, remainder);
+		}
+		return;
+	}
+	assert((dividend->flags & MPtimesafe) == 0);
+
+	// quick check
+	if(mpmagcmp(dividend, divisor) < 0){
+		if(remainder != nil)
+			mpassign(dividend, remainder);
+		if(quotient != nil)
+			mpassign(mpzero, quotient);
+		return;
+	}
+	
+	qsign = divisor->sign * dividend->sign;
+	rsign = dividend->sign;
+
+	// D1: shift until divisor, v, has hi bit set (needed to make trial
+	//     divisor accurate)
+	qd = divisor->p[divisor->top-1];
+	for(s = 0; (qd & mpdighi) == 0; s++)
+		qd <<= 1;
+	u = mpnew((dividend->top+2)*Dbits + s);
+	if(s == 0 && divisor != quotient && divisor != remainder) {
+		mpassign(dividend, u);
+		v = divisor;
+	} else {
+		mpleft(dividend, s, u);
+		v = mpnew(divisor->top*Dbits);
+		mpleft(divisor, s, v);
+	}
+	up = u->p+u->top-1;
+	vp = v->p+v->top-1;
+	vn = v->top;
+
+	// D1a: make sure high digit of dividend is less than high digit of divisor
+	if(*up >= *vp){
+		*++up = 0;
+		u->top++;
+	}
+
+	// storage for multiplies
+	t = mpnew(4*Dbits);
+
+	qp = nil;
+	if(quotient != nil){
+		mpbits(quotient, (u->top - v->top)*Dbits);
+		quotient->top = u->top - v->top;
+		qp = quotient->p+quotient->top-1;
+	}
+
+	// D2, D7: loop on length of dividend
+	for(j = u->top; j > vn; j--){
+
+		// D3: calculate trial divisor
+		mpdigdiv(up-1, *vp, &qd);
+
+		// D3a: rule out trial divisors 2 greater than real divisor
+		if(vn > 1) for(;;){
+			memset(t->p, 0, 3*Dbytes);	// mpvecdigmuladd adds to what's there
+			mpvecdigmuladd(vp-1, 2, qd, t->p);
+			if(mpveccmp(t->p, 3, up-2, 3) > 0)
+				qd--;
+			else
+				break;
+		}
+
+		// D4: u -= v*qd << j*Dbits
+		sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
+		if(sign < 0){
+
+			// D6: trial divisor was too high, add back borrowed
+			//     value and decrease divisor
+			mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
+			qd--;
+		}
+
+		// D5: save quotient digit
+		if(qp != nil)
+			*qp-- = qd;
+
+		// push top of u down one
+		u->top--;
+		*up-- = 0;
+	}
+	if(qp != nil){
+		assert((quotient->flags & MPtimesafe) == 0);
+		mpnorm(quotient);
+		if(quotient->top != 0)
+			quotient->sign = qsign;
+	}
+
+	if(remainder != nil){
+		assert((remainder->flags & MPtimesafe) == 0);
+		mpright(u, s, remainder);	// u is the remainder shifted
+		if(remainder->top != 0)
+			remainder->sign = rsign;
+	}
+
+	mpfree(t);
+	mpfree(u);
+	if(v != divisor)
+		mpfree(v);
+}
--- /dev/null
+++ b/llt/mpfmt.c
@@ -1,0 +1,207 @@
+#include "platform.h"
+
+static int
+toencx(mpint *b, char *buf, int len, int (*enc)(char*, int, uchar*, int))
+{
+	uchar *p;
+	int n, rv;
+
+	p = nil;
+	n = mptobe(b, nil, 0, &p);
+	if(n < 0)
+		return -1;
+	rv = (*enc)(buf, len, p, n);
+	free(p);
+	return rv;
+}
+
+static int
+topow2(mpint *b, char *buf, int len, int s)
+{
+	mpdigit *p, x;
+	int i, j, sn;
+	char *out, *eout;
+
+	if(len < 1)
+		return -1;
+
+	sn = 1<<s;
+	out = buf;
+	eout = buf+len;
+	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;
+			if(j != 0 || out != buf){
+				if(out >= eout)
+					return -1;
+				*out++ = enc16chr(j);
+			}
+		}
+	}
+	if(out == buf)
+		*out++ = '0';
+	if(out >= eout)
+		return -1;
+	*out = 0;
+	return 0;
+}
+
+static char*
+modbillion(int rem, ulong r, char *out, char *buf)
+{
+	ulong rr;
+	int i;
+
+	for(i = 0; i < 9; i++){
+		rr = r%10;
+		r /= 10;
+		if(out <= buf)
+			return nil;
+		*--out = '0' + rr;
+		if(rem == 0 && r == 0)
+			break;
+	}
+	return out;
+}
+
+static int
+to10(mpint *b, char *buf, int len)
+{
+	mpint *d, *r, *billion;
+	char *out;
+
+	if(len < 1)
+		return -1;
+
+	d = mpcopy(b);
+	d->flags &= ~MPtimesafe;
+	mpnorm(d);
+	r = mpnew(0);
+	billion = uitomp(1000000000, nil);
+	out = buf+len;
+	*--out = 0;
+	do {
+		mpdiv(d, billion, d, r);
+		out = modbillion(d->top, r->p[0], out, buf);
+		if(out == nil)
+			break;
+	} while(d->top != 0);
+	mpfree(d);
+	mpfree(r);
+	mpfree(billion);
+
+	if(out == nil)
+		return -1;
+	len -= out-buf;
+	if(out != buf)
+		memmove(buf, out, len);
+	return 0;
+}
+
+static int
+to8(mpint *b, char *buf, int len)
+{
+	mpdigit x, y;
+	char *out;
+	int i, j;
+
+	if(len < 2)
+		return -1;
+
+	out = buf+len;
+	*--out = 0;
+
+	i = j = 0;
+	x = y = 0;
+	while(j < b->top){
+		y = b->p[j++];
+		if(i > 0)
+			x |= y << i;
+		else
+			x = y;
+		i += Dbits;
+		while(i >= 3){
+Digout:			i -= 3;
+			if(out > buf)
+				out--;
+			else if(x != 0)
+				return -1;
+			*out = '0' + (x & 7);
+			x = y >> Dbits-i;
+		}
+	}
+	if(i > 0)
+		goto Digout;
+
+	while(*out == '0') out++;
+	if(*out == '\0')
+		*--out = '0';
+
+	len -= out-buf;
+	if(out != buf)
+		memmove(buf, out, len);
+	return 0;
+}
+
+char*
+mptoa(mpint *b, int base, char *buf, int len)
+{
+	char *out;
+	int rv, alloced;
+
+	if(base == 0)
+		base = 16;	/* default */
+	alloced = 0;
+	if(buf == nil){
+		/* rv <= log₂(base) */
+		for(rv=1; (base >> rv) > 1; rv++)
+			;
+		len = 10 + (b->top*Dbits / rv);
+		buf = malloc(len);
+		if(buf == nil)
+			return nil;
+		alloced = 1;
+	}
+
+	if(len < 2)
+		return nil;
+
+	out = buf;
+	if(b->sign < 0){
+		*out++ = '-';
+		len--;
+	}
+	switch(base){
+	case 64:
+		rv = toencx(b, out, len, enc64);
+		break;
+	case 32:
+		rv = toencx(b, out, len, enc32);
+		break;
+	case 16:
+		rv = topow2(b, out, len, 4);
+		break;
+	case 10:
+		rv = to10(b, out, len);
+		break;
+	case 8:
+		rv = to8(b, out, len);
+		break;
+	case 4:
+		rv = topow2(b, out, len, 2);
+		break;
+	case 2:
+		rv = topow2(b, out, len, 1);
+		break;
+	default:
+		abort();
+		return nil;
+	}
+	if(rv < 0){
+		if(alloced)
+			free(buf);
+		return nil;
+	}
+	return buf;
+}
--- /dev/null
+++ b/llt/mpleft.c
@@ -1,0 +1,49 @@
+#include "platform.h"
+
+// res = b << shift
+void
+mpleft(mpint *b, int shift, mpint *res)
+{
+	int d, l, r, i, otop;
+	mpdigit this, last;
+
+	res->sign = b->sign;
+	if(b->top==0){
+		res->top = 0;
+		return;
+	}
+
+	// a zero or negative left shift is a right shift
+	if(shift <= 0){
+		mpright(b, -shift, res);
+		return;
+	}
+
+	// b and res may be the same so remember the old top
+	otop = b->top;
+
+	// shift
+	mpbits(res, otop*Dbits + shift);	// overkill
+	res->top = DIGITS(otop*Dbits + shift);
+	d = shift/Dbits;
+	l = shift - d*Dbits;
+	r = Dbits - l;
+
+	if(l == 0){
+		for(i = otop-1; i >= 0; i--)
+			res->p[i+d] = b->p[i];
+	} else {
+		last = 0;
+		for(i = otop-1; i >= 0; i--) {
+			this = b->p[i];
+			res->p[i+d+1] = (last<<l) | (this>>r);
+			last = this;
+		}
+		res->p[d] = last<<l;
+	}
+	for(i = 0; i < d; i++)
+		res->p[i] = 0;
+
+	res->flags |= b->flags & MPtimesafe;
+	mpnorm(res);
+}
--- /dev/null
+++ b/llt/mpmul.c
@@ -1,0 +1,174 @@
+#include "platform.h"
+
+//
+//  from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
+//
+//  mpvecmul is an assembly language routine that performs the inner
+//  loop.
+//
+//  the karatsuba trade off is set empiricly by measuring the algs on
+//  a 400 MHz Pentium II.
+//
+
+// karatsuba like (see knuth pg 258)
+// prereq: p is already zeroed
+static void
+mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
+	int u0len, u1len, v0len, v1len, reslen;
+	int sign, n;
+
+	// divide each piece in half
+	n = alen/2;
+	if(alen&1)
+		n++;
+	u0len = n;
+	u1len = alen-n;
+	if(blen > n){
+		v0len = n;
+		v1len = blen-n;
+	} else {
+		v0len = blen;
+		v1len = 0;
+	}
+	u0 = a;
+	u1 = a + u0len;
+	v0 = b;
+	v1 = b + v0len;
+
+	// room for the partial products
+	t = calloc(1, Dbytes*5*(2*n+1));
+	if(t == nil)
+		sysfatal("mpkaratsuba: %r");
+	u0v0 = t;
+	u1v1 = t + (2*n+1);
+	diffprod = t + 2*(2*n+1);
+	res = t + 3*(2*n+1);
+	reslen = 4*n+1;
+
+	// t[0] = (u1-u0)
+	sign = 1;
+	if(mpveccmp(u1, u1len, u0, u0len) < 0){
+		sign = -1;
+		mpvecsub(u0, u0len, u1, u1len, u0v0);
+	} else
+		mpvecsub(u1, u1len, u0, u1len, u0v0);
+
+	// t[1] = (v0-v1)
+	if(mpveccmp(v0, v0len, v1, v1len) < 0){
+		sign *= -1;
+		mpvecsub(v1, v1len, v0, v1len, u1v1);
+	} else
+		mpvecsub(v0, v0len, v1, v1len, u1v1);
+
+	// t[4:5] = (u1-u0)*(v0-v1)
+	mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
+
+	// t[0:1] = u1*v1
+	memset(t, 0, 2*(2*n+1)*Dbytes);
+	if(v1len > 0)
+		mpvecmul(u1, u1len, v1, v1len, u1v1);
+
+	// t[2:3] = u0v0
+	mpvecmul(u0, u0len, v0, v0len, u0v0);
+
+	// res = u0*v0<<n + u0*v0
+	mpvecadd(res, reslen, u0v0, u0len+v0len, res);
+	mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
+
+	// res += u1*v1<<n + u1*v1<<2*n
+	if(v1len > 0){
+		mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
+		mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
+	}
+
+	// res += (u1-u0)*(v0-v1)<<n
+	if(sign < 0)
+		mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+	else
+		mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+	memmove(p, res, (alen+blen)*Dbytes);
+
+	free(t);
+}
+
+#define KARATSUBAMIN 32
+
+void
+mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	int i;
+	mpdigit d;
+	mpdigit *t;
+
+	// both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
+	if(alen < blen){
+		i = alen;
+		alen = blen;
+		blen = i;
+		t = a;
+		a = b;
+		b = t;
+	}
+
+	if(alen >= KARATSUBAMIN && blen > 1){
+		// O(n^1.585)
+		mpkaratsuba(a, alen, b, blen, p);
+	} else {
+		// O(n^2)
+		for(i = 0; i < blen; i++){
+			d = b[i];
+			if(d != 0)
+				mpvecdigmuladd(a, alen, d, &p[i]);
+		}
+	}
+}
+
+void
+mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	int i;
+	mpdigit *t;
+
+	if(alen < blen){
+		i = alen;
+		alen = blen;
+		blen = i;
+		t = a;
+		a = b;
+		b = t;
+	}
+	if(blen == 0)
+		return;
+	for(i = 0; i < blen; i++)
+		mpvecdigmuladd(a, alen, b[i], &p[i]);
+}
+
+void
+mpmul(mpint *b1, mpint *b2, mpint *prod)
+{
+	mpint *oprod;
+
+	oprod = prod;
+	if(prod == b1 || prod == b2){
+		prod = mpnew(0);
+		prod->flags = oprod->flags;
+	}
+	prod->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
+	prod->top = 0;
+	mpbits(prod, (b1->top+b2->top+1)*Dbits);
+	if(prod->flags & MPtimesafe)
+		mpvectsmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+	else
+		mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+	prod->top = b1->top+b2->top+1;
+	prod->sign = b1->sign*b2->sign;
+	mpnorm(prod);
+
+	if(oprod != prod){
+		mpassign(prod, oprod);
+		mpfree(prod);
+	}
+}
--- /dev/null
+++ b/llt/mpright.c
@@ -1,0 +1,55 @@
+#include "platform.h"
+
+// res = b >> shift
+void
+mpright(mpint *b, int shift, mpint *res)
+{
+	int d, l, r, i;
+	mpdigit this, last;
+
+	res->sign = b->sign;
+	if(b->top==0){
+		res->top = 0;
+		return;
+	}
+
+	// a negative right shift is a left shift
+	if(shift < 0){
+		mpleft(b, -shift, res);
+		return;
+	}
+
+	if(res != b)
+		mpbits(res, b->top*Dbits - shift);
+	else if(shift == 0)
+		return;
+
+	d = shift/Dbits;
+	r = shift - d*Dbits;
+	l = Dbits - r;
+
+	//  shift all the bits out == zero
+	if(d>=b->top){
+		res->sign = 1;
+		res->top = 0;
+		return;
+	}
+
+	// special case digit shifts
+	if(r == 0){
+		for(i = 0; i < b->top-d; i++)
+			res->p[i] = b->p[i+d];
+	} else {
+		last = b->p[d];
+		for(i = 0; i < b->top-d-1; i++){
+			this = b->p[i+d+1];
+			res->p[i] = (this<<l) | (last>>r);
+			last = this;
+		}
+		res->p[i++] = last>>r;
+	}
+
+	res->top = i;
+	res->flags |= b->flags & MPtimesafe;
+	mpnorm(res);
+}
--- /dev/null
+++ b/llt/mpsub.c
@@ -1,0 +1,54 @@
+#include "platform.h"
+
+// diff = abs(b1) - abs(b2), i.e., subtract the magnitudes
+void
+mpmagsub(mpint *b1, mpint *b2, mpint *diff)
+{
+	int n, m, sign;
+	mpint *t;
+
+	// get the sizes right
+	if(mpmagcmp(b1, b2) < 0){
+		assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
+		sign = -1;
+		t = b1;
+		b1 = b2;
+		b2 = t;
+	} else {
+		diff->flags |= (b1->flags | b2->flags) & MPtimesafe;
+		sign = 1;
+	}
+	n = b1->top;
+	m = b2->top;
+	if(m == 0){
+		mpassign(b1, diff);
+		diff->sign = sign;
+		return;
+	}
+	mpbits(diff, n*Dbits);
+
+	mpvecsub(b1->p, n, b2->p, m, diff->p);
+	diff->sign = sign;
+	diff->top = n;
+	mpnorm(diff);
+}
+
+// diff = b1 - b2
+void
+mpsub(mpint *b1, mpint *b2, mpint *diff)
+{
+	int sign;
+
+	if(b1->sign != b2->sign){
+		assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
+		sign = b1->sign;
+		mpmagadd(b1, b2, diff);
+		diff->sign = sign;
+		return;
+	}
+
+	sign = b1->sign;
+	mpmagsub(b1, b2, diff);
+	if(diff->top != 0)
+		diff->sign *= sign;
+}
--- /dev/null
+++ b/llt/mptobe.c
@@ -1,0 +1,29 @@
+#include "platform.h"
+
+// convert an mpint into a big endian byte array (most significant byte first; left adjusted)
+//   return number of bytes converted
+//   if p == nil, allocate and result array
+int
+mptobe(mpint *b, uchar *p, uint n, uchar **pp)
+{
+	uint m;
+
+	m = (mpsignif(b)+7)/8;
+	if(m == 0)
+		m++;
+	if(p == nil){
+		n = m;
+		p = malloc(n);
+		if(p == nil)
+			sysfatal("mptobe: %r");
+	} else {
+		if(n < m)
+			return -1;
+		if(n > m)
+			memset(p+m, 0, n-m);
+	}
+	if(pp != nil)
+		*pp = p;
+	mptober(b, p, m);
+	return m;
+}
--- /dev/null
+++ b/llt/mptober.c
@@ -1,0 +1,32 @@
+#include "platform.h"
+
+void
+mptober(mpint *b, uchar *p, int n)
+{
+	int i, j, m;
+	mpdigit x;
+
+	memset(p, 0, n);
+
+	p += n;
+	m = b->top*Dbytes;
+	if(m < n)
+		n = m;
+
+	i = 0;
+	while(n >= Dbytes){
+		n -= Dbytes;
+		x = b->p[i++];
+		for(j = 0; j < Dbytes; j++){
+			*--p = x;
+			x >>= 8;
+		}
+	}
+	if(n > 0){
+		x = b->p[i];
+		for(j = 0; j < n; j++){
+			*--p = x;
+			x >>= 8;
+		}
+	}
+}
--- /dev/null
+++ b/llt/mptod.c
@@ -1,0 +1,83 @@
+#include "platform.h"
+
+extern double D_PINF, D_NINF;
+
+double
+mptod(mpint *a)
+{
+	u64int v;
+	mpdigit w, r;
+	int sf, i, n, m, s;
+	FPdbleword x;
+	
+	if(a->top == 0) return 0.0;
+	sf = mpsignif(a);
+	if(sf > 1024) return a->sign < 0 ? D_NINF : D_PINF;
+	i = a->top - 1;
+	v = a->p[i];
+	n = sf & Dbits - 1;
+	n |= n - 1 & Dbits;
+	r = 0;
+	if(n > 54){
+		s = n - 54;
+		r = v & (1<<s) - 1;
+		v >>= s;
+	}
+	while(n < 54){
+		if(--i < 0)
+			w = 0;
+		else
+			w = a->p[i];
+		m = 54 - n;
+		if(m > Dbits) m = Dbits;
+		s = Dbits - m & Dbits - 1;
+		v = v << m | w >> s;
+		r = w & (1<<s) - 1;
+		n += m;
+	}
+	if((v & 3) == 1){
+		while(--i >= 0)
+			r |= a->p[i];
+		if(r != 0)
+			v++;
+	}else
+		v++;
+	v >>= 1;
+	while((v >> 53) != 0){
+		v >>= 1;
+		if(++sf > 1024)
+			return a->sign < 0 ? D_NINF : D_PINF;
+	}
+	x.lo = v;
+	x.hi = (u32int)(v >> 32) & (1<<20) - 1 | sf + 1022 << 20 | a->sign & 1<<31;
+	return x.x;
+}
+
+mpint *
+dtomp(double d, mpint *a)
+{
+	FPdbleword x;
+	uvlong v;
+	int e;
+
+	if(a == nil)
+		a = mpnew(0);
+	x.x = d;
+	e = x.hi >> 20 & 2047;
+	assert(e != 2047);
+	if(e < 1022){
+		mpassign(mpzero, a);
+		return a;
+	}
+	v = x.lo | (uvlong)(x.hi & (1<<20) - 1) << 32 | 1ULL<<52;
+	if(e < 1075){
+		v += (1ULL<<1074 - e) - (~v >> 1075 - e & 1);
+		v >>= 1075 - e;
+	}
+	uvtomp(v, a);
+	if(e > 1075)
+		mpleft(a, e - 1075, a);
+	if((int)x.hi < 0)
+		a->sign = -1;
+	return a;
+}
--- /dev/null
+++ b/llt/mptoi.c
@@ -1,0 +1,41 @@
+#include "platform.h"
+
+/*
+ *  this code assumes that mpdigit is at least as
+ *  big as an int.
+ */
+
+mpint*
+itomp(int i, mpint *b)
+{
+	if(b == nil){
+		b = mpnew(0);
+	}
+	b->sign = (i >> (sizeof(i)*8 - 1)) | 1;
+	i *= b->sign;
+	*b->p = i;
+	b->top = 1;
+	return mpnorm(b);
+}
+
+int
+mptoi(mpint *b)
+{
+	uint x;
+
+	if(b->top==0)
+		return 0;
+	x = *b->p;
+	if(b->sign > 0){
+		if(b->top > 1 || (x > MAXINT))
+			x = (int)MAXINT;
+		else
+			x = (int)x;
+	} else {
+		if(b->top > 1 || x > MAXINT+1)
+			x = (int)MININT;
+		else
+			x = -(int)x;
+	}
+	return x;
+}
--- /dev/null
+++ b/llt/mptoui.c
@@ -1,0 +1,31 @@
+#include "platform.h"
+
+/*
+ *  this code assumes that mpdigit is at least as
+ *  big as an int.
+ */
+
+mpint*
+uitomp(uint i, mpint *b)
+{
+	if(b == nil){
+		b = mpnew(0);
+	}
+	*b->p = i;
+	b->top = 1;
+	b->sign = 1;
+	return mpnorm(b);
+}
+
+uint
+mptoui(mpint *b)
+{
+	uint x;
+
+	x = *b->p;
+	if(b->sign < 0)
+		x = 0;
+	else if(b->top > 1 || (sizeof(mpdigit) > sizeof(uint) && x > MAXUINT))
+		x =  MAXUINT;
+	return x;
+}
--- /dev/null
+++ b/llt/mptouv.c
@@ -1,0 +1,44 @@
+#include "platform.h"
+
+#define VLDIGITS (int)(sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ *  this code assumes that a vlong is an integral number of
+ *  mpdigits long.
+ */
+mpint*
+uvtomp(uvlong v, mpint *b)
+{
+	int s;
+
+	if(b == nil){
+		b = mpnew(VLDIGITS*Dbits);
+	}else
+		mpbits(b, VLDIGITS*Dbits);
+	b->sign = 1;
+	for(s = 0; s < VLDIGITS; s++){
+		b->p[s] = v;
+		v >>= sizeof(mpdigit)*8;
+	}
+	b->top = s;
+	return mpnorm(b);
+}
+
+uvlong
+mptouv(mpint *b)
+{
+	uvlong v;
+	int s;
+
+	if(b->top == 0 || b->sign < 0)
+		return 0LL;
+
+	if(b->top > VLDIGITS)
+		return -1LL;
+
+	v = 0ULL;
+	for(s = 0; s < b->top; s++)
+		v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+	return v;
+}
--- /dev/null
+++ b/llt/mptov.c
@@ -1,0 +1,60 @@
+#include "platform.h"
+
+#define VLDIGITS (int)(sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ *  this code assumes that a vlong is an integral number of
+ *  mpdigits long.
+ */
+mpint*
+vtomp(vlong v, mpint *b)
+{
+	int s;
+	uvlong uv;
+
+	if(b == nil){
+		b = mpnew(VLDIGITS*Dbits);
+	}else
+		mpbits(b, VLDIGITS*Dbits);
+	b->sign = (v >> (sizeof(v)*8 - 1)) | 1;
+	uv = v * b->sign;
+	for(s = 0; s < VLDIGITS; s++){
+		b->p[s] = uv;
+		uv >>= sizeof(mpdigit)*8;
+	}
+	b->top = s;
+	return mpnorm(b);
+}
+
+vlong
+mptov(mpint *b)
+{
+	uvlong v;
+	int s;
+
+	if(b->top == 0)
+		return 0LL;
+
+	if(b->top > VLDIGITS){
+		if(b->sign > 0)
+			return (vlong)MAXVLONG;
+		else
+			return (vlong)MINVLONG;
+	}
+
+	v = 0ULL;
+	for(s = 0; s < b->top; s++)
+		v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+	if(b->sign > 0){
+		if(v > MAXVLONG)
+			v = MAXVLONG;
+	} else {
+		if(v > MINVLONG)
+			v = MINVLONG;
+		else
+			v = -(vlong)v;
+	}
+
+	return (vlong)v;
+}
--- /dev/null
+++ b/llt/mpvecadd.c
@@ -1,0 +1,34 @@
+#include "platform.h"
+
+// prereq: alen >= blen, sum has at least blen+1 digits
+void
+mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum)
+{
+	int i;
+	uint carry;
+	mpdigit x, y;
+
+	carry = 0;
+	for(i = 0; i < blen; i++){
+		x = *a++;
+		y = *b++;
+		x += carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		x += y;
+		if(x < y)
+			carry++;
+		*sum++ = x;
+	}
+	for(; i < alen; i++){
+		x = *a++ + carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		*sum++ = x;
+	}
+	*sum = carry;
+}
--- /dev/null
+++ b/llt/mpveccmp.c
@@ -1,0 +1,25 @@
+#include "platform.h"
+
+int
+mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{
+	mpdigit x;
+
+	while(alen > blen)
+		if(a[--alen] != 0)
+			return 1;
+	while(blen > alen)
+		if(b[--blen] != 0)
+			return -1;
+	while(alen > 0){
+		--alen;
+		x = a[alen] - b[alen];
+		if(x == 0)
+			continue;
+		if(x > a[alen])
+			return -1;
+		else
+			return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/llt/mpvecdigmuladd.c
@@ -1,0 +1,101 @@
+#include "platform.h"
+
+#define LO(x) ((x) & ((1<<(Dbits/2))-1))
+#define HI(x) ((x) >> (Dbits/2))
+
+static void
+mpdigmul(mpdigit a, mpdigit b, mpdigit *p)
+{
+	mpdigit x, ah, al, bh, bl, p1, p2, p3, p4;
+	int carry;
+
+	// half digits
+	ah = HI(a);
+	al = LO(a);
+	bh = HI(b);
+	bl = LO(b);
+
+	// partial products
+	p1 = ah*bl;
+	p2 = bh*al;
+	p3 = bl*al;
+	p4 = ah*bh;
+
+	// p = ((p1+p2)<<(Dbits/2)) + (p4<<Dbits) + p3
+	carry = 0;
+	x = p1<<(Dbits/2);
+	p3 += x;
+	if(p3 < x)
+		carry++;
+	x = p2<<(Dbits/2);
+	p3 += x;
+	if(p3 < x)
+		carry++;
+	p4 += carry + HI(p1) + HI(p2);	// can't carry out of the high digit
+	p[0] = p3;
+	p[1] = p4;
+}
+
+// prereq: p must have room for n+1 digits
+void
+mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{
+	int i;
+	mpdigit carry, x, y, part[2];
+
+	carry = 0;
+	part[1] = 0;
+	for(i = 0; i < n; i++){
+		x = part[1] + carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		y = *p;
+		mpdigmul(*b++, m, part);
+		x += part[0];
+		if(x < part[0])
+			carry++;
+		x += y;
+		if(x < y)
+			carry++;
+		*p++ = x;
+	}
+	*p = part[1] + carry;
+}
+
+// prereq: p must have room for n+1 digits
+int
+mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{
+	int i;
+	mpdigit x, y, part[2], borrow;
+
+	borrow = 0;
+	part[1] = 0;
+	for(i = 0; i < n; i++){
+		x = *p;
+		y = x - borrow;
+		if(y > x)
+			borrow = 1;
+		else
+			borrow = 0;
+		x = part[1];
+		mpdigmul(*b++, m, part);
+		x += part[0];
+		if(x < part[0])
+			borrow++;
+		x = y - x;
+		if(x > y)
+			borrow++;
+		*p++ = x;
+	}
+
+	x = *p;
+	y = x - borrow - part[1];
+	*p = y;
+	if(y > x)
+		return -1;
+	else
+		return 1;
+}
--- /dev/null
+++ b/llt/mpvecsub.c
@@ -1,0 +1,32 @@
+#include "platform.h"
+
+// prereq: a >= b, alen >= blen, diff has at least alen digits
+void
+mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff)
+{
+	int i, borrow;
+	mpdigit x, y;
+
+	borrow = 0;
+	for(i = 0; i < blen; i++){
+		x = *a++;
+		y = *b++;
+		y += borrow;
+		if(y < (mpdigit)borrow)
+			borrow = 1;
+		else
+			borrow = 0;
+		if(x < y)
+			borrow++;
+		*diff++ = x - y;
+	}
+	for(; i < alen; i++){
+		x = *a++;
+		y = x - borrow;
+		if(y > x)
+			borrow = 1;
+		else
+			borrow = 0;
+		*diff++ = y;
+	}
+}
--- /dev/null
+++ b/llt/mpvectscmp.c
@@ -1,0 +1,32 @@
+#include "platform.h"
+
+int
+mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{
+	mpdigit x, y, z, v;
+	int m, p;
+
+	if(alen > blen){
+		v = 0;
+		while(alen > blen)
+			v |= a[--alen];
+		m = p = (-v^v|v)>>Dbits-1;
+	} else if(blen > alen){
+		v = 0;
+		while(blen > alen)
+			v |= b[--blen];
+		m = (-v^v|v)>>Dbits-1;
+		p = m^1;
+	} else
+		m = p = 0;
+	while(alen-- > 0){
+		x = a[alen];
+		y = b[alen];
+		z = x - y;
+		x = ~x;
+		v = ((-z^z|z)>>Dbits-1) & ~m;
+		p = ((~(x&y|x&z|y&z)>>Dbits-1) & v) | (p & ~v);
+		m |= v;
+	}
+	return (p-m) | m;
+}
--- /dev/null
+++ b/llt/strtomp.c
@@ -1,0 +1,174 @@
+#include "platform.h"
+
+static char*
+frompow2(char *a, mpint *b, int s)
+{
+	char *p, *next;
+	mpdigit x;
+	int i;
+
+	i = 1<<s;
+	for(p = a; (dec16chr(*p) & 255) < i; p++)
+		;
+
+	mpbits(b, (p-a)*s);
+	b->top = 0;
+	next = p;
+
+	while(p > a){
+		x = 0;
+		for(i = 0; i < Dbits; i += s){
+			if(p <= a)
+				break;
+			x |= dec16chr(*--p)<<i;
+		}
+		b->p[b->top++] = x;
+	}
+	return next;
+}
+
+static char*
+from8(char *a, mpint *b)
+{
+	char *p, *next;
+	mpdigit x, y;
+	int i;
+
+	for(p = a; ((*p - '0') & 255) < 8; p++)
+		;
+
+	mpbits(b, (p-a)*3);
+	b->top = 0;
+	next = p;
+
+	i = 0;
+	x = y = 0;
+	while(p > a){
+		y = *--p - '0';
+		x |= y << i;
+		i += 3;
+		if(i >= Dbits){
+Digout:
+			i -= Dbits;
+			b->p[b->top++] = x;
+			x = y >> 3-i;
+		}
+	}
+	if(i > 0)
+		goto Digout;
+
+	return next;
+}
+
+static ulong mppow10[] = {
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
+};
+
+static char*
+from10(char *a, mpint *b)
+{
+	ulong x, y;
+	mpint *pow, *r;
+	int i;
+
+	pow = mpnew(0);
+	r = mpnew(0);
+
+	b->top = 0;
+	for(;;){
+		// do a billion at a time in native arithmetic
+		x = 0;
+		for(i = 0; i < 9; i++){
+			y = *a - '0';
+			if(y > 9)
+				break;
+			a++;
+			x *= 10;
+			x += y;
+		}
+		if(i == 0)
+			break;
+
+		// accumulate into mpint
+		uitomp(mppow10[i], pow);
+		uitomp(x, r);
+		mpmul(b, pow, b);
+		mpadd(b, r, b);
+		if(i < 9)
+			break;
+	}
+	mpfree(pow);
+	mpfree(r);
+	return a;
+}
+
+mpint*
+strtomp(char *a, char **pp, int base, mpint *b)
+{
+	int sign;
+	char *e;
+
+	if(b == nil){
+		b = mpnew(0);
+	}
+
+	while(*a==' ' || *a=='\t')
+		a++;
+
+	sign = 1;
+	for(;; a++){
+		switch(*a){
+		case '-':
+			sign *= -1;
+			continue;
+		}
+		break;
+	}
+
+	if(base == 0){
+		base = 10;
+		if(a[0] == '0'){
+			if(a[1] == 'x' || a[1] == 'X') {
+				a += 2;
+				base = 16;
+			} else if(a[1] == 'b' || a[1] == 'B') {
+				a += 2;
+				base = 2;
+			} else if(a[1] >= '0' && a[1] <= '7') {
+				a++;
+				base = 8;
+			}
+		}
+	}
+
+	switch(base){
+	case 2:
+		e = frompow2(a, b, 1);
+		break;
+	case 4:
+		e = frompow2(a, b, 2);
+		break;
+	case 8:
+		e = from8(a, b);
+		break;
+	case 10:
+		e = from10(a, b);
+		break;
+	case 16:
+		e = frompow2(a, b, 4);
+		break;
+	default:
+		abort();
+		return nil;
+	}
+
+	if(pp != nil)
+		*pp = e;
+
+	// if no characters parsed, there wasn't a number to convert
+	if(e == a)
+		return nil;
+
+	b->sign = sign;
+	return mpnorm(b);
+}
--- /dev/null
+++ b/llt/u16.c
@@ -1,0 +1,68 @@
+#include "platform.h"
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc16chr(int o)
+{
+	int c;
+
+	c  = between(o,  0,  9) & ('0'+o);
+	c |= between(o, 10, 15) & ('A'+(o-10));
+	return c;
+}
+
+int
+dec16chr(int c)
+{
+	int o;
+
+	o  = between(c, '0', '9') & (1+(c-'0'));
+	o |= between(c, 'A', 'F') & (1+10+(c-'A'));
+	o |= between(c, 'a', 'f') & (1+10+(c-'a'));
+	return o-1;
+}
+
+int
+dec16(uchar *out, int lim, char *in, int n)
+{
+	int c, w = 0, i = 0;
+	uchar *start = out;
+	uchar *eout = out + lim;
+
+	while(n-- > 0){
+		c = dec16chr(*in++);
+		if(c < 0)
+			continue;
+		w = (w<<4) + c;
+		i++;
+		if(i == 2){
+			if(out + 1 > eout)
+				goto exhausted;
+			*out++ = w;
+			w = 0;
+			i = 0;
+		}
+	}
+exhausted:
+	return out - start;
+}
+
+int
+enc16(char *out, int lim, uchar *in, int n)
+{
+	uint c;
+	char *eout = out + lim;
+	char *start = out;
+
+	while(n-- > 0){
+		c = *in++;
+		if(out + 2 >= eout)
+			goto exhausted;
+		*out++ = enc16chr(c>>4);
+		*out++ = enc16chr(c&15);
+	}
+exhausted:
+	*out = 0;
+	return out - start;
+}
--- /dev/null
+++ b/llt/u32.c
@@ -1,0 +1,143 @@
+#include "platform.h"
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc32chr(int o)
+{
+	int c;
+
+	c  = between(o,  0, 25) & ('A'+o);
+	c |= between(o, 26, 31) & ('2'+(o-26));
+	return c;
+}
+
+int
+dec32chr(int c)
+{
+	int o;
+
+	o  = between(c, 'A', 'Z') & (1+(c-'A'));
+	o |= between(c, 'a', 'z') & (1+(c-'a'));
+	o |= between(c, '2', '7') & (1+26+(c-'2'));
+	return o-1;
+}
+
+int
+dec32x(uchar *dest, int ndest, char *src, int nsrc, int (*chr)(int))
+{
+	uchar *start;
+	int i, j, u[8];
+
+	if(ndest+1 < (5*nsrc+7)/8)
+		return -1;
+	start = dest;
+	while(nsrc>=8){
+		for(i=0; i<8; i++){
+			j = chr(src[i]);
+			if(j < 0)
+				j = 0;
+			u[i] = j;
+		}
+		*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+		*dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+		*dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+		*dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+		*dest++ = ((0x7 & u[6])<<5) | u[7];
+		src  += 8;
+		nsrc -= 8;
+	}
+	if(nsrc > 0){
+		if(nsrc == 1 || nsrc == 3 || nsrc == 6)
+			return -1;
+		for(i=0; i<nsrc; i++){
+			j = chr(src[i]);
+			if(j < 0)
+				j = 0;
+			u[i] = j;
+		}
+		*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+		if(nsrc == 2)
+			goto out;
+		*dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+		if(nsrc == 4)
+			goto out;
+		*dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+		if(nsrc == 5)
+			goto out;
+		*dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+	}
+out:
+	return dest-start;
+}
+
+int
+enc32x(char *dest, int ndest, uchar *src, int nsrc, int (*chr)(int))
+{
+	char *start;
+	int j;
+
+	if(ndest <= (8*nsrc+4)/5)
+		return -1;
+	start = dest;
+	while(nsrc>=5){
+		j = (0x1f & (src[0]>>3));
+		*dest++ = chr(j);
+		j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
+		*dest++ = chr(j);
+		j = (0x1f & (src[1]>>1));
+		*dest++ = chr(j);
+		j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
+		*dest++ = chr(j);
+		j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
+		*dest++ = chr(j);
+		j = (0x1f & (src[3]>>2));
+		*dest++ = chr(j);
+		j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
+		*dest++ = chr(j);
+		j = (0x1f & (src[4]));
+		*dest++ = chr(j);
+		src  += 5;
+		nsrc -= 5;
+	}
+	if(nsrc){
+		j = (0x1f & (src[0]>>3));
+		*dest++ = chr(j);
+		j = (0x1c & (src[0]<<2));
+		if(nsrc == 1)
+			goto out;
+		j |= (0x03 & (src[1]>>6));
+		*dest++ = chr(j);
+		j = (0x1f & (src[1]>>1));
+		*dest++ = chr(j);
+		j = (0x10 & (src[1]<<4));
+		if(nsrc == 2)
+			goto out;
+		j |= (0x0f & (src[2]>>4));
+		*dest++ = chr(j);
+		j = (0x1e & (src[2]<<1));
+		if(nsrc == 3)
+			goto out;
+		j |= (0x01 & (src[3]>>7));
+		*dest++ = chr(j);
+		j = (0x1f & (src[3]>>2));
+		*dest++ = chr(j);
+		j = (0x18 & (src[3]<<3));
+out:
+		*dest++ = chr(j);
+	}
+	*dest = 0;
+	return dest-start;
+}
+
+int
+enc32(char *dest, int ndest, uchar *src, int nsrc)
+{
+	return enc32x(dest, ndest, src, nsrc, enc32chr);
+}
+
+int
+dec32(uchar *dest, int ndest, char *src, int nsrc)
+{
+	return dec32x(dest, ndest, src, nsrc, dec32chr);
+}
--- /dev/null
+++ b/llt/u64.c
@@ -1,0 +1,141 @@
+#include "platform.h"
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc64chr(int o)
+{
+	int c;
+
+	c  = between(o,  0, 25) & ('A'+o);
+	c |= between(o, 26, 51) & ('a'+(o-26));
+	c |= between(o, 52, 61) & ('0'+(o-52));
+	c |= between(o, 62, 62) & ('+');
+	c |= between(o, 63, 63) & ('/');
+	return c;
+}
+
+int
+dec64chr(int c)
+{
+	int o;
+
+	o  = between(c, 'A', 'Z') & (1+(c-'A'));
+	o |= between(c, 'a', 'z') & (1+26+(c-'a'));
+	o |= between(c, '0', '9') & (1+52+(c-'0'));
+	o |= between(c, '+', '+') & (1+62);
+	o |= between(c, '/', '/') & (1+63);
+	return o-1;
+}
+
+int
+dec64x(uchar *out, int lim, char *in, int n, int (*chr)(int))
+{
+	ulong b24;
+	uchar *start = out;
+	uchar *e = out + lim;
+	int i, c;
+
+	b24 = 0;
+	i = 0;
+	while(n-- > 0){
+		c = chr(*in++);
+		if(c < 0)
+			continue;
+		switch(i){
+		case 0:
+			b24 = c<<18;
+			break;
+		case 1:
+			b24 |= c<<12;
+			break;
+		case 2:
+			b24 |= c<<6;
+			break;
+		case 3:
+			if(out + 3 > e)
+				goto exhausted;
+
+			b24 |= c;
+			*out++ = b24>>16;
+			*out++ = b24>>8;
+			*out++ = b24;
+			i = 0;
+			continue;
+		}
+		i++;
+	}
+	switch(i){
+	case 2:
+		if(out + 1 > e)
+			goto exhausted;
+		*out++ = b24>>16;
+		break;
+	case 3:
+		if(out + 2 > e)
+			goto exhausted;
+		*out++ = b24>>16;
+		*out++ = b24>>8;
+		break;
+	}
+exhausted:
+	return out - start;
+}
+
+int
+enc64x(char *out, int lim, uchar *in, int n, int (*chr)(int))
+{
+	int i;
+	ulong b24;
+	char *start = out;
+	char *e = out + lim;
+
+	for(i = n/3; i > 0; i--){
+		b24 = *in++<<16;
+		b24 |= *in++<<8;
+		b24 |= *in++;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = chr(b24>>18);
+		*out++ = chr((b24>>12)&0x3f);
+		*out++ = chr((b24>>6)&0x3f);
+		*out++ = chr(b24&0x3f);
+	}
+
+	switch(n%3){
+	case 2:
+		b24 = *in++<<16;
+		b24 |= *in<<8;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = chr(b24>>18);
+		*out++ = chr((b24>>12)&0x3f);
+		*out++ = chr((b24>>6)&0x3f);
+		*out++ = '=';
+		break;
+	case 1:
+		b24 = *in<<16;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = chr(b24>>18);
+		*out++ = chr((b24>>12)&0x3f);
+		*out++ = '=';
+		*out++ = '=';
+		break;
+	}
+exhausted:
+	*out = 0;
+	return out - start;
+}
+
+int
+enc64(char *out, int lim, uchar *in, int n)
+{
+	return enc64x(out, lim, in, n, enc64chr);
+}
+
+int
+dec64(uchar *out, int lim, char *in, int n)
+{
+	return dec64x(out, lim, in, n, dec64chr);
+}
--- a/mkfile
+++ b/mkfile
@@ -6,10 +6,12 @@
 CLEANFILES=boot.h builtin_fns.h
 
 HFILES=\
-	cvalues.c\
-	equal.c\
 	equalhash.h\
 	flisp.h\
+
+CHFILES=\
+	cvalues.c\
+	equal.c\
 	operators.c\
 	print.c\
 	read.c\
@@ -41,7 +43,7 @@
 
 flmain.$O: boot.h
 
-flisp.$O: maxstack.inc opcodes.h builtin_fns.h
+flisp.$O: maxstack.inc opcodes.h builtin_fns.h $CHFILES
 
 bootstrap:V: $O.out
     ./$O.out gen.lsp && \
--- a/operators.c
+++ b/operators.c
@@ -1,5 +1,24 @@
 #include "llt.h"
 
+mpint *conv_to_mpint(void *data, numerictype_t tag)
+{
+    mpint *i = mpzero;
+    switch (tag) {
+    case T_INT8:   i = itomp(*(int8_t*)data, nil); break;
+    case T_UINT8:  i = uitomp(*(uint8_t*)data, nil); break;
+    case T_INT16:  i = itomp(*(int16_t*)data, nil); break;
+    case T_UINT16: i = uitomp(*(uint16_t*)data, nil); break;
+    case T_INT32:  i = itomp(*(int32_t*)data, nil); break;
+    case T_UINT32: i = uitomp(*(uint32_t*)data, nil); break;
+    case T_INT64:  i = vtomp(*(int64_t*)data, nil); break;
+    case T_UINT64: i = uvtomp(*(int64_t*)data, nil); break;
+    case T_MPINT:  i = mpcopy(*(mpint**)data); break;
+    case T_FLOAT:  i = dtomp(*(float*)data, nil); break;
+    case T_DOUBLE: i = dtomp(*(double*)data, nil); break;
+    }
+    return i;
+}
+
 double conv_to_double(void *data, numerictype_t tag)
 {
     double d=0;
@@ -16,6 +35,7 @@
             d = -d;
         break;
     case T_UINT64: d = (double)*(uint64_t*)data; break;
+    case T_MPINT:  d = mptod(*(mpint**)data); break;
     case T_FLOAT:  d = (double)*(float*)data; break;
     case T_DOUBLE: return *(double*)data;
     }
@@ -37,11 +57,13 @@
             *(int64_t*)dest = INT64_MAX;
         break;
     case T_UINT64: *(uint64_t*)dest = (int64_t)d; break;
+    case T_MPINT:  *(mpint**)dest = dtomp(d, nil); break;
     case T_FLOAT:  *(float*)dest = d; break;
     case T_DOUBLE: *(double*)dest = d; break;
     }
 }
 
+// FIXME sign with mpint
 #define CONV_TO_INTTYPE(name, ctype)                    \
 ctype conv_to_##name(void *data, numerictype_t tag)     \
 {                                                       \
@@ -54,6 +76,7 @@
     case T_UINT32: return *(uint32_t*)data;             \
     case T_INT64:  return *(int64_t*)data;              \
     case T_UINT64: return *(uint64_t*)data;             \
+    case T_MPINT:  return mptov(*(mpint**)data);        \
     case T_FLOAT:  return *(float*)data;                \
     case T_DOUBLE: return *(double*)data;               \
     }                                                   \
@@ -79,6 +102,7 @@
     case T_UINT32: return *(uint32_t*)data; break;
     case T_INT64:  return *(int64_t*)data; break;
     case T_UINT64: return *(uint64_t*)data; break;
+    case T_MPINT:  return mptouv(*(mpint**)data); break;
     case T_FLOAT:
         if (*(float*)data >= 0)
             return *(float*)data;
@@ -104,6 +128,7 @@
     case T_UINT32: return *(uint32_t*)a < *(uint32_t*)b;
     case T_INT64:  return *(int64_t*)a < *(int64_t*)b;
     case T_UINT64: return *(uint64_t*)a < *(uint64_t*)b;
+    case T_MPINT:  return mpcmp(*(mpint**)a, *(mpint**)b) < 0;
     case T_FLOAT:  return *(float*)a < *(float*)b;
     case T_DOUBLE: return *(double*)a < *(double*)b;
     }
@@ -121,6 +146,7 @@
     case T_UINT32: return *(uint32_t*)a == *(uint32_t*)b;
     case T_INT64:  return *(int64_t*)a == *(int64_t*)b;
     case T_UINT64: return *(uint64_t*)a == *(uint64_t*)b;
+    case T_MPINT:  return mpcmp(*(mpint**)a, *(mpint**)b) == 0;
     case T_FLOAT:  return *(float*)a == *(float*)b;
     case T_DOUBLE: return *(double*)a == *(double*)b;
     }
@@ -127,6 +153,9 @@
     return 0;
 }
 
+/* FIXME one is allocated for all compare ops */
+static mpint *cmpmpint;
+
 int cmp_lt(void *a, numerictype_t atag, void *b, numerictype_t btag)
 {
     if (atag==btag)
@@ -142,6 +171,9 @@
     if (db < da)
         return 0;
 
+    if (cmpmpint == nil && (atag == T_MPINT || btag == T_MPINT))
+        cmpmpint = mpnew(0);
+
     if (atag == T_UINT64) {
         if (btag == T_INT64) {
             if (*(int64_t*)b >= 0)
@@ -152,6 +184,9 @@
             if (db != db) return 0;
             return (*(uint64_t*)a < (uint64_t)*(double*)b);
         }
+        else if (btag == T_MPINT) {
+            return mpcmp(uvtomp(*(uint64_t*)a, cmpmpint), *(mpint**)b) < 0;
+        }
     }
     else if (atag == T_INT64) {
         if (btag == T_UINT64) {
@@ -163,6 +198,9 @@
             if (db != db) return 0;
             return (*(int64_t*)a < (int64_t)*(double*)b);
         }
+        else if (btag == T_MPINT) {
+            return mpcmp(vtomp(*(int64_t*)a, cmpmpint), *(mpint**)b) < 0;
+        }
     }
     if (btag == T_UINT64) {
         if (atag == T_DOUBLE) {
@@ -169,6 +207,9 @@
             if (da != da) return 0;
             return (*(uint64_t*)b > (uint64_t)*(double*)a);
         }
+        else if (atag == T_MPINT) {
+            return mpcmp(*(mpint**)a, uvtomp(*(uint64_t*)b, cmpmpint)) < 0;
+        }
     }
     else if (btag == T_INT64) {
         if (atag == T_DOUBLE) {
@@ -175,6 +216,9 @@
             if (da != da) return 0;
             return (*(int64_t*)b > (int64_t)*(double*)a);
         }
+        else if (atag == T_MPINT) {
+            return mpcmp(*(mpint**)a, vtomp(*(int64_t*)b, cmpmpint)) < 0;
+        }
     }
     return 0;
 }
@@ -200,6 +244,9 @@
     if (da != db)
         return 0;
 
+    if (cmpmpint == nil && (atag == T_MPINT || btag == T_MPINT))
+        cmpmpint = mpnew(0);
+
     if (atag == T_UINT64) {
         // this is safe because if a had been bigger than INT64_MAX,
         // we would already have concluded that it's bigger than b.
@@ -207,6 +254,8 @@
             return ((int64_t)*(uint64_t*)a == *(int64_t*)b);
         else if (btag == T_DOUBLE)
             return (*(uint64_t*)a == (uint64_t)(int64_t)*(double*)b);
+        else if (btag == T_MPINT)
+            return mpcmp(uvtomp(*(uint64_t*)a, cmpmpint), *(mpint**)b) == 0;
     }
     else if (atag == T_INT64) {
         if (btag == T_UINT64)
@@ -213,6 +262,8 @@
             return (*(int64_t*)a == (int64_t)*(uint64_t*)b);
         else if (btag == T_DOUBLE)
             return (*(int64_t*)a == (int64_t)*(double*)b);
+        else if (btag == T_MPINT)
+            return mpcmp(vtomp(*(int64_t*)a, cmpmpint), *(mpint**)b) == 0;
     }
     else if (btag == T_UINT64) {
         if (atag == T_INT64)
@@ -219,6 +270,8 @@
             return ((int64_t)*(uint64_t*)b == *(int64_t*)a);
         else if (atag == T_DOUBLE)
             return (*(uint64_t*)b == (uint64_t)(int64_t)*(double*)a);
+        else if (atag == T_MPINT)
+            return mpcmp(*(mpint**)a, uvtomp(*(uint64_t*)b, cmpmpint)) == 0;
     }
     else if (btag == T_INT64) {
         if (atag == T_UINT64)
@@ -225,6 +278,8 @@
             return (*(int64_t*)b == (int64_t)*(uint64_t*)a);
         else if (atag == T_DOUBLE)
             return (*(int64_t*)b == (int64_t)*(double*)a);
+        else if (atag == T_MPINT)
+            return mpcmp(*(mpint**)a, vtomp(*(int64_t*)b, cmpmpint)) == 0;
     }
     return 1;
 }
--- a/plan9/platform.h
+++ b/plan9/platform.h
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <ctype.h>
+#include <mp.h>
 
 #if defined(__amd64__) || \
     defined(__arm64__) || \
--- /dev/null
+++ b/posix/mp.h
@@ -1,0 +1,231 @@
+#ifndef _MPINT_H_
+#define _MPINT_H_
+
+typedef uint32_t mpdigit;
+typedef uint8_t uchar;
+typedef uint32_t ulong;
+typedef uint32_t u32int;
+typedef unsigned int uint;
+typedef int64_t vlong;
+typedef uint64_t uvlong;
+typedef uint64_t u64int;
+
+typedef union FPdbleword FPdbleword;
+union FPdbleword
+{
+	double	x;
+	struct {	/* little endian */
+		uint lo;
+		uint hi;
+	};
+};
+
+#define sysfatal(s) do{ fprintf(stderr, "%s\n", s); abort(); }while(0)
+
+#define mpdighi  (mpdigit)(1U<<(Dbits-1))
+#define DIGITS(x) ((Dbits - 1 + (x))/Dbits)
+
+// for converting between int's and mpint's
+#define MAXUINT ((uint)-1)
+#define MAXINT (MAXUINT>>1)
+#define MININT (MAXINT+1)
+
+// for converting between vlongs's and mpint's
+#define MAXUVLONG (~0ULL)
+#define MAXVLONG (MAXUVLONG>>1)
+#define MINVLONG (MAXVLONG+1ULL)
+
+extern	int	dec64(uchar*, int, char*, int);
+extern	int	enc64(char*, int, uchar*, int);
+extern	int	dec64x(uchar*, int, char*, int, int (*)(int));
+extern	int	enc64x(char*, int, uchar*, int, int (*)(int));
+extern	int	dec32(uchar*, int, char*, int);
+extern	int	enc32(char*, int, uchar*, int);
+extern	int	dec32x(uchar*, int, char*, int, int (*)(int));
+extern	int	enc32x(char*, int, uchar*, int, int (*)(int));
+extern	int	dec16(uchar*, int, char*, int);
+extern	int	enc16(char*, int, uchar*, int);
+extern	int	dec64chr(int);
+extern	int	enc64chr(int);
+extern	int	dec32chr(int);
+extern	int	enc32chr(int);
+extern	int	dec16chr(int);
+extern	int	enc16chr(int);
+
+/*
+ * the code assumes mpdigit to be at least an int
+ * mpdigit must be an atomic type.  mpdigit is defined
+ * in the architecture specific u.h
+ */
+typedef struct mpint mpint;
+
+struct mpint
+{
+	int	sign;	/* +1 or -1 */
+	int	size;	/* allocated digits */
+	int	top;	/* significant digits */
+	mpdigit	*p;
+	char	flags;
+};
+
+enum
+{
+	MPstatic=	0x01,	/* static constant */
+	MPnorm=		0x02,	/* normalization status */
+	MPtimesafe=	0x04,	/* request time invariant computation */
+	MPfield=	0x08,	/* this mpint is a field modulus */
+
+	Dbytes=		sizeof(mpdigit),	/* bytes per digit */
+	Dbits=		Dbytes*8		/* bits per digit */
+};
+
+/* allocation */
+void	mpsetminbits(int n);	/* newly created mpint's get at least n bits */
+mpint*	mpnew(int n);		/* create a new mpint with at least n bits */
+void	mpfree(mpint *b);
+void	mpbits(mpint *b, int n);	/* ensure that b has at least n bits */
+mpint*	mpnorm(mpint *b);		/* dump leading zeros */
+mpint*	mpcopy(mpint *b);
+void	mpassign(mpint *old, mpint *new);
+
+/* random bits */
+mpint*	mprand(int bits, void (*gen)(uchar*, int), mpint *b);
+/* return uniform random [0..n-1] */
+mpint*	mpnrand(mpint *n, void (*gen)(uchar*, int), mpint *b);
+
+/* conversion */
+mpint*	strtomp(char*, char**, int, mpint*);	/* ascii */
+char*	mptoa(mpint*, int, char*, int);
+mpint*	letomp(uchar*, uint, mpint*);	/* byte array, little-endian */
+int	mptole(mpint*, uchar*, uint, uchar**);
+void	mptolel(mpint *b, uchar *p, int n);
+mpint*	betomp(uchar*, uint, mpint*);	/* byte array, big-endian */
+int	mptobe(mpint*, uchar*, uint, uchar**);
+void	mptober(mpint *b, uchar *p, int n);
+uint	mptoui(mpint*);			/* unsigned int */
+mpint*	uitomp(uint, mpint*);
+int	mptoi(mpint*);			/* int */
+mpint*	itomp(int, mpint*);
+uvlong	mptouv(mpint*);			/* unsigned vlong */
+mpint*	uvtomp(uvlong, mpint*);
+vlong	mptov(mpint*);			/* vlong */
+mpint*	vtomp(vlong, mpint*);
+double	mptod(mpint*);			/* double */
+mpint*	dtomp(double, mpint*);
+
+/* divide 2 digits by one */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* in the following, the result mpint may be */
+/* the same as one of the inputs. */
+void	mpadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpsub(mpint *b1, mpint *b2, mpint *diff);	/* diff = b1-b2 */
+void	mpleft(mpint *b, int shift, mpint *res);	/* res = b<<shift */
+void	mpright(mpint *b, int shift, mpint *res);	/* res = b>>shift */
+void	mpmul(mpint *b1, mpint *b2, mpint *prod);	/* prod = b1*b2 */
+void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	/* res = b**e mod m */
+void	mpmod(mpint *b, mpint *m, mpint *remainder);	/* remainder = b mod m */
+
+/* logical operations */
+void	mpand(mpint *b1, mpint *b2, mpint *res);
+void	mpbic(mpint *b1, mpint *b2, mpint *res);
+void	mpor(mpint *b1, mpint *b2, mpint *res);
+void	mpnot(mpint *b, mpint *res);
+void	mpxor(mpint *b1, mpint *b2, mpint *res);
+void	mptrunc(mpint *b, int n, mpint *res);
+void	mpxtend(mpint *b, int n, mpint *res);
+void	mpasr(mpint *b, int shift, mpint *res);
+
+/* modular arithmetic, time invariant when 0≤b1≤m-1 and 0≤b2≤m-1 */
+void	mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum);	/* sum = b1+b2 % m */
+void	mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff);	/* diff = b1-b2 % m */
+void	mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod);	/* prod = b1*b2 % m */
+
+/* quotient = dividend/divisor, remainder = dividend % divisor */
+void	mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
+
+/* return neg, 0, pos as b1-b2 is neg, 0, pos */
+int	mpcmp(mpint *b1, mpint *b2);
+
+/* res = s != 0 ? b1 : b2 */
+void	mpsel(int s, mpint *b1, mpint *b2, mpint *res);
+
+/* extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d */
+void	mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
+
+/* res = b**-1 mod m */
+void	mpinvert(mpint *b, mpint *m, mpint *res);
+
+/* bit counting */
+int	mpsignif(mpint*);	/* number of sigificant bits in mantissa */
+int	mplowbits0(mpint*);	/* k, where n = 2**k * q for odd q */
+
+/* well known constants */
+extern mpint	*mpzero, *mpone, *mptwo;
+
+/* sum[0:alen] = a[0:alen-1] + b[0:blen-1] */
+/* prereq: alen >= blen, sum has room for alen+1 digits */
+void	mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
+
+/* diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] */
+/* prereq: alen >= blen, diff has room for alen digits */
+void	mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
+
+/* p[0:n] += m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+void	mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:n] -= m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+int	mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:alen+blen-1] = a[0:alen-1] * b[0:blen-1] */
+/* prereq: alen >= blen, p has room for m*n digits */
+void	mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+void	mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+
+/* sign of a - b or zero if the same */
+int	mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
+int	mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen);
+
+/* divide the 2 digit dividend by the one digit divisor and stick in quotient */
+/* we assume that the result is one digit - overflow is all 1's */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* playing with magnitudes */
+int	mpmagcmp(mpint *b1, mpint *b2);
+void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+
+/* chinese remainder theorem */
+typedef struct CRTpre	CRTpre;		/* precomputed values for converting */
+					/*  twixt residues and mpint */
+typedef struct CRTres	CRTres;		/* residue form of an mpint */
+
+struct CRTres
+{
+	int	n;		/* number of residues */
+	mpint	*r[1];		/* residues */
+};
+
+CRTpre*	crtpre(int, mpint**);			/* precompute conversion values */
+CRTres*	crtin(CRTpre*, mpint*);			/* convert mpint to residues */
+void	crtout(CRTpre*, CRTres*, mpint*);	/* convert residues to mpint */
+void	crtprefree(CRTpre*);
+void	crtresfree(CRTres*);
+
+/* fast field arithmetic */
+typedef struct Mfield	Mfield;
+
+struct Mfield
+{
+	mpint	m;
+	int	(*reduce)(Mfield*, mpint*, mpint*);
+};
+
+mpint *mpfield(mpint*);
+
+Mfield *gmfield(mpint*);
+Mfield *cnfield(mpint*);
+
+#endif
--- a/posix/platform.h
+++ b/posix/platform.h
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <wctype.h>
 #include <wchar.h>
+#include "mp.h"
 
 #ifndef __SIZEOF_POINTER__
 #error pointer size unknown
--- a/print.c
+++ b/print.c
@@ -700,6 +700,15 @@
         else
             HPOS += ios_printf(f, "#%s(%"PRIu64")", symbol_name(type), ui64);
     }
+    else if (type == mpintsym) {
+        mpint *i = *(mpint**)data;
+        char *s = mptoa(i, 10, nil, 0);
+        if (weak || print_princ)
+            HPOS += ios_printf(f, "%s", s);
+        else
+            HPOS += ios_printf(f, "#%s(%s)", symbol_name(type), s);
+        free(s);
+    }
     else if (issymbol(type)) {
         // handle other integer prims. we know it's smaller than uint64
         // at this point, so int64 is big enough to capture everything.
--- a/read.c
+++ b/read.c
@@ -6,23 +6,25 @@
 };
 
 #if defined(__plan9__)
-// FIXME remove all this after adding mp entirely
-#include <mp.h>
-#define ERANGE 34
+static int errno;
 #define VLONG_MAX ~(1LL<<63)
 #define VLONG_MIN (1LL<<63)
 #define UVLONG_MAX (1LL<<63)
-static int errno;
 static mpint *mp_vlong_min, *mp_vlong_max, *mp_uvlong_max;
+#endif
 
-static vlong strtoll_(char *nptr, char **rptr, int base)
+static int64_t strtoll_mp(char *nptr, char **rptr, int base, mpint **mp)
 {
-    vlong x;
-    mpint *m, *c;
+    int64_t x;
+    mpint *m;
 
+    *mp = nil;
+    errno = 0;
     x = strtoll(nptr, rptr, base);
+#if defined(__plan9__)
     if((x != VLONG_MAX && x != VLONG_MIN) || *rptr == nptr)
         return x;
+    mpint *c;
     m = strtomp(nptr, rptr, base, nil);
     if(x == VLONG_MAX){
         if(mp_vlong_max == nil) mp_vlong_max = vtomp(VLONG_MAX, nil);
@@ -31,29 +33,45 @@
         if(mp_vlong_min == nil) mp_vlong_min = vtomp(VLONG_MIN, nil);
         c = mp_vlong_min;
     }
-    errno = mpcmp(c, m) == 0 ? 0 : ERANGE;
-    mpfree(m);
+    if (mpcmp(c, m) == 0) {
+        mpfree(m);
+        m = nil;
+    }
+#else
+    m = nil;
+    if (errno == ERANGE && (x == LLONG_MAX || x == LLONG_MIN))
+        m = strtomp(nptr, rptr, base, nil);
+#endif
+    *mp = m;
     return x;
 }
 
-static uvlong strtoull_(char *nptr, char **rptr, int base)
+static uint64_t strtoull_mp(char *nptr, char **rptr, int base, mpint **mp)
 {
-    uvlong x;
+    uint64_t x;
     mpint *m;
 
+    *mp = nil;
+    errno = 0;
     x = strtoull(nptr, rptr, base);
+#if defined(__plan9__)
     if(x != UVLONG_MAX || *rptr == nptr)
         return x;
     m = strtomp(nptr, rptr, base, nil);
     if(mp_uvlong_max == nil)
         mp_uvlong_max = uvtomp(UVLONG_MAX, nil);
-    errno = mpcmp(mp_uvlong_max, m) == 0 ? 0 : ERANGE;
-    mpfree(m);
+    if(mpcmp(mp_uvlong_max, m) == 0){
+        mpfree(m);
+        m = nil;
+    }
+#else
+    m = nil;
+    if (errno == ERANGE && x == ULLONG_MAX)
+        m = strtomp(nptr, rptr, base, nil);
+#endif
+    *mp = m;
     return x;
 }
-#define strtoll strtoll_
-#define strtoull strtoull_
-#endif
 
 #define F value2c(ios_t*,readstate->source)
 
@@ -73,6 +91,7 @@
     int64_t i64;
     uint64_t ui64;
     double d;
+    mpint *mp = nil;
     if (*tok == '\0')
         return 0;
     if (!((tok[0]=='0' && tok[1]=='x') || (base >= 15)) &&
@@ -110,18 +129,14 @@
             if (pval) *pval = mk_double(D_NINF);
             return 1;
         }
-        errno = 0;
-        i64 = strtoll(tok, &end, base);
-        if (errno)
-            return 0;
-        if (pval) *pval = return_from_int64(i64);
+        i64 = strtoll_mp(tok, &end, base, &mp);
+        if (pval)
+            *pval = mp == nil ? return_from_int64(i64) : mk_mpint(mp);
         return (*end == '\0');
     }
-    errno = 0;
-    ui64 = strtoull(tok, &end, base);
-    if (errno)
-        return 0;
-    if (pval) *pval = return_from_uint64(ui64);
+    ui64 = strtoull_mp(tok, &end, base, &mp);
+    if (pval)
+        *pval = mp == nil ? return_from_uint64(ui64) : mk_mpint(mp);
     return (*end == '\0');
 }
 
@@ -132,12 +147,7 @@
 
 static int read_numtok(char *tok, value_t *pval, int base)
 {
-    int result;
-    errno = 0;
-    result = isnumtok_base(tok, pval, base);
-    if (errno == ERANGE)
-        lerrorf(ParseError, "read: overflow in numeric constant %s", tok);
-    return result;
+    return isnumtok_base(tok, pval, base);
 }
 
 static uint32_t toktype = TOK_NONE;
@@ -182,7 +192,7 @@
 {
     buf[(*pi)++] = c;
     if (*pi >= (int)(sizeof(buf)-1))
-        lerrorf(ParseError, "read: token too long");
+        lerrorf(ParseError, "token too long");
 }
 
 // return: 1 if escaped (forced to be symbol)
@@ -262,7 +272,7 @@
     else if (c == '#') {
         ch = ios_getc(F); c = (char)ch;
         if (ch == IOS_EOF)
-            lerrorf(ParseError, "read: invalid read macro");
+            lerrorf(ParseError, "invalid read macro");
         if (c == '.') {
             toktype = TOK_SHARPDOT;
         }
@@ -272,7 +282,7 @@
         else if (c == '\\') {
             uint32_t cval;
             if (ios_getutf8(F, &cval) == IOS_EOF)
-                lerrorf(ParseError, "read: end of input in character constant");
+                lerrorf(ParseError, "end of input in character constant");
             if (cval == (uint32_t)'u' || cval == (uint32_t)'U' ||
                 cval == (uint32_t)'x') {
                 read_token('u', 0);
@@ -300,7 +310,7 @@
                 else if (tokval == spacesym)      cval = 0x20;
                 else if (tokval == deletesym)     cval = 0x7F;
                 else
-                    lerrorf(ParseError, "read: unknown character #\\%s", buf);
+                    lerrorf(ParseError, "unknown character #\\%s", buf);
             }
             toktype = TOK_NUM;
             tokval = mk_wchar(cval);
@@ -309,7 +319,7 @@
             toktype = TOK_SHARPOPEN;
         }
         else if (c == '<') {
-            lerrorf(ParseError, "read: unreadable object");
+            lerrorf(ParseError, "unreadable object");
         }
         else if (isdigit(c)) {
             read_token(c, 1);
@@ -319,11 +329,10 @@
             else if (c == '=')
                 toktype = TOK_LABEL;
             else
-                lerrorf(ParseError, "read: invalid label");
-            errno = 0;
+                lerrorf(ParseError, "invalid label");
             x = strtoll(buf, &end, 10);
-            if (*end != '\0' || errno)
-                lerrorf(ParseError, "read: invalid label");
+            if (*end != '\0')
+                lerrorf(ParseError, "invalid label");
             tokval = fixnum(x);
         }
         else if (c == '!') {
@@ -340,7 +349,7 @@
                 ch = ios_getc(F);
             hashpipe_gotc:
                 if (ch == IOS_EOF)
-                    lerrorf(ParseError, "read: eof within comment");
+                    lerrorf(ParseError, "eof within comment");
                 if ((char)ch == '|') {
                     ch = ios_getc(F);
                     if ((char)ch == '#') {
@@ -374,10 +383,9 @@
             if ((char)ch == 'g')
                 ch = ios_getc(F);
             read_token((char)ch, 0);
-            errno = 0;
             x = strtol(buf, &end, 10);
-            if (*end != '\0' || buf[0] == '\0' || errno)
-                lerrorf(ParseError, "read: invalid gensym label");
+            if (*end != '\0' || buf[0] == '\0')
+                lerrorf(ParseError, "invalid gensym label");
             toktype = TOK_GENSYM;
             tokval = fixnum(x);
         }
@@ -391,7 +399,7 @@
                 (isdigit_base(buf[1],base) ||
                  buf[1]=='-')) {
                 if (!read_numtok(&buf[1], &tokval, base))
-                    lerrorf(ParseError, "read: invalid base %d constant", base);
+                    lerrorf(ParseError, "invalid base %d constant", base);
                 return (toktype=TOK_NUM);
             }
 
@@ -399,7 +407,7 @@
             tokval = symbol(buf);
         }
         else {
-            lerrorf(ParseError, "read: unknown read macro");
+            lerrorf(ParseError, "unknown read macro");
         }
     }
     else if (c == ',') {
@@ -465,7 +473,7 @@
         ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
     while (peek() != closer) {
         if (ios_eof(F))
-            lerrorf(ParseError, "read: unexpected end of input");
+            lerrorf(ParseError, "unexpected end of input");
         if (i >= vector_size(v)) {
             v = Stack[SP-1] = vector_grow(v);
             if (label != UNBOUND)
@@ -499,7 +507,7 @@
             temp = realloc(buf, sz);
             if (temp == nil) {
                 free(buf);
-                lerrorf(ParseError, "read: out of memory reading string");
+                lerrorf(ParseError, "out of memory reading string");
             }
             buf = temp;
         }
@@ -506,7 +514,7 @@
         c = ios_getc(F);
         if (c == IOS_EOF) {
             free(buf);
-            lerrorf(ParseError, "read: unexpected end of input in string");
+            lerrorf(ParseError, "unexpected end of input in string");
         }
         if (c == '"')
             break;
@@ -514,7 +522,7 @@
             c = ios_getc(F);
             if (c == IOS_EOF) {
                 free(buf);
-                lerrorf(ParseError, "read: end of input in escape sequence");
+                lerrorf(ParseError, "end of input in escape sequence");
             }
             j = 0;
             if (octal_digit(c)) {
@@ -544,7 +552,7 @@
                 if (j) wc = strtol(eseq, nil, 16);
                 if (!j || wc > 0x10ffff) {
                     free(buf);
-                    lerrorf(ParseError, "read: invalid escape sequence");
+                    lerrorf(ParseError, "invalid escape sequence");
                 }
                 if (ndig == 2)
                     buf[i++] = ((char)wc);
@@ -555,7 +563,7 @@
                 char esc = read_escape_control_char((char)c);
                 if (esc == (char)c && !strchr("\\'\"`", esc)) {
                     free(buf);
-                    lerrorf(ParseError, "read: invalid escape sequence: \\%c", (char)c);
+                    lerrorf(ParseError, "invalid escape sequence: \\%c", (char)c);
                 }
                 buf[i++] = esc;
             }
@@ -583,7 +591,7 @@
     t = peek();
     while (t != closer) {
         if (ios_eof(F))
-            lerrorf(ParseError, "read: unexpected end of input");
+            lerrorf(ParseError, "unexpected end of input");
         c = mk_cons(); car_(c) = cdr_(c) = NIL;
         if (iscons(*pc)) {
             cdr_(*pc) = c;
@@ -604,10 +612,10 @@
             cdr_(*pc) = c;
             t = peek();
             if (ios_eof(F))
-                lerrorf(ParseError, "read: unexpected end of input");
+                lerrorf(ParseError, "unexpected end of input");
             if (t != closer) {
                 take();
-                lerrorf(ParseError, "read: expected '%c'", closer==TOK_CLOSEB ? ']' : ')');
+                lerrorf(ParseError, "expected '%c'", closer==TOK_CLOSEB ? ']' : ')');
             }
         }
     }
@@ -628,11 +636,11 @@
     take();
     switch (t) {
     case TOK_CLOSE:
-        lerrorf(ParseError, "read: unexpected ')'");
+        lerrorf(ParseError, "unexpected ')'");
     case TOK_CLOSEB:
-        lerrorf(ParseError, "read: unexpected ']'");
+        lerrorf(ParseError, "unexpected ']'");
     case TOK_DOT:
-        lerrorf(ParseError, "read: unexpected '.'");
+        lerrorf(ParseError, "unexpected '.'");
     case TOK_SYM:
     case TOK_NUM:
         return tokval;
@@ -678,7 +686,7 @@
         c = nextchar();
         if (c != '(') {
             take();
-            lerrorf(ParseError, "read: expected argument list for %s",
+            lerrorf(ParseError, "expected argument list for %s",
                     symbol_name(tokval));
         }
         PUSH(NIL);
@@ -713,7 +721,7 @@
     case TOK_LABEL:
         // create backreference label
         if (ptrhash_has(&readstate->backrefs, (void*)tokval))
-            lerrorf(ParseError, "read: label %"PRIdPTR" redefined", numval(tokval));
+            lerrorf(ParseError, "label %"PRIdPTR" redefined", numval(tokval));
         oldtokval = tokval;
         v = do_read_sexpr(tokval);
         ptrhash_put(&readstate->backrefs, (void*)oldtokval, (void*)v);
@@ -722,7 +730,7 @@
         // look up backreference
         v = (value_t)ptrhash_get(&readstate->backrefs, (void*)tokval);
         if (v == (value_t)HT_NOTFOUND)
-            lerrorf(ParseError, "read: undefined label %"PRIdPTR, numval(tokval));
+            lerrorf(ParseError, "undefined label %"PRIdPTR, numval(tokval));
         return v;
     case TOK_GENSYM:
         pv = (value_t*)ptrhash_bp(&readstate->gensyms, (void*)tokval);
--- a/test/unittest.lsp
+++ b/test/unittest.lsp
@@ -82,6 +82,9 @@
 
 (assert (> 9223372036854775808 9223372036854775807))
 
+; mpint
+(assert (> 0x10000000000000000 0x8fffffffffffffff))
+
 ; NaNs
 (assert (equal? +nan.0 +nan.0))
 (assert (not (= +nan.0 +nan.0)))