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 `_\. +.. 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) {