chiark / gitweb /
*.c: Be more careful about `PySequence_Size'.
[catacomb-python] / mp.c
diff --git a/mp.c b/mp.c
index 2434eaf75f1147e54cfb25be55ac5a05066075d7..ab15d3fd10e115c476f1623e1eb162c37bb1d68f 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -163,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;
@@ -172,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:
@@ -209,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);
@@ -362,7 +365,7 @@ static PyObject *mp_pyid(PyObject *x) { RETURN_OBJ(x); }
     PyObject *z = 0;                                                   \
     long n;                                                            \
     if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL;                    \
-    if (mp_tolong_checked(yy, &n)) goto end;                           \
+    if (mp_tolong_checked(yy, &n, 1)) goto end;                                \
     if (n < 0)                                                         \
       z = pre##_pywrap(mp_##rname(MP_NEW, xx, -n));                    \
     else                                                               \
@@ -483,8 +486,8 @@ 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 (mp_topylong(MP_X(x))); }
@@ -542,13 +545,15 @@ end:
   return ((PyObject *)zz);
 }
 
-static long mp_pyhash(PyObject *me)
+long mphash(mp *x)
 {
-  long h;
-  PyObject *l = mp_topylong(MP_X(me)); h = PyObject_Hash(l);
+  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;
@@ -680,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)           \
@@ -711,7 +729,7 @@ STOREOP(storeb2c, 2c)
   {                                                                    \
     buf b;                                                             \
     char *p;                                                           \
-    int sz;                                                            \
+    Py_ssize_t sz;                                                     \
     PyObject *rc = 0;                                                  \
     mp *x;                                                             \
                                                                        \
@@ -778,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")
@@ -790,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 }
@@ -873,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\
@@ -909,7 +933,7 @@ static PyObject *meth__MP_fromstring(PyObject *me,
 {
   int r = 0;
   char *p;
-  int len;
+  Py_ssize_t len;
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
@@ -922,7 +946,8 @@ static PyObject *meth__MP_fromstring(PyObject *me,
   sc.buf = p; sc.lim = p + len;
   if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0)
     VALERR("bad integer");
-  z = Py_BuildValue("(Ns#)", mp_pywrap(zz), sc.buf, (int)(sc.lim - sc.buf));
+  z = Py_BuildValue("(Ns#)", mp_pywrap(zz),
+                   sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
 end:
   return (z);
 }
@@ -950,7 +975,7 @@ static PyObject *meth__MP_fibonacci(PyObject *me, PyObject *arg)
   static PyObject *meth__##py##_##name(PyObject *me, PyObject *arg)    \
   {                                                                    \
     char *p;                                                           \
-    int len;                                                           \
+    Py_ssize_t len;                                                    \
     if (!PyArg_ParseTuple(arg, "Os#:" #name, &me, &p, &len)) return (0); \
     return (pre##_pywrap(mp_##name(MP_NEW, p, len)));                  \
   }
@@ -1309,17 +1334,17 @@ 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 }
@@ -1458,7 +1483,7 @@ 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 }
@@ -1655,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)
@@ -1664,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;
@@ -1694,10 +1720,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)
@@ -1706,18 +1733,28 @@ static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
     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:
@@ -1728,6 +1765,7 @@ end:
     xfree(v);
   }
   Py_XDECREF(q);
+  mp_drop(xx); mp_drop(y); mp_drop(g);
   return (0);
 }
 
@@ -1867,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;
@@ -1988,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 }
@@ -2058,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@ */
@@ -2072,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\
@@ -2109,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;
@@ -2125,7 +2156,8 @@ static PyObject *meth__GF_fromstring(PyObject *me,
     if (zz) MP_DROP(zz);
     VALERR("bad binary polynomial");
   }
-  z = Py_BuildValue("(Ns#)", gf_pywrap(zz), sc.buf, (int)(sc.lim - sc.buf));
+  z = Py_BuildValue("(Ns#)", gf_pywrap(zz),
+                   sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
 end:
   return (z);
 }
@@ -2338,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");
   }
@@ -2371,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)
@@ -2379,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);
 }
 
@@ -2454,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\