Serhiy Storchaka added the comment:
And for comparison here is simpler patch with Euclidean algorithm.
----------
Added file: http://bugs.python.org/file36742/euclidean_gcd.patch
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue22486>
_______________________________________
diff -r e9d4288c32de Doc/library/math.rst
--- a/Doc/library/math.rst Wed Sep 24 13:29:27 2014 +0300
+++ b/Doc/library/math.rst Fri Sep 26 21:37:23 2014 +0300
@@ -100,6 +100,14 @@ Number-theoretic and representation func
<http://code.activestate.com/recipes/393090/>`_\.
+.. function:: gcd(a, b)
+
+ Return the greatest common divisor of the integers *a* and *b*. If either
+ *a* or *b* is nonzero, then the value of ``gcd(a, b)`` is the largest
+ positive integer that divides both *a* and *b*. ``gcd(0, 0)`` returns
+ ``0``.
+
+
.. function:: isfinite(x)
Return ``True`` if *x* is neither an infinity nor a NaN, and
diff -r e9d4288c32de Include/longobject.h
--- a/Include/longobject.h Wed Sep 24 13:29:27 2014 +0300
+++ b/Include/longobject.h Fri Sep 26 21:37:23 2014 +0300
@@ -198,6 +198,9 @@ PyAPI_FUNC(int) _PyLong_FormatAdvancedWr
PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int);
PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int);
+/* For use by the gcd function in mathmodule.c */
+PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *);
+
#ifdef __cplusplus
}
#endif
diff -r e9d4288c32de Lib/fractions.py
--- a/Lib/fractions.py Wed Sep 24 13:29:27 2014 +0300
+++ b/Lib/fractions.py Sat Sep 27 11:09:15 2014 +0300
@@ -174,9 +174,12 @@ class Fraction(numbers.Rational):
if denominator == 0:
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
if _normalize:
- g = gcd(numerator, denominator)
+ g = math.gcd(numerator, denominator)
numerator //= g
denominator //= g
+ if denominator < 0:
+ numerator = -numerator
+ denominator = -denominator
self._numerator = numerator
self._denominator = denominator
return self
diff -r e9d4288c32de Lib/test/test_math.py
--- a/Lib/test/test_math.py Wed Sep 24 13:29:27 2014 +0300
+++ b/Lib/test/test_math.py Sat Sep 27 11:09:15 2014 +0300
@@ -595,6 +595,45 @@ class MathTests(unittest.TestCase):
s = msum(vals)
self.assertEqual(msum(vals), math.fsum(vals))
+ def testGcd(self):
+ gcd = math.gcd
+ self.assertEqual(gcd(0, 0), 0)
+ self.assertEqual(gcd(1, 0), 1)
+ self.assertEqual(gcd(-1, 0), 1)
+ self.assertEqual(gcd(0, 1), 1)
+ self.assertEqual(gcd(0, -1), 1)
+ self.assertEqual(gcd(7, 1), 1)
+ self.assertEqual(gcd(7, -1), 1)
+ self.assertEqual(gcd(-23, 15), 1)
+ self.assertEqual(gcd(120, 84), 12)
+ self.assertEqual(gcd(84, -120), 12)
+ self.assertEqual(gcd(1216342683557601535506311712,
+ 436522681849110124616458784), 32)
+ c = 652560
+ x = 434610456570399902378880679233098819019853229470286994367836600566
+ y = 1064502245825115327754847244914921553977
+ a = x * c
+ b = y * c
+ self.assertEqual(gcd(a, b), c)
+ self.assertEqual(gcd(b, a), c)
+ self.assertEqual(gcd(-a, b), c)
+ self.assertEqual(gcd(b, -a), c)
+ self.assertEqual(gcd(a, -b), c)
+ self.assertEqual(gcd(-b, a), c)
+ self.assertEqual(gcd(-a, -b), c)
+ self.assertEqual(gcd(-b, -a), c)
+ c = 576559230871654959816130551884856912003141446781646602790216406874
+ a = x * c
+ b = y * c
+ self.assertEqual(gcd(a, b), c)
+ self.assertEqual(gcd(b, a), c)
+ self.assertEqual(gcd(-a, b), c)
+ self.assertEqual(gcd(b, -a), c)
+ self.assertEqual(gcd(a, -b), c)
+ self.assertEqual(gcd(-b, a), c)
+ self.assertEqual(gcd(-a, -b), c)
+ self.assertEqual(gcd(-b, -a), c)
+
def testHypot(self):
self.assertRaises(TypeError, math.hypot)
self.ftest('hypot(0,0)', math.hypot(0,0), 0)
diff -r e9d4288c32de Modules/mathmodule.c
--- a/Modules/mathmodule.c Wed Sep 24 13:29:27 2014 +0300
+++ b/Modules/mathmodule.c Fri Sep 26 21:37:23 2014 +0300
@@ -656,6 +656,22 @@ m_log10(double x)
}
+static PyObject *
+math_gcd(PyObject *self, PyObject *args)
+{
+ PyObject *a, *b;
+
+ if (!PyArg_ParseTuple(args, "O!O!:gcd", &PyLong_Type, &a, &PyLong_Type,
&b))
+ return NULL;
+
+ return _PyLong_GCD(a, b);
+}
+
+PyDoc_STRVAR(math_gcd_doc,
+"gcd(x, y) -> int\n\
+greatest common divisor of x and y");
+
+
/* Call is_error when errno != 0, and where x is the result libm
* returned. is_error will usually set up an exception and return
* true (1), but may return false (0) without setting up an exception.
@@ -1958,6 +1974,7 @@ static PyMethodDef math_methods[] = {
{"frexp", math_frexp, METH_O, math_frexp_doc},
{"fsum", math_fsum, METH_O, math_fsum_doc},
{"gamma", math_gamma, METH_O, math_gamma_doc},
+ {"gcd", math_gcd, METH_VARARGS, math_gcd_doc},
{"hypot", math_hypot, METH_VARARGS, math_hypot_doc},
{"isfinite", math_isfinite, METH_O, math_isfinite_doc},
{"isinf", math_isinf, METH_O, math_isinf_doc},
diff -r e9d4288c32de Objects/longobject.c
--- a/Objects/longobject.c Wed Sep 24 13:29:27 2014 +0300
+++ b/Objects/longobject.c Fri Sep 26 21:37:23 2014 +0300
@@ -4327,6 +4327,80 @@ long_long(PyObject *v)
return v;
}
+PyObject *
+_PyLong_GCD(PyObject *a, PyObject *b)
+{
+ PyObject *e;
+ long x, y, t;
+ int overflow;
+
+ x = PyLong_AsLongAndOverflow(a, &overflow);
+ if (x == -1 && PyErr_Occurred())
+ return NULL;
+ if (!overflow && x >= -LONG_MAX) {
+ y = PyLong_AsLongAndOverflow(b, &overflow);
+ if (y == -1 && PyErr_Occurred())
+ return NULL;
+ if (!overflow && y >= -LONG_MAX) {
+ x = Py_ABS(x);
+ y = Py_ABS(y);
+ goto fastloop;
+ }
+ }
+
+ /* Initial reduction: make sure that 0 <= b <= a. */
+ a = long_abs((PyLongObject *)a);
+ if (a == NULL)
+ return NULL;
+ b = long_abs((PyLongObject *)b);
+ if (b == NULL) {
+ Py_DECREF(a);
+ return NULL;
+ }
+ if (long_compare((PyLongObject *)a, (PyLongObject *)b) < 0) {
+ e = a;
+ a = b;
+ b = e;
+ }
+ /* We now own references to a and b */
+
+ /* reduce until a fits into longs or b == 0 */
+ while (1) {
+ if (!Py_SIZE(b)) {
+ Py_DECREF(b);
+ return a;
+ }
+ x = PyLong_AsLongAndOverflow(a, &overflow);
+ assert(x != -1 || !PyErr_Occurred());
+ if (!overflow)
+ break;
+
+ if (l_divmod((PyLongObject *)a, (PyLongObject *)b,
+ NULL, (PyLongObject **)&e) < 0) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+ Py_DECREF(a);
+ a = b;
+ b = e;
+ }
+ Py_DECREF(a);
+ y = PyLong_AsLongAndOverflow(b, &overflow);
+ Py_DECREF(b);
+ if (y == -1 && PyErr_Occurred())
+ return NULL;
+
+fastloop:
+ /* usual Euclidean algorithm for longs */
+ while (y != 0) {
+ t = y;
+ y = x % y;
+ x = t;
+ }
+ return PyLong_FromLong(x);
+}
+
static PyObject *
long_float(PyObject *v)
{
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com