chiark / gitweb /
Merge remote-tracking branch 'origin/HEAD'
[catacomb-python] / mp.c
diff --git a/mp.c b/mp.c
index e14b3a0d1d8a58b039b9a77bbc5abdcc82d59acb..88ee73813b538baa863097df8f07232a4fe66523 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:
@@ -214,7 +222,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 +269,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 = tomp(y)) == 0) {
+  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 = implicitgf(y)) == 0) {
     MP_DROP(*xx);
     return (-1);
   }
@@ -278,7 +319,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 +363,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 +384,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 +429,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 +485,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 +529,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);
@@ -505,10 +546,9 @@ end:
 
 static long mp_pyhash(PyObject *me)
 {
-  long i = mp_tolong(MP_X(me));
-  if (i == -1)
-    i = -2;
-  return (i);
+  long h;
+  PyObject *l = mp_topylong(MP_X(me)); h = PyObject_Hash(l);
+  Py_DECREF(l); return (h);
 }
 
 static PyObject *mpmeth_jacobi(PyObject *me, PyObject *arg)
@@ -526,27 +566,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 +662,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 +682,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 +726,7 @@ STOREOP(storeb2c, 2c)
   {                                                                    \
     buf b;                                                             \
     char *p;                                                           \
-    int sz;                                                            \
+    Py_ssize_t sz;                                                     \
     PyObject *rc = 0;                                                  \
     mp *x;                                                             \
                                                                        \
@@ -753,6 +805,8 @@ 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")
+  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")
@@ -810,8 +864,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@ */
 
@@ -851,7 +905,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 +922,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 +935,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 +1226,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 +1266,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 +1327,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 +1375,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 +1419,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 +1476,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 +1514,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 +1611,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 +1642,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@ */
@@ -1594,8 +1780,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 +1811,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 +1838,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 +1869,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);
@@ -1817,7 +2003,7 @@ 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")
@@ -1876,8 +2062,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@ */
 
@@ -1920,7 +2106,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 +2127,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 +2136,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 +2175,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 +2276,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 +2317,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@ */
@@ -2176,8 +2420,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 +2452,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 +2472,22 @@ and normal basis representations.",
 
 static PyMethodDef methods[] = {
 #define METHNAME(func) meth_##func
-  KWMETH(_MP_fromstring,        "\
+  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,        "\
+  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 +2498,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 +2512,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 +2525,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);