@@@ py_buffer/freebin wip
[catacomb-python] / field.c
diff --git a/field.c b/field.c
index cce2e89..2e99efc 100644 (file)
--- a/field.c
+++ b/field.c
@@ -42,9 +42,9 @@ static PyObject *fe_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   PyObject *x;
   mp *z;
-  char *kwlist[] = { "x", 0 };
+  static const char *const kwlist[] = { "x", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", kwlist, &x))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", KWLIST, &x))
     return (0);
   if (FE_PYCHECK(x) && FE_F(x) == FIELD_F(ty)) RETURN_OBJ(x);
   if ((z = getmp(x)) == 0) return (0);
@@ -74,23 +74,23 @@ PyObject *field_pywrap(field *f)
 {
   PyTypeObject *ty;
 
-  if (strcmp(F_NAME(f), "prime") == 0) ty = primefield_pytype;
-  else if (strcmp(F_NAME(f), "niceprime") == 0) ty = niceprimefield_pytype;
-  else if (strcmp(F_NAME(f), "binpoly") == 0) ty = binpolyfield_pytype;
-  else if (strcmp(F_NAME(f), "binnorm") == 0) ty = binnormfield_pytype;
+  if (STRCMP(F_NAME(f), ==, "prime")) ty = primefield_pytype;
+  else if (STRCMP(F_NAME(f), ==, "niceprime")) ty = niceprimefield_pytype;
+  else if (STRCMP(F_NAME(f), ==, "binpoly")) ty = binpolyfield_pytype;
+  else if (STRCMP(F_NAME(f), ==, "binnorm")) ty = binnormfield_pytype;
   else abort();
   return (field_dopywrap(ty, f));
 }
 
 field *field_copy(field *f)
 {
-  if (strcmp(F_NAME(f), "prime") == 0)
+  if (STRCMP(F_NAME(f), ==, "prime"))
     f = field_prime(f->m);
-  else if (strcmp(F_NAME(f), "niceprime") == 0)
+  else if (STRCMP(F_NAME(f), ==, "niceprime"))
     f = field_niceprime(f->m);
-  else if (strcmp(F_NAME(f), "binpoly") == 0)
+  else if (STRCMP(F_NAME(f), ==, "binpoly"))
     f = field_binpoly(f->m);
-  else if (strcmp(F_NAME(f), "binnorm") == 0) {
+  else if (STRCMP(F_NAME(f), ==, "binnorm")) {
     fctx_binnorm *fc = (fctx_binnorm *)f;
     f = field_binnorm(f->m, fc->ntop.r[fc->ntop.n - 1]);
   } else
@@ -107,33 +107,18 @@ PyObject *fe_pywrap(PyObject *fobj, mp *x)
   return ((PyObject *)z);
 }
 
-static mp *tofe(field *f, PyObject *o)
+static mp *implicitfe(field *f, PyObject *o)
 {
-  mp *x = 0, *y = 0;
-
-  if (FE_PYCHECK(o)) {
-    if (FE_F(o) != f && !field_samep(FE_F(o), f)) return (0);
-    y = MP_COPY(FE_X(o));
-  } else if ((x = tomp(o)) != 0) {
-    if (MP_ZEROP(x))
-      y = MP_COPY(f->zero);
-    else if (MP_EQ(x, MP_ONE))
-      y = MP_COPY(f->one);
-    else
-      y = F_IN(f, MP_NEW, x);
-    MP_DROP(x);
-  }
-  return (y);
-}
+  mp *x;
 
-mp *getfe(field *f, PyObject *o)
-{
-  mp *x = 0;
-  if ((x = tofe(f, o)) == 0) {
-    PyErr_Format(PyExc_TypeError, "can't convert %.100s to fe",
-                o->ob_type->tp_name);
+  if (FE_PYCHECK(o) && FE_F(o) == f) return (MP_COPY(FE_X(o)));
+  switch (f->ops->ty) {
+    case FTY_PRIME: x = implicitmp(o); break;
+    case FTY_BINARY: x = implicitgf(o); break;
+    default: assert(!"huh?");
   }
-  return (x);
+  if (!x) return (0);
+  return (F_IN(f, MP_NEW, x));
 }
 
 /*----- Field elements ----------------------------------------------------*/
@@ -145,9 +130,9 @@ static int febinop(PyObject *x, PyObject *y,
   else if (FE_PYCHECK(y)) *fobj = FE_FOBJ(y);
   else return (-1);
   *f = FIELD_F(*fobj);
-  if ((*xx = tofe(*f, x)) == 0)
+  if ((*xx = implicitfe(*f, x)) == 0)
     return (-1);
-  if ((*yy = tofe(*f, y)) == 0) {
+  if ((*yy = implicitfe(*f, y)) == 0) {
     MP_DROP(*xx);
     return (-1);
   }
@@ -189,7 +174,10 @@ static PyObject *fe_pyexp(PyObject *x, PyObject *y, PyObject *z)
   field *ff;
   mp *xx, *yy;
 
-  if (z != Py_None || !FE_PYCHECK(x) || (yy = tomp(y)) == 0)
+  if (z != Py_None || !FE_PYCHECK(x)) RETURN_NOTIMPL;
+  if (FE_PYCHECK(y) && FE_F(y)->ops->ty == FTY_PRIME)
+    yy = F_OUT(FE_F(y), MP_NEW, FE_X(y));
+  else if ((yy = implicitmp(y)) == 0)
     RETURN_NOTIMPL;
   ff = FE_F(x); xx = FE_X(x); MP_COPY(xx);
   if (MP_NEGP(yy) && F_ZEROP(ff, xx)) ZDIVERR("division by zero");
@@ -228,17 +216,10 @@ end:
   return (rc);
 }
 
-static long fe_pyhash(PyObject *me)
-{
-  size_t sz = FE_F(me)->noctets;
-  uint32 h = 0xe0c127ca + FE_F(me)->ops->ty;
-  octet *p = xmalloc(sz);
-  mp_storeb(FE_X(me), p, sz);
-  h = unihash_hash(&unihash_global, h, p, sz);
-  xfree(p);
-  return (h % LONG_MAX);
-}
+static Py_hash_t fe_pyhash(PyObject *me)
+  { return (mphash(FE_X(me))); }
 
+#ifdef PY2
 static int fe_pycoerce(PyObject **x, PyObject **y)
 {
   mp *z;
@@ -249,7 +230,7 @@ static int fe_pycoerce(PyObject **x, PyObject **y)
     Py_INCREF(*x); Py_INCREF(*y);
     return (0);
   }
-  if ((z = tofe(FE_F(*x), *y)) != 0) {
+  if ((z = implicitfe(FE_F(*x), *y)) != 0) {
     Py_INCREF(*x);
     *y = fe_pywrap(FE_FOBJ(*x), z);
     return (0);
@@ -259,16 +240,20 @@ static int fe_pycoerce(PyObject **x, PyObject **y)
 end:
   return (-1);
 }
+#endif
 
 static PyObject *fe_pyint(PyObject *x)
 {
   long l;
+  PyObject *rc;
   mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));
-  if (mp_tolong_checked(xx, &l)) { MP_DROP(xx); return (0); }
+  if (!mp_tolong_checked(xx, &l, 0)) rc = PyInt_FromLong(l);
+  else rc = mp_topylong(xx);
   MP_DROP(xx);
-  return (PyInt_FromLong(l));
+  return (rc);
 }
 
+#ifdef PY2
 static PyObject *fe_pylong(PyObject *x)
 {
   mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));
@@ -276,15 +261,18 @@ static PyObject *fe_pylong(PyObject *x)
   MP_DROP(xx);
   return (rc);
 }
+#endif
 
 #define BASEOP(name, radix, pre)                                       \
   static PyObject *fe_py##name(PyObject *x) {                          \
     mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));                          \
-    PyObject *rc = mp_topystring(FE_X(x), radix, 0, pre, 0);           \
+    PyObject *rc = mp_topystring(xx, radix, 0, pre, 0);                        \
     MP_DROP(xx);                                                       \
     return (rc);                                                       \
   }
+#ifdef PY2
 BASEOP(oct, 8, "0");
+#endif
 BASEOP(hex, 16, "0x");
 #undef BASEOP
 
@@ -296,10 +284,9 @@ static void fe_pydealloc(PyObject *me)
 }
 
 #define UNOP(name, check)                                              \
-  static PyObject *femeth_##name(PyObject *me, PyObject *arg) {                \
+  static PyObject *femeth_##name(PyObject *me) {                       \
     field *f = FE_F(me);                                               \
     mp *x = FE_X(me);                                                  \
-    if (!PyArg_ParseTuple(arg, ":" #name)) return (0);                 \
     if (!f->ops->name) TYERR(#name " not supported for this field");   \
     check                                                              \
     x = f->ops->name(f, MP_NEW, x);                                    \
@@ -318,9 +305,6 @@ UNOP(qdl, ; )
 UNOP(hlv, ; )
 #undef UNOP
 
-static PyObject *feget_field(PyObject *me, void *hunoz)
-  { RETURN_OBJ(FE_FOBJ(me)); }
-
 static PyObject *feget_value(PyObject *me, void *hunoz)
 {
   mp *x = F_OUT(FE_F(me), MP_NEW, FE_X(me));
@@ -340,34 +324,43 @@ static PyObject *feget__value(PyObject *me, void *hunoz)
     return (mp_pywrap(x));
 }
 
-static PyGetSetDef fe_pygetset[] = {
+static const PyMemberDef fe_pymembers[] = {
+#define MEMBERSTRUCT fe_pyobj
+  MEMRNM(field, T_OBJECT, PY23(ob_type, ob_base.ob_type), READONLY,
+                       "X.field -> field containing X")
+#undef MEMBERSTRUCT
+  { 0 }
+};
+
+static const PyGetSetDef fe_pygetset[] = {
 #define GETSETNAME(op, name) fe##op##_##name
-  GET  (field,         "X.field -> field containing X")
-  GET  (value,         "X.value -> `natural' integer representation of X")
-  GET  (_value,        "X._value -> internal integer representation of X")
+  GET  (value,         "X.value -> `natural' MP/GF representation of X")
+  GET  (_value,        "X._value -> internal MP/GF representation of X")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyMethodDef fe_pymethods[] = {
+static const PyMethodDef fe_pymethods[] = {
 #define METHNAME(func) femeth_##func
-  METH (inv,           "X.inv() -> X^{-1}")
-  METH (sqr,           "X.sqr() -> X^2")
-  METH (sqrt,          "X.sqrt() -> sqrt(X)")
-  METH (quadsolve,     "X.quadsolve() -> Y where Y^2 + Y = X (binary only)")
-  METH (dbl,           "X.dbl() -> 2 * X (prime only)")
-  METH (tpl,           "X.tpl() -> 3 * X (prime only)")
-  METH (qdl,           "X.qdl() -> 4 * X (prime only)")
-  METH (hlv,           "X.hlv() -> X/2 (prime only)")
+  NAMETH(inv,          "X.inv() -> X^{-1}")
+  NAMETH(sqr,          "X.sqr() -> X^2")
+  NAMETH(sqrt,         "X.sqrt() -> sqrt(X)")
+  NAMETH(quadsolve,    "X.quadsolve() -> Y where Y^2 + Y = X (binary only)")
+  NAMETH(dbl,          "X.dbl() -> 2 * X (prime only)")
+  NAMETH(tpl,          "X.tpl() -> 3 * X (prime only)")
+  NAMETH(qdl,          "X.qdl() -> 4 * X (prime only)")
+  NAMETH(hlv,          "X.hlv() -> X/2 (prime only)")
 #undef METHNAME
   { 0 }
 };
 
-static PyNumberMethods fe_pynumber = {
+static const PyNumberMethods fe_pynumber = {
   fe_pyadd,                            /* @nb_add@ */
   fe_pysub,                            /* @nb_subtract@ */
   fe_pymul,                            /* @nb_multiply@ */
+#ifdef PY2
   fe_pydiv,                            /* @nb_divide@ */
+#endif
   0,                                   /* @nb_remainder@ */
   0,                                   /* @nb_divmod@ */
   fe_pyexp,                            /* @nb_power@ */
@@ -381,17 +374,23 @@ static PyNumberMethods fe_pynumber = {
   0,                                   /* @nb_and@ */
   0,                                   /* @nb_xor@ */
   0,                                   /* @nb_or@ */
+#ifdef PY2
   fe_pycoerce,                         /* @nb_coerce@ */
+#endif
   fe_pyint,                            /* @nb_int@ */
-  fe_pylong,                           /* @nb_long@ */
+  PY23(fe_pylong, 0),                  /* @nb_long@ */
   0 /* meaningless */,                 /* @nb_float@ */
+#ifdef PY2
   fe_pyoct,                            /* @nb_oct@ */
   fe_pyhex,                            /* @nb_hex@ */
+#endif
 
   0,                                   /* @nb_inplace_add@ */
   0,                                   /* @nb_inplace_subtract@ */
   0,                                   /* @nb_inplace_multiply@ */
+#ifdef PY2
   0,                                   /* @nb_inplace_divide@ */
+#endif
   0,                                   /* @nb_inplace_remainder@ */
   0,                                   /* @nb_inplace_power@ */
   0,                                   /* @nb_inplace_lshift@ */
@@ -404,10 +403,12 @@ static PyNumberMethods fe_pynumber = {
   fe_pydiv,                            /* @nb_true_divide@ */
   0,                                   /* @nb_inplace_floor_divide@ */
   0,                                   /* @nb_inplace_true_divide@ */
+
+  fe_pyint,                            /* @nb_index@ */
 };
 
-static PyTypeObject fe_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject fe_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "FE",                                        /* @tp_name@ */
   sizeof(fe_pyobj),                    /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -418,7 +419,7 @@ static PyTypeObject fe_pytype_skel = {
   0,                                   /* @tp_setattr@ */
   0,                                   /* @tp_compare@ */
   0,                                   /* @tp_repr@ */
-  &fe_pynumber,                                /* @tp_as_number@ */
+  PYNUMBER(fe),                                /* @tp_as_number@ */
   0,                                   /* @tp_as_sequence@ */
   0,                                   /* @tp_as_mapping@ */
   fe_pyhash,                           /* @tp_hash@ */
@@ -432,7 +433,7 @@ static PyTypeObject fe_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Finite field elements, abstract base class.",
+  "Finite field elements, abstract base class.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -440,9 +441,9 @@ static PyTypeObject fe_pytype_skel = {
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
-  fe_pymethods,                                /* @tp_methods@ */
-  0,                                   /* @tp_members@ */
-  fe_pygetset,                         /* @tp_getset@ */
+  PYMETHODS(fe),                       /* @tp_methods@ */
+  PYMEMBERS(fe),                       /* @tp_members@ */
+  PYGETSET(fe),                                /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -472,10 +473,10 @@ end:
 
 static PyObject *fmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = &rand_global;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
                                   convgrand, &r))
     return (0);
   return (fe_pywrap(me, F_RAND(FIELD_F(me), MP_NEW, r)));
@@ -488,6 +489,21 @@ static PyObject *fmeth__adopt(PyObject *me, PyObject *arg)
   return (fe_pywrap(me, xx));
 }
 
+static PyObject *fmeth_parse(PyObject *me, PyObject *arg)
+{
+  field *f;
+  char *p;
+  PyObject *rc = 0;
+  qd_parse qd;
+
+  if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+  qd.p = p; qd.e = 0;
+  if ((f = field_parse(&qd)) == 0) VALERR(qd.e);
+  rc = Py_BuildValue("(Ns)", field_pywrap(f), qd.p);
+end:
+  return (rc);
+}
+
 static void field_pydealloc(PyObject *me)
 {
   F_DESTROY(FIELD_F(me));
@@ -510,12 +526,12 @@ static PyObject *fget_noctets(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(FIELD_F(me)->noctets)); }
 
 static PyObject *fget_name(PyObject *me, void *hunoz)
-  { return (PyString_FromString(F_NAME(FIELD_F(me)))); }
+  { return (TEXT_FROMSTR(F_NAME(FIELD_F(me)))); }
 
 static PyObject *fget_type(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(F_TYPE(FIELD_F(me)))); }
 
-static PyGetSetDef field_pygetset[] = {
+static const PyGetSetDef field_pygetset[] = {
 #define GETSETNAME(op, name) f##op##_##name
   GET  (zero,          "F.zero -> field additive identity")
   GET  (one,           "F.one -> field multiplicative identity")
@@ -528,16 +544,17 @@ static PyGetSetDef field_pygetset[] = {
   { 0 }
 };
 
-static PyMethodDef field_pymethods[] = {
+static const PyMethodDef field_pymethods[] = {
 #define METHNAME(name) fmeth_##name
   METH (_adopt,        "F._adopt(X) -> FE")
-  KWMETH(rand,         "F.rand(rng = rand) -> FE, uniformly distributed")
+  KWMETH(rand,         "F.rand([rng = rand]) -> FE, uniformly distributed")
+  SMTH (parse,         "parse(STR) -> F, REST")
 #undef METHNAME
   { 0 }
 };
 
-static PyTypeObject field_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject field_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "Field",                             /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -561,7 +578,7 @@ static PyTypeObject field_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"An abstract field.  This is an abstract type.",
+  "An abstract field.  This is an abstract type.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -569,9 +586,9 @@ static PyTypeObject field_pytype_skel = {
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
-  field_pymethods,                     /* @tp_methods@ */
+  PYMETHODS(field),                    /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  field_pygetset,                      /* @tp_getset@ */
+  PYGETSET(field),                     /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -591,9 +608,9 @@ static PyObject *primefield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", KWLIST,
                                   convmp, &xx))
     goto end;
   if ((f = field_prime(xx)) == 0)
@@ -608,14 +625,14 @@ end:
 static PyObject *pfget_p(PyObject *me, void *hunoz)
   { return (mp_pywrap(MP_COPY(FIELD_F(me)->m))); }
 
-static PyGetSetDef primefield_pygetset[] = {
+static const PyGetSetDef primefield_pygetset[] = {
 #define GETSETNAME(op, name) pf##op##_##name
   GET  (p,             "F.p -> prime field characteristic")
 #undef GETSETNAME
 };
 
-static PyTypeObject primefield_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject primefield_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "PrimeField",                                /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -639,17 +656,17 @@ static PyTypeObject primefield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Prime fields.",
+  "PrimeField(P): prime fields.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  field_pyrichcompare,                 /* @tp_richcompare@ */
+  0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  primefield_pygetset,                 /* @tp_getset@ */
+  PYGETSET(primefield),                        /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -667,10 +684,10 @@ static PyObject *niceprimefield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:niceprimefield",
-                                  kwlist, convmp, &xx))
+                                  KWLIST, convmp, &xx))
     goto end;
   if ((f = field_niceprime(xx)) == 0)
     VALERR("bad prime for niceprimefield");
@@ -681,8 +698,8 @@ end:
   return (0);
 }
 
-static PyTypeObject niceprimefield_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject niceprimefield_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "NicePrimeField",                    /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -706,11 +723,11 @@ static PyTypeObject niceprimefield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Nice prime fields.",
+  "NicePrimeField(P): prime field using Solinas reduction.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  field_pyrichcompare,                 /* @tp_richcompare@ */
+  0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
@@ -734,15 +751,19 @@ static PyTypeObject niceprimefield_pytype_skel = {
 static PyObject *bfget_m(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(FIELD_F(me)->nbits)); }
 
-static PyGetSetDef binfield_pygetset[] = {
+static PyObject *bfget_p(PyObject *me, void *hunoz)
+  { return (gf_pywrap(MP_COPY(FIELD_F(me)->m))); }
+
+static const PyGetSetDef binfield_pygetset[] = {
 #define GETSETNAME(op, name) bf##op##_##name
   GET  (m,             "F.m -> field polynomial degree")
+  GET  (p,             "F.p -> field polynomial")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyTypeObject binfield_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject binfield_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "BinField",                          /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -770,13 +791,13 @@ static PyTypeObject binfield_pytype_skel = {
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  field_pyrichcompare,                 /* @tp_richcompare@ */
+  0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  binfield_pygetset,                   /* @tp_getset@ */
+  PYGETSET(binfield),                  /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -794,9 +815,9 @@ static PyObject *binpolyfield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", KWLIST,
                                   convgf, &xx))
     goto end;
   if ((f = field_binpoly(xx)) == 0) VALERR("bad poly for binpolyfield");
@@ -807,15 +828,8 @@ end:
   return (0);
 }
 
-static PyGetSetDef binpolyfield_pygetset[] = {
-#define GETSETNAME(op, name) pf##op##_##name
-  GET  (p,             "F.p -> field polynomial")
-#undef GETSETNAME
-  { 0 }
-};
-
-static PyTypeObject binpolyfield_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject binpolyfield_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "BinPolyField",                      /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -839,17 +853,17 @@ static PyTypeObject binpolyfield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary fields with polynomial basis representation.",
+  "BinPolyField(P): binary fields with polynomial basis representation.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  field_pyrichcompare,                 /* @tp_richcompare@ */
+  0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  binpolyfield_pygetset,               /* @tp_getset@ */
+  0,                                   /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -867,10 +881,10 @@ static PyObject *binnormfield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0, *yy = 0;
   field *f;
-  char *kwlist[] = { "p", "beta", 0 };
+  static const char *const kwlist[] = { "p", "beta", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:binnormfield",
-                                  kwlist, convgf, &xx, convgf, &yy))
+                                  KWLIST, convgf, &xx, convgf, &yy))
     goto end;
   if ((f = field_binnorm(xx, yy)) == 0) VALERR("bad args for binnormfield");
   MP_DROP(xx); MP_DROP(yy);
@@ -886,18 +900,15 @@ static PyObject *bnfget_beta(PyObject *me, void *hunoz)
   return (gf_pywrap(MP_COPY(fc->ntop.r[fc->ntop.n - 1])));
 }
 
-static PyGetSetDef binnormfield_pygetset[] = {
-#define GETSETNAME(op, name) pf##op##_##name
-  GET  (p,             "F.p -> field polynomial")
-#undef GETSETNAME
+static const PyGetSetDef binnormfield_pygetset[] = {
 #define GETSETNAME(op, name) bnf##op##_##name
   GET  (beta,          "F.beta -> conversion factor")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyTypeObject binnormfield_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
+static const PyTypeObject binnormfield_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
   "BinNormField",                      /* @tp_name@ */
   sizeof(field_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
@@ -921,17 +932,17 @@ static PyTypeObject binnormfield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary fields with normal basis representation.",
+  "BinNormField(P, BETA): binary fields with normal basis representation.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  field_pyrichcompare,                 /* @tp_richcompare@ */
+  0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  binnormfield_pygetset,               /* @tp_getset@ */
+  PYGETSET(binnormfield),              /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -946,28 +957,8 @@ static PyTypeObject binnormfield_pytype_skel = {
 
 /*----- Setup -------------------------------------------------------------*/
 
-static PyObject *meth__Field_parse(PyObject *me, PyObject *arg)
-{
-  field *f;
-  char *p;
-  PyObject *rc = 0;
-  qd_parse qd;
-
-  if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p))
-    goto end;
-  qd.p = p;
-  qd.e = 0;
-  if ((f = field_parse(&qd)) == 0)
-    SYNERR(qd.e);
-  rc = Py_BuildValue("(Ns)", field_pywrap(f), qd.p);
-end:
-  return (rc);
-}
-
-static PyMethodDef methods[] = {
-#define METHNAME(func) meth_##func
-  METH (_Field_parse,          "parse(STR) -> F, REST")
-#undef METHNAME
+static const struct nameval consts[] = {
+  CONST(FTY_PRIME), CONST(FTY_BINARY),
   { 0 }
 };
 
@@ -980,7 +971,6 @@ void field_pyinit(void)
   INITTYPE(binfield, field);
   INITTYPE(binpolyfield, binfield);
   INITTYPE(binnormfield, binfield);
-  addmethods(methods);
 }
 
 void field_pyinsert(PyObject *mod)
@@ -992,6 +982,7 @@ void field_pyinsert(PyObject *mod)
   INSERT("BinField", binfield_pytype);
   INSERT("BinPolyField", binpolyfield_pytype);
   INSERT("BinNormField", binnormfield_pytype);
+  setconstants(mod, consts);
 }
 
 /*----- That's all, folks -------------------------------------------------*/