algorithms.c (AEADAAD.copy): Propagate the hashed length to the copy.
[catacomb-python] / mp.c
diff --git a/mp.c b/mp.c
index 2aa4cab..1448bc2 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -211,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);
@@ -527,9 +528,9 @@ static PyObject *mp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   mp *z;
   mp_pyobj *zz = 0;
   int radix = 0;
-  char *kwlist[] = { "x", "radix", 0 };
+  static const char *const kwlist[] = { "x", "radix", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", kwlist, &x, &radix))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", KWLIST, &x, &radix))
     goto end;
   if (MP_PYCHECK(x)) RETURN_OBJ(x);
   if (!good_radix_p(radix, 1)) VALERR("bad radix");
@@ -661,8 +662,8 @@ end:
 static PyObject *mpmeth_tostring(PyObject *me, PyObject *arg, PyObject *kw)
 {
   int radix = 10;
-  char *kwlist[] = { "radix", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", kwlist, &radix))
+  static const char *const kwlist[] = { "radix", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", KWLIST, &radix))
     goto end;
   if (!good_radix_p(radix, 0)) VALERR("bad radix");
   return (mp_topystring(MP_X(me), radix, 0, 0, 0));
@@ -702,11 +703,11 @@ end:
                                 PyObject *arg, PyObject *kw)           \
   {                                                                    \
     long len = -1;                                                     \
-    char *kwlist[] = { "len", 0 };                                     \
+    static const char *const kwlist[] = { "len", 0 };                                  \
     PyObject *rc = 0;                                                  \
                                                                        \
     if (!PyArg_ParseTupleAndKeywords(arg, kw, "|l:" #name,             \
-                                   kwlist, &len))                      \
+                                   KWLIST, &len))                      \
       goto end;                                                                \
     if (len < 0) {                                                     \
       len = mp_octets##c(MP_X(me));                                    \
@@ -765,10 +766,10 @@ static PyObject *mpmeth_tobuf(PyObject *me, PyObject *arg)
 static PyObject *mpmeth_primep(PyObject *me, PyObject *arg, PyObject *kw)
 {
   grand *r = &rand_global;
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   PyObject *rc = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", kwlist, convgrand, &r))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", KWLIST, convgrand, &r))
     goto end;
   rc = getbool(pgen_primep(MP_X(me), r));
 end:
@@ -809,14 +810,14 @@ static PyMethodDef mp_pymethods[] = {
   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")
-  KWMETH(storeb,       "X.storeb(len = -1) -> big-endian bytes")
+  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 }
@@ -896,7 +897,7 @@ 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 field elements, elliptic curve\n\
 points, group elements, Python `int' and `long' objects, and anything\n\
@@ -904,7 +905,7 @@ with an integer conversion.\n\
 \n\
 Notes:\n\
 \n\
-  * Use `//' for division.  MPs don't have `/' division.",
+  * Use `//' for integer division.  `/' gives exact rational division.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -936,10 +937,10 @@ static PyObject *meth__MP_fromstring(PyObject *me,
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
-  char *kwlist[] = { "class", "x", "radix", 0 };
+  static const char *const kwlist[] = { "class", "x", "radix", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
-                                  kwlist, &me, &p, &len, &r))
+                                  KWLIST, &me, &p, &len, &r))
     goto end;
   if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
@@ -1107,7 +1108,7 @@ static PyTypeObject *mpmul_pytype, mpmul_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"An object for multiplying many small integers.",
+"MPMul(N_0, N_1, ....): an object for multiplying many small integers.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1300,10 +1301,10 @@ static void mpmont_pydealloc(PyObject *me)
 static PyObject *mpmont_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpmont_pyobj *mm = 0;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx) || !MP_ODDP(xx)) VALERR("m must be positive and odd");
   mm = (mpmont_pyobj *)ty->tp_alloc(ty, 0);
@@ -1374,7 +1375,7 @@ static PyTypeObject *mpmont_pytype, mpmont_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A Montgomery reduction context.",
+"MPMont(N): a Montgomery reduction context.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1454,10 +1455,10 @@ static PyObject *mpbarrett_pynew(PyTypeObject *ty,
                                 PyObject *arg, PyObject *kw)
 {
   mpbarrett_pyobj *mb = 0;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx)) VALERR("m must be positive");
   mb = (mpbarrett_pyobj *)ty->tp_alloc(ty, 0);
@@ -1513,7 +1514,7 @@ static PyTypeObject *mpbarrett_pytype, mpbarrett_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A Barrett reduction context.",
+"MPBarrett(N): a Barrett reduction context.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1584,10 +1585,10 @@ static PyObject *mpreduce_pynew(PyTypeObject *ty,
 {
   mpreduce_pyobj *mr = 0;
   mpreduce r;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx)) VALERR("m must be positive");
   if (mpreduce_create(&r, xx)) VALERR("bad modulus (must be 2^k - ...)");
@@ -1641,7 +1642,7 @@ static PyTypeObject *mpreduce_pytype, mpreduce_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A reduction context for reduction modulo primes of special form.",
+"MPReduce(N): a reduction context for reduction modulo Solinas primes.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1679,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)
@@ -1688,7 +1689,8 @@ static PyObject *mcmeth_solve(PyObject *me, PyObject *arg)
     goto end;
   Py_INCREF(q);
   if (!PySequence_Check(q)) TYERR("want a sequence of residues");
-  if (PySequence_Size(q) != n) VALERR("residue count mismatch");
+  i = PySequence_Size(q); if (i < 0) goto end;
+  if (i != n) VALERR("residue count mismatch");
   v = xmalloc(n * sizeof(*v));
   for (i = 0; i < n; i++) {
     if ((x = PySequence_GetItem(q, i)) == 0) goto end;
@@ -1718,30 +1720,41 @@ static void mpcrt_pydealloc(PyObject *me)
 static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpcrt_mod *v = 0;
-  int n, i = 0;
-  char *kwlist[] = { "mv", 0 };
+  Py_ssize_t n, i = 0, j;
+  static const char *const 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)
     q = arg;
-  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &q))
+  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &q))
     goto end;
   Py_INCREF(q);
   if (!PySequence_Check(q)) TYERR("want a sequence of moduli");
-  n = PySequence_Size(q);
-  if (PyErr_Occurred()) goto end;
+  n = PySequence_Size(q); if (n < 0) goto end;
   if (!n) VALERR("want at least one modulus");
   v = xmalloc(n * sizeof(*v));
   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:
@@ -1752,6 +1765,7 @@ end:
     xfree(v);
   }
   Py_XDECREF(q);
+  mp_drop(xx); mp_drop(y); mp_drop(g);
   return (0);
 }
 
@@ -1810,7 +1824,7 @@ static PyTypeObject *mpcrt_pytype, mpcrt_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A context for the solution of Chinese Remainder Theorem problems.",
+"MPCRT(SEQ): a context for solving Chinese Remainder Theorem problems.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1870,9 +1884,9 @@ static PyObject *gf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   mp *z;
   mp_pyobj *zz = 0;
   int radix = 0;
-  char *kwlist[] = { "x", "radix", 0 };
+  static const char *const kwlist[] = { "x", "radix", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", kwlist, &x, &radix))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", KWLIST, &x, &radix))
     goto end;
   if (GF_PYCHECK(x)) RETURN_OBJ(x);
   if (!good_radix_p(radix, 1)) VALERR("radix out of range");
@@ -2003,13 +2017,13 @@ static PyMethodDef gf_pymethods[] = {
   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 }
@@ -2087,7 +2101,7 @@ 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 field elements, elliptic curve\n\
 points, group elements, Python `int' and `long' objects, and anything\n\
@@ -2098,7 +2112,7 @@ but it's much easier to type than `p2' or `c2' or whatever.\n\
 \n\
 Notes:\n\
 \n\
-  * Use `//' for division.  GFs don't have `/' division.",
+  * Use `//' for Euclidean division.  `/' gives exact rational division.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2130,10 +2144,10 @@ static PyObject *meth__GF_fromstring(PyObject *me,
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
-  char *kwlist[] = { "class", "x", "radix", 0 };
+  static const char *const kwlist[] = { "class", "x", "radix", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
-                                  kwlist, &me, &p, &len, &r))
+                                  KWLIST, &me, &p, &len, &r))
     goto end;
   if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
@@ -2248,10 +2262,10 @@ static PyObject *gfreduce_pynew(PyTypeObject *ty,
 {
   gfreduce_pyobj *mr = 0;
   gfreduce r;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convgf, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convgf, &xx))
     goto end;
   if (MP_ZEROP(xx)) ZDIVERR("modulus is zero!");
   gfreduce_create(&r, xx);
@@ -2309,7 +2323,7 @@ static PyTypeObject *gfreduce_pytype, gfreduce_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A reduction context for reduction modulo sparse irreducible polynomials.",
+"GFReduce(N): a context for reduction modulo sparse polynomials.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2350,14 +2364,15 @@ static PyObject *gfn_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mp *p = 0, *beta = 0;
   gfn_pyobj *gg = 0;
-  char *kwlist[] = { "p", "beta", 0 };
+  static const char *const kwlist[] = { "p", "beta", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   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");
   }
@@ -2389,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)
@@ -2397,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);
 }
 
@@ -2443,8 +2461,8 @@ static PyTypeObject gfn_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"An object for transforming elements of binary fields between polynomial\n\
-and normal basis representations.",
+"GFN(P, BETA): an object for transforming elements of binary fields\n\
+  between polynomial and normal basis representations.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2472,13 +2490,13 @@ and normal basis representations.",
 static PyMethodDef methods[] = {
 #define METHNAME(func) meth_##func
   KWMETH(_MP_fromstring,       "\
-fromstring(STR, radix = 0) -> (X, REST)\n\
+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\
+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\