chiark / gitweb /
catacomb/__init__.py, mp.c: Use implicit-conversion rules for rationals.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 24 Nov 2019 00:50:18 +0000 (00:50 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 11:49:31 +0000 (12:49 +0100)
Export the implicit conversions for `MP' and `GF' to Python, and use
them in the `BaseRat' constructor.

catacomb/__init__.py
mp.c
t/t-mp.py
t/t-rat.py

index dc5ecbb44e5876223cc47c3c2865c3d3591a38ee..725f6d27acd9ef528ddf11e83b275e5847ac2000 100644 (file)
@@ -275,7 +275,7 @@ def secret_unbox(k, n, c):
 class BaseRat (object):
   """Base class implementing fields of fractions over Euclidean domains."""
   def __new__(cls, a, b):
-    a, b = cls.RING(a), cls.RING(b)
+    a, b = cls.RING._implicit(a), cls.RING._implicit(b)
     q, r = divmod(a, b)
     if r == cls.ZERO: return q
     g = b.gcd(r)
diff --git a/mp.c b/mp.c
index ce048197e24a79618d17e7545640344355e67f28..3abbcd5463b4f573193e77fa239d8c42c755c1c5 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -603,6 +603,22 @@ end:
   return ((PyObject *)zz);
 }
 
+#define IMPLICIT(pre)                                                  \
+  static PyObject *pre##meth__implicit(PyObject *me, PyObject *arg)    \
+  {                                                                    \
+    PyObject *x, *rc = 0;                                              \
+    mp *y = MP_NEW;                                                    \
+    if (!PyArg_ParseTuple(arg, "O:_implicit", &x)) goto end;           \
+    y = implicit##pre(x);                                              \
+    if (!y) TYERR("can't convert implicitly to " #pre);                        \
+    rc = pre##_pywrap(y);                                              \
+  end:                                                                 \
+    return (rc);                                                       \
+  }
+IMPLICIT(mp)
+IMPLICIT(gf)
+#undef IMPLICIT
+
 Py_hash_t mphash(mp *x)
 {
   PyObject *l = mp_topylong(x);
@@ -931,6 +947,7 @@ static const PyMethodDef mp_pymethods[] = {
     "  Parse STR as a large integer, according to RADIX.  If RADIX is\n"
     "  zero, read a prefix from STR to decide radix: allow `0b' for binary,\n"
     "  `0' or `0o' for octal, `0x' for hex, or `R_' for other radix R.")
+  SMTH (_implicit,     0)
   SMTH (factorial,     "factorial(I) -> I!: compute factorial")
   SMTH (fibonacci,     "fibonacci(I) -> F(I): compute Fibonacci number")
   SMTH (loadl,         "loadl(STR) -> X: read little-endian bytes")
@@ -2123,6 +2140,7 @@ static const PyMethodDef gf_pymethods[] = {
     "  Parse STR as a binary polynomial, according to RADIX.  If RADIX is\n"
     "  zero, read a prefix from STR to decide radix: allow `0b' for binary,\n"
     "  `0' or `0o' for octal, `0x' for hex, or `R_' for other radix R.")
+  SMTH (_implicit,     0)
   SMTH (loadl,         "loadl(STR) -> X: read little-endian bytes")
   SMTH (loadb,         "loadb(STR) -> X: read big-endian bytes")
   SMTH (frombuf,       "frombuf(STR) -> (X, REST): read buffer format")
index 24e80cd491085cfe3b8c8ded7bc1ba1466ad60e9..2cba8ec4b203a07cc4689f0c47816fb0c80d7e4a 100644 (file)
--- a/t/t-mp.py
+++ b/t/t-mp.py
@@ -156,7 +156,7 @@ class TestMP (U.TestCase):
 
   def test_strconv(me):
     x, y = C.MP(169), "24"
-    for fn in [T.add, T.sub]:
+    for fn in [T.add, T.sub, T.div]:
       me.assertRaises(TypeError, fn, x, y)
       me.assertRaises(TypeError, fn, y, x)
     me.assertEqual(x*y, 169*"24")
index 9fd15d412ef00b33f6198fe80147db8dc77d23ae..a2198bd239fab950ff973d4ecc64beea9889344b 100644 (file)
@@ -68,11 +68,20 @@ class TestRat (U.TestCase):
     me.assertFalse(b/a > c/a)
     me.assertTrue(c/a > b/a)
 
+    ## Conversions from string.
+    me.assertRaises(TypeError, T.add, f, "3")
+
   def test_intrat(me):
     me.check_rat(C.IntRat)
 
-    ## Check string conversions.
+    ## Check interaction with floating point.
     u = C.MP(5)/C.MP(4)
+    me.assertEqual(type(u + 0.0), float)
+    me.assertEqual(u, 1.25)
+    me.assertTrue(u < 1.26)
+    me.assertTrue(u > 1.24)
+
+    ## Check string conversions.
     me.assertEqual(str(u), "5/4")
     me.assertEqual(repr(u), "IntRat(5, 4)")
 
@@ -81,6 +90,7 @@ class TestRat (U.TestCase):
 
     ## Check string conversions.
     u = C.GF(5)/C.GF(4)
+    me.assertRaises(TypeError, T.add, u, 0.0)
     me.assertEqual(str(u), "0x5/0x4")
     me.assertEqual(repr(u), "GFRat(0x5, 0x4)")