pubkey.c (RSAPriv): Fix segfault if `p' is provided but not `q'.
[catacomb-python] / mp.c
diff --git a/mp.c b/mp.c
index e14b3a0..f2a00b9 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -1,13 +1,11 @@
 /* -*-c-*-
  *
- * $Id$
- *
  * Multiprecision arithmetic
  *
  * (c) 2004 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of the Python interface to Catacomb.
  *
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * Catacomb/Python is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with Catacomb/Python; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 PyTypeObject *mp_pytype = 0;
 PyTypeObject *gf_pytype = 0;
 
-mp *mp_frompylong(PyLongObject *l)
+mp *mp_frompylong(PyObject *obj)
 {
   unsigned long bits;
+  PyLongObject *l = (PyLongObject *)obj;
   int sz;
   size_t w;
   mpd r = 0;
@@ -71,7 +70,7 @@ mp *mp_frompylong(PyLongObject *l)
   return (x);
 }
 
-PyLongObject *mp_topylong(mp *x)
+PyObject *mp_topylong(mp *x)
 {
   unsigned long bits = mp_bits(x);
   int sz = (bits + SHIFT - 1)/SHIFT;
@@ -83,7 +82,7 @@ PyLongObject *mp_topylong(mp *x)
 
   assert(MPW_BITS >= SHIFT);
   while (i < sz && p < x->vl) {
-    r |= *p << b;
+    r |= (mpd)*p++ << b;
     b += MPW_BITS;
     while (i < sz && b >= SHIFT) {
       l->ob_digit[i++] = r & MASK;
@@ -96,15 +95,13 @@ PyLongObject *mp_topylong(mp *x)
     r >>= SHIFT;
   }
   l->ob_size = (x->f & MP_NEG) ? -sz : sz;
-  return (l);
+  return ((PyObject *)l);
 }
 
 mp *mp_frompyobject(PyObject *o, int radix)
 {
   mp *x;
 
-  if ((x = tomp(o)) != 0)
-    return (x);
   if (PyString_Check(o)) {
     mptext_stringctx sc;
     mp *x;
@@ -115,6 +112,8 @@ mp *mp_frompyobject(PyObject *o, int radix)
     if (sc.buf < sc.lim) { MP_DROP(x); return (0); }
     return (x);
   }
+  if ((x = tomp(o)) != 0)
+    return (x);
   return (0);
 }
 
@@ -143,6 +142,13 @@ PyObject *mp_topystring(mp *x, int radix, const char *xpre,
   return (o);
 }
 
+static int good_radix_p(int r, int readp)
+{
+  return ((r >= -255 && r <= -2) ||
+         (readp && r == 0) ||
+         (r >= 2 && r <= 62));
+}
+
 PyObject *mp_pywrap(mp *x)
 {
   mp_pyobj *z = PyObject_New(mp_pyobj, mp_pytype);
@@ -157,7 +163,7 @@ PyObject *gf_pywrap(mp *x)
   return ((PyObject *)z);
 }
 
-int mp_tolong_checked(mp *x, long *l)
+int mp_tolong_checked(mp *x, long *l, int must)
 {
   static mp *longmin = 0, *longmax = 0;
   int rc = -1;
@@ -166,8 +172,10 @@ int mp_tolong_checked(mp *x, long *l)
     longmin = mp_fromlong(MP_NEW, LONG_MIN);
     longmax = mp_fromlong(MP_NEW, LONG_MAX);
   }
-  if (MP_CMP(x, <, longmin) || MP_CMP(x, >, longmax))
-    VALERR("mp out of range for int");
+  if (MP_CMP(x, <, longmin) || MP_CMP(x, >, longmax)) {
+    if (must) VALERR("mp out of range for int");
+    else goto end;
+  }
   *l = mp_tolong(x);
   rc = 0;
 end:
@@ -203,6 +211,7 @@ mp *tomp(PyObject *o)
     return (MP_COPY(PFILT_F(o)->m));
   else if (ECPT_PYCHECK(o)) {
     ec p = EC_INIT;
+    if (EC_ATINF(ECPT_P(o))) return (0);
     getecptout(&p, o);
     x = MP_COPY(p.x);
     EC_DESTROY(&p);
@@ -214,7 +223,7 @@ mp *tomp(PyObject *o)
   } else if (PyInt_Check(o))
     return (mp_fromlong(MP_NEW, PyInt_AS_LONG(o)));
   else if ((l = PyNumber_Long(o)) != 0) {
-    x = mp_frompylong((PyLongObject *)l);
+    x = mp_frompylong(l);
     Py_DECREF(l);
     return (x);
   } else {
@@ -261,11 +270,44 @@ int convgf(PyObject *o, void *p)
   return (1);
 }
 
+static mp *implicitmp(PyObject *o)
+{
+  if (!o ||
+      GF_PYCHECK(o) ||
+      ECPT_PYCHECK(o) ||
+      FE_PYCHECK(o) ||
+      GE_PYCHECK(o))
+    return (0);
+  return (tomp(o));
+}
+
+static mp *implicitgf(PyObject *o)
+{
+  if (!o ||
+      MP_PYCHECK(o) ||
+      ECPT_PYCHECK(o) ||
+      FE_PYCHECK(o) ||
+      GE_PYCHECK(o))
+    return (0);
+  return (tomp(o));
+}
+
 static int mpbinop(PyObject *x, PyObject *y, mp **xx, mp **yy)
 {
-  if ((*xx = tomp(x)) == 0)
+  if ((*xx = implicitmp(x)) == 0)
+    return (-1);
+  if ((*yy = implicitmp(y)) == 0) {
+    MP_DROP(*xx);
+    return (-1);
+  }
+  return (0);
+}
+
+static int gfbinop(PyObject *x, PyObject *y, mp **xx, mp **yy)
+{
+  if ((*xx = implicitgf(x)) == 0)
     return (-1);
-  if ((*yy = tomp(y)) == 0) {
+  if ((*yy = implicitgf(y)) == 0) {
     MP_DROP(*xx);
     return (-1);
   }
@@ -278,7 +320,7 @@ static int mpbinop(PyObject *x, PyObject *y, mp **xx, mp **yy)
 #define BINOP(pre, name)                                               \
   static PyObject *pre##_py##name(PyObject *x, PyObject *y) {          \
     mp *xx, *yy, *zz;                                                  \
-    if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;                       \
+    if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL;                    \
     zz = pre##_##name(MP_NEW, xx, yy);                                 \
     MP_DROP(xx); MP_DROP(yy);                                          \
     return (pre##_pywrap(zz));                                         \
@@ -322,8 +364,8 @@ static PyObject *mp_pyid(PyObject *x) { RETURN_OBJ(x); }
     mp *xx, *yy;                                                       \
     PyObject *z = 0;                                                   \
     long n;                                                            \
-    if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;                       \
-    if (mp_tolong_checked(yy, &n)) goto end;                           \
+    if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL;                    \
+    if (mp_tolong_checked(yy, &n, 1)) goto end;                                \
     if (n < 0)                                                         \
       z = pre##_pywrap(mp_##rname(MP_NEW, xx, -n));                    \
     else                                                               \
@@ -343,7 +385,7 @@ SHIFTOP(gf, lsr, lsl)
     mp *xx, *yy;                                                       \
     PyObject *z = 0;                                                   \
     INIT_##qq(q) INIT_##rr(r)                                          \
-    if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;                       \
+    if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL;                    \
     if (MP_ZEROP(yy))                                                  \
       ZDIVERR("division by zero");                                     \
     pre##_div(ARG_##qq(q), ARG_##rr(r), xx, yy);                       \
@@ -388,7 +430,7 @@ static PyObject *mp_pyexp(PyObject *x, PyObject *y, PyObject *z)
   mp *r = 0;
   PyObject *rc = 0;
 
-  if ((xx = tomp(x)) == 0 || (yy = tomp(y)) == 0 ||
+  if ((xx = implicitmp(x)) == 0 || (yy = implicitmp(y)) == 0 ||
       (z && z != Py_None && (zz = tomp(z)) == 0)) {
     mp_drop(xx); mp_drop(yy); mp_drop(zz);
     RETURN_NOTIMPL;
@@ -444,20 +486,20 @@ static int mp_pynonzerop(PyObject *x) { return !MP_ZEROP(MP_X(x)); }
 static PyObject *mp_pyint(PyObject *x)
 {
   long l;
-  if (mp_tolong_checked(MP_X(x), &l)) return (0);
-  return (PyInt_FromLong(l));
+  if (!mp_tolong_checked(MP_X(x), &l, 0)) return (PyInt_FromLong(l));
+  else return mp_topylong(MP_X(x));
 }
 static PyObject *mp_pylong(PyObject *x)
-  { return (PyObject *)mp_topylong(MP_X(x)); }
+  { return (mp_topylong(MP_X(x))); }
 static PyObject *mp_pyfloat(PyObject *x)
 {
-  PyObject *l = (PyObject *)mp_topylong(MP_X(x));
+  PyObject *l = mp_topylong(MP_X(x));
   double f = PyLong_AsDouble(l);
   Py_DECREF(l);
   return (PyFloat_FromDouble(f));
 }
 
-#define COERCE(pre, PRE)                                               \
+#define COERCE(pre, PRE)                                               \
   static int pre##_pycoerce(PyObject **x, PyObject **y)                        \
   {                                                                    \
     mp *z;                                                             \
@@ -488,10 +530,10 @@ static PyObject *mp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   int radix = 0;
   char *kwlist[] = { "x", "radix", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:mp", kwlist, &x, &radix))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", kwlist, &x, &radix))
     goto end;
   if (MP_PYCHECK(x)) RETURN_OBJ(x);
-  if (radix < -255 || radix > 62) VALERR("radix out of range");
+  if (!good_radix_p(radix, 1)) VALERR("bad radix");
   if ((z = mp_frompyobject(x, radix)) == 0) {
     PyErr_Format(PyExc_TypeError, "can't convert %.100s to mp",
                 x->ob_type->tp_name);
@@ -503,14 +545,15 @@ end:
   return ((PyObject *)zz);
 }
 
-static long mp_pyhash(PyObject *me)
+long mphash(mp *x)
 {
-  long i = mp_tolong(MP_X(me));
-  if (i == -1)
-    i = -2;
-  return (i);
+  PyObject *l = mp_topylong(x);
+  long h = PyObject_Hash(l);
+  Py_DECREF(l); return (h);
 }
 
+static long mp_pyhash(PyObject *me) { return (mphash(MP_X(me))); }
+
 static PyObject *mpmeth_jacobi(PyObject *me, PyObject *arg)
 {
   mp *y = 0;
@@ -526,27 +569,27 @@ end:
 #define BITOP(pre, name, c)                                            \
   static PyObject *pre##meth_##name(PyObject *me, PyObject *arg)       \
   {                                                                    \
-    int i;                                                             \
-    if (!PyArg_ParseTuple(arg, "i:" #name, &i)) return (0);            \
+    unsigned long i;                                                   \
+    if (!PyArg_ParseTuple(arg, "O&:" #name, convulong, &i)) return (0);        \
     return (pre##_pywrap(mp_##name##c(MP_NEW, MP_X(me), i)));          \
   }
 BITOP(mp, setbit, 2c);
 BITOP(mp, clearbit, 2c);
-BITOP(gf, setbit, ); 
+BITOP(gf, setbit, );
 BITOP(gf, clearbit, );
 #undef BITOP
 
 static PyObject *mpmeth_testbit(PyObject *me, PyObject *arg)
 {
-  int i;
-  if (!PyArg_ParseTuple(arg, "i:testbit", &i)) return (0);
+  unsigned long i;
+  if (!PyArg_ParseTuple(arg, "O&:testbit", convulong, &i)) return (0);
   return (getbool(mp_testbit2c(MP_X(me), i)));
 }
 
 static PyObject *gfmeth_testbit(PyObject *me, PyObject *arg)
 {
-  int i;
-  if (!PyArg_ParseTuple(arg, "i:testbit", &i)) return (0);
+  unsigned long i;
+  if (!PyArg_ParseTuple(arg, "O&:testbit", convulong, &i)) return (0);
   return (getbool(mp_testbit(MP_X(me), i)));
 }
 
@@ -622,8 +665,7 @@ static PyObject *mpmeth_tostring(PyObject *me, PyObject *arg, PyObject *kw)
   char *kwlist[] = { "radix", 0 };
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", kwlist, &radix))
     goto end;
-  if (radix < -255 || radix > 62 || radix == -1 || radix == 0 || radix == 1)
-    VALERR("bad radix");
+  if (!good_radix_p(radix, 0)) VALERR("bad radix");
   return (mp_topystring(MP_X(me), radix, 0, 0, 0));
 end:
   return (0);
@@ -643,6 +685,19 @@ end:
   return (z);
 }
 
+static PyObject *mpmeth_leastcongruent(PyObject *me, PyObject *arg)
+{
+  mp *z, *b, *m;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "O&O&:leastcongruent", convmp, &b, convmp, &m))
+    goto end;
+  z = mp_leastcongruent(MP_NEW, b, MP_X(me), m);
+  rc = mp_pywrap(z);
+end:
+  return (rc);
+}
+
 #define STOREOP(name, c)                                               \
   static PyObject *mpmeth_##name(PyObject *me,                         \
                                 PyObject *arg, PyObject *kw)           \
@@ -674,7 +729,7 @@ STOREOP(storeb2c, 2c)
   {                                                                    \
     buf b;                                                             \
     char *p;                                                           \
-    int sz;                                                            \
+    Py_ssize_t sz;                                                     \
     PyObject *rc = 0;                                                  \
     mp *x;                                                             \
                                                                        \
@@ -741,7 +796,7 @@ static PyGetSetDef mp_pygetset[] = {
 
 static PyMethodDef mp_pymethods[] = {
 #define METHNAME(func) mpmeth_##func
-  METH (jacobi,        "X.jacobi(Y) -> Jacobi symbol (Y/X) (NB inversion!)")
+  METH (jacobi,        "X.jacobi(Y) -> Jacobi symbol (Y|X) (NB inversion!)")
   METH (setbit,        "X.setbit(N) -> X with bit N set")
   METH (clearbit,      "X.clearbit(N) -> X with bit N clear")
   METH (testbit,       "X.testbit(N) -> true/false if bit N set/clear in X")
@@ -753,14 +808,16 @@ static PyMethodDef mp_pymethods[] = {
         "X.gcdx(Y) -> (gcd(X, Y), U, V) with X U + Y V = gcd(X, Y)")
   METH (modinv,        "X.modinv(Y) -> multiplicative inverse of Y mod X")
   METH (modsqrt,       "X.modsqrt(Y) -> square root of Y mod X, if X prime")
-  KWMETH(primep,       "X.primep(rng = rand) -> true/false if X is prime")
-  KWMETH(tostring,     "X.tostring(radix = 10) -> STR")
-  KWMETH(storel,       "X.storel(len = -1) -> little-endian bytes")
-  KWMETH(storeb,       "X.storeb(len = -1) -> big-endian bytes")
+  METH (leastcongruent,
+        "X.leastcongruent(B, M) -> smallest Z >= B with Z == X (mod M)")
+  KWMETH(primep,       "X.primep([rng = rand]) -> true/false if X is prime")
+  KWMETH(tostring,     "X.tostring([radix = 10]) -> STR")
+  KWMETH(storel,       "X.storel([len = -1]) -> little-endian bytes")
+  KWMETH(storeb,       "X.storeb([len = -1]) -> big-endian bytes")
   KWMETH(storel2c,
-        "X.storel2c(len = -1) -> little-endian bytes, two's complement")
+        "X.storel2c([len = -1]) -> little-endian bytes, two's complement")
   KWMETH(storeb2c,
-        "X.storeb2c(len = -1) -> big-endian bytes, two's complement")
+        "X.storeb2c([len = -1]) -> big-endian bytes, two's complement")
   METH (tobuf,         "X.tobuf() -> buffer format")
 #undef METHNAME
   { 0 }
@@ -810,8 +867,8 @@ static PyNumberMethods mp_pynumber = {
 };
 
 static PyTypeObject mp_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.MP",                       /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MP",                                        /* @tp_name@ */
   sizeof(mp_pyobj),                    /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -836,11 +893,15 @@ static PyTypeObject mp_pytype_skel = {
 
   /* @tp_doc@ */
 "Multiprecision integers, similar to `long' but more efficient and\n\
-versatile.  Support all the standard arithmetic operations.\n\
+versatile.  Support all the standard arithmetic operations, with\n\
+implicit conversions from `PrimeFilter', and other objects which\n\
+convert to `long'.\n\
 \n\
-Constructor mp(X, radix = R) attempts to convert X to an `mp'.  If\n\
+Constructor MP(X, [radix = R]) attempts to convert X to an `MP'.  If\n\
 X is a string, it's read in radix-R form, or we look for a prefix\n\
-if R = 0.  Other acceptable things are ints and longs.\n\
+if R = 0.  Other acceptable things are field elements, elliptic curve\n\
+points, group elements, Python `int' and `long' objects, and anything\n\
+with an integer conversion.\n\
 \n\
 Notes:\n\
 \n\
@@ -851,7 +912,7 @@ Notes:\n\
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   mp_pymethods,                                /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   mp_pygetset,                         /* @tp_getset@ */
@@ -868,11 +929,11 @@ Notes:\n\
 };
 
 static PyObject *meth__MP_fromstring(PyObject *me,
-                                   PyObject *arg, PyObject *kw)
+                                    PyObject *arg, PyObject *kw)
 {
   int r = 0;
   char *p;
-  int len;
+  Py_ssize_t len;
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
@@ -881,62 +942,194 @@ static PyObject *meth__MP_fromstring(PyObject *me,
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
                                   kwlist, &me, &p, &len, &r))
     goto end;
-  if (r < -255 || r > 62) VALERR("radix out of range");
+  if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
   if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0)
-    SYNERR("bad integer");
-  z = Py_BuildValue("(Ns#)", mp_pywrap(zz), sc.buf, (int)(sc.lim - sc.buf));
+    VALERR("bad integer");
+  z = Py_BuildValue("(Ns#)", mp_pywrap(zz),
+                   sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
 end:
   return (z);
 }
 
-static PyObject *meth__MP_product(PyObject *me, PyObject *arg)
+static PyObject *meth__MP_factorial(PyObject *me, PyObject *arg)
+{
+  unsigned long i;
+  mp *x;
+  if (!PyArg_ParseTuple(arg, "OO&:factorial", &me, convulong, &i))
+    return (0);
+  x = mp_factorial(i);
+  return mp_pywrap(x);
+}
+
+static PyObject *meth__MP_fibonacci(PyObject *me, PyObject *arg)
+{
+  long i;
+  mp *x;
+  if (!PyArg_ParseTuple(arg, "Ol:fibonacci", &me, &i)) return (0);
+  x = mp_fibonacci(i);
+  return mp_pywrap(x);
+}
+
+#define LOADOP(pre, py, name)                                          \
+  static PyObject *meth__##py##_##name(PyObject *me, PyObject *arg)    \
+  {                                                                    \
+    char *p;                                                           \
+    Py_ssize_t len;                                                    \
+    if (!PyArg_ParseTuple(arg, "Os#:" #name, &me, &p, &len)) return (0); \
+    return (pre##_pywrap(mp_##name(MP_NEW, p, len)));                  \
+  }
+LOADOP(mp, MP, loadl)
+LOADOP(mp, MP, loadb)
+LOADOP(mp, MP, loadl2c)
+LOADOP(mp, MP, loadb2c)
+LOADOP(gf, GF, loadl)
+LOADOP(gf, GF, loadb)
+#undef LOADOP
+
+/*----- Products of small integers ----------------------------------------*/
+
+typedef struct mpmul_pyobj {
+  PyObject_HEAD
+  int livep;
+  mpmul mm;
+} mpmul_pyobj;
+
+#define MPMUL_LIVEP(o) (((mpmul_pyobj *)(o))->livep)
+#define MPMUL_PY(o) (&((mpmul_pyobj *)(o))->mm)
+
+static void mpmul_pydealloc(PyObject *me)
+{
+  if (MPMUL_LIVEP(me))
+    mp_drop(mpmul_done(MPMUL_PY(me)));
+  FREEOBJ(me);
+}
+
+static PyObject *mmmeth_factor(PyObject *me, PyObject *arg)
 {
-  mpmul m;
   PyObject *q, *i;
   mp *x;
 
-  if (PyTuple_Size(arg) != 2) {
+  if (!MPMUL_LIVEP(me)) VALERR("MPMul object invalid");
+  if (PyTuple_Size(arg) != 1)
     i = PyObject_GetIter(arg);
-    PyIter_Next(i);
-  } else {
-    if ((q = PyTuple_GetItem(arg, 1)) == 0) return (0);
+  else {
+    if ((q = PyTuple_GetItem(arg, 0)) == 0) goto end;
     if ((i = PyObject_GetIter(q)) == 0) {
       PyErr_Clear(); /* that's ok */
       i = PyObject_GetIter(arg);
     }
   }
-  if (!i) return (0);
-  mpmul_init(&m);
+  if (!i) goto end;
   while ((q = PyIter_Next(i)) != 0) {
     x = getmp(q); Py_DECREF(q); if (!x) {
-      MP_DROP(mpmul_done(&m));
       Py_DECREF(i);
-      return (0);
+      goto end;
     }
-    mpmul_add(&m, x);
+    mpmul_add(MPMUL_PY(me), x);
     MP_DROP(x);
   }
-  x = mpmul_done(&m);
   Py_DECREF(i);
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *mmmeth_done(PyObject *me, PyObject *arg)
+{
+  mp *x;
+
+  if (!PyArg_ParseTuple(arg, ":done")) goto end;
+  if (!MPMUL_LIVEP(me)) VALERR("MPMul object invalid");
+  x = mpmul_done(MPMUL_PY(me));
+  MPMUL_LIVEP(me) = 0;
   return (mp_pywrap(x));
+end:
+  return (0);
 }
 
-#define LOADOP(pre, py, name)                                          \
-  static PyObject *meth__##py##_##name(PyObject *me, PyObject *arg)    \
-  {                                                                    \
-    char *p;                                                           \
-    int len;                                                           \
-    if (!PyArg_ParseTuple(arg, "Os#:" #name, &me, &p, &len)) return (0); \
-    return (pre##_pywrap(mp_##name(MP_NEW, p, len)));                  \
+static PyObject *mpmul_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  mpmul_pyobj *mm;
+
+  if (kw) TYERR("keyword arguments not allowed here");
+  mm = (mpmul_pyobj *)ty->tp_alloc(ty, 0);
+  mpmul_init(&mm->mm);
+  mm->livep = 1;
+  if (mmmeth_factor((PyObject *)mm, arg) == 0) {
+    Py_DECREF(mm);
+    goto end;
   }
-LOADOP(mp, MP, loadl)
-LOADOP(mp, MP, loadb)
-LOADOP(mp, MP, loadl2c)
-LOADOP(mp, MP, loadb2c)
-LOADOP(gf, GF, loadl)
-LOADOP(gf, GF, loadb)
-#undef LOADOP
+  return ((PyObject *)mm);
+end:
+  return (0);
+}
+
+static PyObject *mmget_livep(PyObject *me, void *hunoz)
+  { return (getbool(MPMUL_LIVEP(me))); }
+
+static PyGetSetDef mpmul_pygetset[] = {
+#define GETSETNAME(op, name) mm##op##_##name
+  GET  (livep,                 "MM.livep -> flag: object still valid?")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyMethodDef mpmul_pymethods[] = {
+#define METHNAME(name) mmmeth_##name
+  METH (factor,                "MM.factor(ITERABLE) or MM.factor(I, ...)")
+  METH (done,                  "MM.done() -> PRODUCT")
+#undef METHNAME
+  { 0 }
+};
+
+static PyTypeObject *mpmul_pytype, mpmul_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MPMul",                             /* @tp_name@ */
+  sizeof(mpmul_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  mpmul_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"An object for multiplying many small integers.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  mpmul_pymethods,                     /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  mpmul_pygetset,                      /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  mpmul_pynew,                         /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
 
 /*----- Montgomery reduction ----------------------------------------------*/
 
@@ -1040,7 +1233,7 @@ fail:
 
 static PyObject *mm_mexpr(PyObject *me, void *v, int n)
   { return mp_pywrap(mpmont_mexpr(MPMONT_PY(me), MP_NEW, v, n)); }
-  
+
 static void mp_mexp_drop(void *p)
 {
   mp_expfactor *f = p;
@@ -1080,7 +1273,7 @@ fail:
 
 static PyObject *mm_mexp(PyObject *me, void *v, int n)
   { return mp_pywrap(mpmont_mexp(MPMONT_PY(me), MP_NEW, v, n)); }
-  
+
 static PyObject *mmmeth_mexp(PyObject *me, PyObject *arg)
 {
   return mexp_common(me, arg, sizeof(mp_expfactor),
@@ -1141,25 +1334,25 @@ static PyGetSetDef mpmont_pygetset[] = {
 
 static PyMethodDef mpmont_pymethods[] = {
 #define METHNAME(name) mmmeth_##name
-  METH (int,           "M.out(X) -> XR")
+  METH (int,           "M.int(X) -> XR")
   METH (mul,           "M.mul(XR, YR) -> ZR where Z = X Y")
   METH (expr,          "M.expr(XR, N) -> ZR where Z = X^N mod M.m")
   METH (mexpr,         "\
-B.mexp([(XR0, N0), (XR1, N1), ...]) = ZR where Z = X0^N0 X1^N1 mod B.m\n\
+M.mexpr([(XR0, N0), (XR1, N1), ...]) = ZR where Z = X0^N0 X1^N1 ... mod M.m\n\
 \t(the list may be flattened if this more convenient.)")
   METH (reduce,        "M.reduce(XR) -> X")
   METH (ext,           "M.ext(XR) -> X")
   METH (exp,           "M.exp(X, N) -> X^N mod M.m")
   METH (mexp,          "\
-B.mexp([(X0, N0), (X1, N1), ...]) = X0^N0 X1^N1 mod B.m\n\
+M.mexp([(X0, N0), (X1, N1), ...]) = X0^N0 X1^N1 ... mod M.m\n\
 \t(the list may be flattened if this more convenient.)")
 #undef METHNAME
   { 0 }
 };
 
 static PyTypeObject *mpmont_pytype, mpmont_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.MPMont",                   /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MPMont",                            /* @tp_name@ */
   sizeof(mpmont_pyobj),                        /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1189,7 +1382,7 @@ static PyTypeObject *mpmont_pytype, mpmont_pytype_skel = {
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   mpmont_pymethods,                    /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   mpmont_pygetset,                     /* @tp_getset@ */
@@ -1233,7 +1426,7 @@ end:
 
 static PyObject *mb_mexp(PyObject *me, void *v, int n)
   { return mp_pywrap(mpbarrett_mexp(MPBARRETT_PY(me), MP_NEW, v, n)); }
-  
+
 static PyObject *mbmeth_mexp(PyObject *me, PyObject *arg)
 {
   return mexp_common(me, arg, sizeof(mp_expfactor),
@@ -1290,15 +1483,15 @@ static PyMethodDef mpbarrett_pymethods[] = {
   METH (reduce,        "B.reduce(X) -> X mod B.m")
   METH (exp,           "B.exp(X, N) -> X^N mod B.m")
   METH (mexp,          "\
-B.mexp([(X0, N0), (X1, N1), ...]) = X0^N0 X1^N1 mod B.m\n\
+B.mexp([(X0, N0), (X1, N1), ...]) = X0^N0 X1^N1 ... mod B.m\n\
 \t(the list may be flattened if this more convenient.)")
 #undef METHNAME
   { 0 }
 };
 
 static PyTypeObject *mpbarrett_pytype, mpbarrett_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.MPBarrett",                        /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MPBarrett",                         /* @tp_name@ */
   sizeof(mpbarrett_pyobj),             /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1328,7 +1521,7 @@ static PyTypeObject *mpbarrett_pytype, mpbarrett_pytype_skel = {
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   mpbarrett_pymethods,                 /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   mpbarrett_pygetset,                  /* @tp_getset@ */
@@ -1425,8 +1618,8 @@ static PyMethodDef mpreduce_pymethods[] = {
 };
 
 static PyTypeObject *mpreduce_pytype, mpreduce_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.MPReduce",                 /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MPReduce",                          /* @tp_name@ */
   sizeof(mpreduce_pyobj),              /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1456,7 +1649,7 @@ static PyTypeObject *mpreduce_pytype, mpreduce_pytype_skel = {
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   mpreduce_pymethods,                  /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   mpreduce_pygetset,                   /* @tp_getset@ */
@@ -1487,7 +1680,7 @@ static PyObject *mcmeth_solve(PyObject *me, PyObject *arg)
   PyObject *q = 0, *x, *z = 0;
   mp *xx;
   mp **v = 0;
-  int i = 0, n = c->k;
+  Py_ssize_t i = 0, n = c->k;
 
   Py_INCREF(me);
   if (PyTuple_Size(arg) == n)
@@ -1526,10 +1719,11 @@ static void mpcrt_pydealloc(PyObject *me)
 static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpcrt_mod *v = 0;
-  int n, i = 0;
+  Py_ssize_t n, i = 0, j;
   char *kwlist[] = { "mv", 0 };
   PyObject *q = 0, *x;
-  mp *xx;
+  mp *xx = MP_NEW, *y = MP_NEW, *g = MP_NEW;
+  mpmul mm;
   mpcrt_pyobj *c = 0;
 
   if (PyTuple_Size(arg) > 1)
@@ -1545,11 +1739,22 @@ static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   for (i = 0; i < n; i++) {
     if ((x = PySequence_GetItem(q, i)) == 0) goto end;
     xx = getmp(x); Py_DECREF(x); if (!xx) goto end;
-    v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0;
+    if (MP_CMP(xx, <=, MP_ZERO)) VALERR("moduli must be positive");
+    v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0; xx = MP_NEW;
+  }
+  mpmul_init(&mm);
+  for (j = 0; j < i; j++) mpmul_add(&mm, v[j].m);
+  xx = mpmul_done(&mm);
+  for (j = 0; j < i; j++) {
+    mp_div(&y, 0, xx, v[j].m);
+    mp_gcd(&g, 0, 0, y, v[j].m);
+    if (!MP_EQ(g, MP_ONE)) VALERR("moduli must be pairwise coprime");
   }
+
   c = (mpcrt_pyobj *)ty->tp_alloc(ty, 0);
   mpcrt_create(&c->c, v, n, 0);
   Py_DECREF(q);
+  mp_drop(xx); mp_drop(y); mp_drop(g);
   return ((PyObject *)c);
 
 end:
@@ -1560,6 +1765,7 @@ end:
     xfree(v);
   }
   Py_XDECREF(q);
+  mp_drop(xx); mp_drop(y); mp_drop(g);
   return (0);
 }
 
@@ -1594,8 +1800,8 @@ static PyMethodDef mpcrt_pymethods[] = {
 };
 
 static PyTypeObject *mpcrt_pytype, mpcrt_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.MPCRT",                    /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "MPCRT",                             /* @tp_name@ */
   sizeof(mpcrt_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1625,7 +1831,7 @@ static PyTypeObject *mpcrt_pytype, mpcrt_pytype_skel = {
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   mpcrt_pymethods,                     /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   mpcrt_pygetset,                      /* @tp_getset@ */
@@ -1652,7 +1858,7 @@ static PyObject *gf_pyrichcompare(PyObject *x, PyObject *y, int op)
   int xl, yl;
   int b;
 
-  if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;
+  if (gfbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;
   switch (op) {
     case Py_EQ: b = MP_EQ(xx, yy); break;
     case Py_NE: b = !MP_EQ(xx, yy); break;
@@ -1683,7 +1889,7 @@ static PyObject *gf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", kwlist, &x, &radix))
     goto end;
   if (GF_PYCHECK(x)) RETURN_OBJ(x);
-  if (radix < -255 || radix > 62) VALERR("radix out of range");
+  if (!good_radix_p(radix, 1)) VALERR("radix out of range");
   if ((z = mp_frompyobject(x, radix)) == 0) {
     PyErr_Format(PyExc_TypeError, "can't convert %.100s to gf",
                 x->ob_type->tp_name);
@@ -1699,15 +1905,6 @@ end:
   return ((PyObject *)zz);
 }
 
-static long gf_pyhash(PyObject *me)
-{
-  long i = mp_tolong(MP_X(me));
-  i ^= 0xc7ecd67c; /* random perturbance */
-  if (i == -1)
-    i = -2;
-  return (i);
-}
-
 static PyObject *gf_pyexp(PyObject *x, PyObject *y, PyObject *z)
 {
   mp *xx = 0, *yy = 0, *zz = 0;
@@ -1817,16 +2014,16 @@ static PyMethodDef gf_pymethods[] = {
   METH (gcdx,
         "X.gcdx(Y) -> (gcd(X, Y), U, V) with X U + Y V = gcd(X, Y)")
   METH (modinv,        "X.modinv(Y) -> multiplicative inverse of Y mod X")
-  METH (irreduciblep,  "X.irreduciblep() -> true/false")
+  METH (irreduciblep,  "X.irreduciblep() -> true/false")
 #undef METHNAME
 #define METHNAME(func) mpmeth_##func
-  KWMETH(tostring,     "X.tostring(radix = 10) -> STR")
-  KWMETH(storel,       "X.storel(len = -1) -> little-endian bytes")
-  KWMETH(storeb,       "X.storeb(len = -1) -> big-endian bytes")
+  KWMETH(tostring,     "X.tostring([radix = 10]) -> STR")
+  KWMETH(storel,       "X.storel([len = -1]) -> little-endian bytes")
+  KWMETH(storeb,       "X.storeb([len = -1]) -> big-endian bytes")
   KWMETH(storel2c,
-        "X.storel2c(len = -1) -> little-endian bytes, two's complement")
+        "X.storel2c([len = -1]) -> little-endian bytes, two's complement")
   KWMETH(storeb2c,
-        "X.storeb2c(len = -1) -> big-endian bytes, two's complement")
+        "X.storeb2c([len = -1]) -> big-endian bytes, two's complement")
   METH (tobuf,         "X.tobuf() -> buffer format")
 #undef METHNAME
   { 0 }
@@ -1876,8 +2073,8 @@ static PyNumberMethods gf_pynumber = {
 };
 
 static PyTypeObject gf_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.GF",                       /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GF",                                        /* @tp_name@ */
   sizeof(mp_pyobj),                    /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1890,7 +2087,7 @@ static PyTypeObject gf_pytype_skel = {
   &gf_pynumber,                                /* @tp_as_number@ */
   0,                                   /* @tp_as_sequence@ */
   0,                                   /* @tp_as_mapping@ */
-  gf_pyhash,                           /* @tp_hash@ */
+  mp_pyhash,                           /* @tp_hash@ */
   0,                                   /* @tp_call@ */
   mp_pyhex,                            /* @tp_str@ */
   0,                                   /* @tp_getattro@ */
@@ -1904,9 +2101,11 @@ static PyTypeObject gf_pytype_skel = {
 "Binary polynomials.  Support almost all the standard arithmetic\n\
 operations.\n\
 \n\
-Constructor gf(X, radix = R) attempts to convert X to a `gf'.  If\n\
+Constructor GF(X, [radix = R]) attempts to convert X to a `GF'.  If\n\
 X is a string, it's read in radix-R form, or we look for a prefix\n\
-if R = 0.  Other acceptable things are ints and longs.\n\
+if R = 0.  Other acceptable things are field elements, elliptic curve\n\
+points, group elements, Python `int' and `long' objects, and anything\n\
+with an integer conversion.\n\
 \n\
 The name is hopelessly wrong from a technical point of view, but\n\
 but it's much easier to type than `p2' or `c2' or whatever.\n\
@@ -1920,7 +2119,7 @@ Notes:\n\
   gf_pyrichcompare,                    /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   gf_pymethods,                                /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   gf_pygetset,                         /* @tp_getset@ */
@@ -1941,7 +2140,7 @@ static PyObject *meth__GF_fromstring(PyObject *me,
 {
   int r = 0;
   char *p;
-  int len;
+  Py_ssize_t len;
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
@@ -1950,13 +2149,15 @@ static PyObject *meth__GF_fromstring(PyObject *me,
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
                                   kwlist, &me, &p, &len, &r))
     goto end;
-  if (r < -255 || r > 62) VALERR("radix out of range");
+  if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
-  if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0 || MP_NEGP(zz))
-    z = Py_BuildValue("(Os#)", Py_None, p, len);
-  else
-    z = Py_BuildValue("(Ns#)", gf_pywrap(zz),
-                     sc.buf, (int)(sc.lim - sc.buf));
+  if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0 ||
+      MP_NEGP(zz)) {
+    if (zz) MP_DROP(zz);
+    VALERR("bad binary polynomial");
+  }
+  z = Py_BuildValue("(Ns#)", gf_pywrap(zz),
+                   sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
 end:
   return (z);
 }
@@ -1987,6 +2188,58 @@ end:
   return (rc);
 }
 
+static PyObject *grmeth_trace(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  mp *xx = 0;
+
+  if (!PyArg_ParseTuple(arg, "O&:trace", convgf, &xx)) goto end;
+  rc = PyInt_FromLong(gfreduce_trace(GFREDUCE_PY(me), xx));
+end:
+  if (xx) MP_DROP(xx);
+  return (rc);
+}
+
+static PyObject *grmeth_halftrace(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  mp *xx = 0;
+
+  if (!PyArg_ParseTuple(arg, "O&:halftrace", convgf, &xx)) goto end;
+  rc = gf_pywrap(gfreduce_halftrace(GFREDUCE_PY(me), MP_NEW, xx));
+end:
+  if (xx) MP_DROP(xx);
+  return (rc);
+}
+
+static PyObject *grmeth_sqrt(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  mp *xx = 0, *yy;
+
+  if (!PyArg_ParseTuple(arg, "O&:sqrt", convgf, &xx)) goto end;
+  if ((yy = gfreduce_sqrt(GFREDUCE_PY(me), MP_NEW, xx)) == 0)
+    VALERR("no modular square root");
+  rc = gf_pywrap(yy);
+end:
+  if (xx) MP_DROP(xx);
+  return (rc);
+}
+
+static PyObject *grmeth_quadsolve(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  mp *xx = 0, *yy;
+
+  if (!PyArg_ParseTuple(arg, "O&:quadsolve", convgf, &xx)) goto end;
+  if ((yy = gfreduce_quadsolve(GFREDUCE_PY(me), MP_NEW, xx)) == 0)
+    VALERR("no solution found");
+  rc = gf_pywrap(yy);
+end:
+  if (xx) MP_DROP(xx);
+  return (rc);
+}
+
 static PyObject *grmeth_reduce(PyObject *me, PyObject *arg)
 {
   PyObject *z = 0;
@@ -2036,14 +2289,18 @@ static PyGetSetDef gfreduce_pygetset[] = {
 static PyMethodDef gfreduce_pymethods[] = {
 #define METHNAME(name) grmeth_##name
   METH (reduce,        "R.reduce(X) -> X mod B.m")
+  METH (trace,        "R.trace(X) -> Tr(X) = x + x^2 + ... + x^{2^{m - 1}}")
+  METH (halftrace,   "R.halftrace(X) -> x + x^{2^2} + ... + x^{2^{m - 1}}")
+  METH (sqrt,          "R.sqrt(X) -> Y where Y^2 = X mod R")
+  METH (quadsolve,     "R.quadsolve(X) -> Y where Y^2 + Y = X mod R")
   METH (exp,           "R.exp(X, N) -> X^N mod B.m")
 #undef METHNAME
   { 0 }
 };
 
 static PyTypeObject *gfreduce_pytype, gfreduce_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.GFReduce",                 /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GFReduce",                          /* @tp_name@ */
   sizeof(gfreduce_pyobj),              /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -2073,7 +2330,7 @@ static PyTypeObject *gfreduce_pytype, gfreduce_pytype_skel = {
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   gfreduce_pymethods,                  /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   gfreduce_pygetset,                   /* @tp_getset@ */
@@ -2113,8 +2370,9 @@ static PyObject *gfn_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
                                   convgf, &p, convgf, &beta))
     goto end;
   gg = PyObject_New(gfn_pyobj, ty);
+  gg->p = 0;
   if (gfn_create(p, beta, &gg->ntop, &gg->pton)) {
-    FREEOBJ(gg);
+    Py_DECREF(gg);
     gg = 0;
     VALERR("can't invert transformation matrix");
   }
@@ -2146,7 +2404,7 @@ static PyObject *gfnget_beta(PyObject *me, void *hunoz)
   end:                                                                 \
     mp_drop(xx);                                                       \
     if (!z) return (0);                                                        \
-    return (mp_pywrap(z));                                             \
+    return (gf_pywrap(z));                                             \
   }
 XFORMOP(pton, PTON)
 XFORMOP(ntop, NTOP)
@@ -2154,8 +2412,11 @@ XFORMOP(ntop, NTOP)
 
 static void gfn_pydealloc(PyObject *me)
 {
-  gfn_destroy(GFN_PTON(me));
-  gfn_destroy(GFN_NTOP(me));
+  if (GFN_P(me)) {
+    MP_DROP(GFN_P(me));
+    gfn_destroy(GFN_PTON(me));
+    gfn_destroy(GFN_NTOP(me));
+  }
   FREEOBJ(me);
 }
 
@@ -2176,8 +2437,8 @@ static PyMethodDef gfn_pymethods[] = {
 };
 
 static PyTypeObject gfn_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.GFN",                      /* @tp_name@ */
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GFN",                               /* @tp_name@ */
   sizeof(gfn_pyobj),                   /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -2208,7 +2469,7 @@ and normal basis representations.",
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   gfn_pymethods,                       /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   gfn_pygetset,                                /* @tp_getset@ */
@@ -2228,18 +2489,22 @@ and normal basis representations.",
 
 static PyMethodDef methods[] = {
 #define METHNAME(func) meth_##func
-  KWMETH(_MP_fromstring,        "\
-fromstring(STR, radix = 0) -> (X, REST)\n\
+  KWMETH(_MP_fromstring,       "\
+fromstring(STR, [radix = 0]) -> (X, REST)\n\
 \n\
 Parse STR as a large integer, according to radix.  If radix is zero,\n\
 read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\
 or `R_' for other radix R.")
-  KWMETH(_GF_fromstring,        "\
-fromstring(STR, radix = 0) -> (X, REST)\n\
+  KWMETH(_GF_fromstring,       "\
+fromstring(STR, [radix = 0]) -> (X, REST)\n\
 \n\
 Parse STR as a binary polynomial, according to radix.  If radix is zero,\n\
 read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\
 or `R_' for other radix R.")
+  METH (_MP_factorial,         "\
+factorial(I) -> I!: compute factorial")
+  METH (_MP_fibonacci,         "\
+fibonacci(I) -> F(I): compute Fibonacci number")
   METH (_MP_loadl,             "\
 loadl(STR) -> X: read little-endian bytes")
   METH (_MP_loadb,             "\
@@ -2250,8 +2515,6 @@ loadl2c(STR) -> X: read little-endian bytes, two's complement")
 loadb2c(STR) -> X: read big-endian bytes, two's complement")
   METH (_MP_frombuf,           "\
 frombuf(STR) -> (X, REST): read buffer format")
-  METH (_MP_product,           "\
-product(ITER) -> X: product of things iterated over")
   METH (_GF_loadl,             "\
 loadl(STR) -> X: read little-endian bytes")
   METH (_GF_loadb,             "\
@@ -2266,6 +2529,7 @@ void mp_pyinit(void)
 {
   INITTYPE(mp, root);
   INITTYPE(gf, root);
+  INITTYPE(mpmul, root);
   INITTYPE(mpmont, root);
   INITTYPE(mpbarrett, root);
   INITTYPE(mpreduce, root);
@@ -2278,6 +2542,7 @@ void mp_pyinit(void)
 void mp_pyinsert(PyObject *mod)
 {
   INSERT("MP", mp_pytype);
+  INSERT("MPMul", mpmul_pytype);
   INSERT("MPMont", mpmont_pytype);
   INSERT("MPBarrett", mpbarrett_pytype);
   INSERT("MPReduce", mpreduce_pytype);