From e6a4949b2b9f28fe45ecf831b0fca96ff5da5ed6 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 28 Jul 2018 20:09:10 -0700 Subject: [PATCH 01/11] Stub function --- Modules/mathmodule.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 726fc562129e46..f5652a2a3f0eec 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2031,6 +2031,13 @@ math_fmod_impl(PyObject *module, double x, double y) return PyFloat_FromDouble(r); } +/* AC: XXX Todo */ +static PyObject * +math_dist(PyObject *self, PyObject *args) +{ + return PyFloat_FromDouble(42.0); +} + /* AC: cannot convert yet, waiting for *args support */ static PyObject * math_hypot(PyObject *self, PyObject *args) @@ -2358,6 +2365,7 @@ static PyMethodDef math_methods[] = { {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, MATH_DEGREES_METHODDEF + {"dist", math_dist, METH_VARARGS, math_remainder_doc}, // XXX fix doc, then convert to arg clinic {"erf", math_erf, METH_O, math_erf_doc}, {"erfc", math_erfc, METH_O, math_erfc_doc}, {"exp", math_exp, METH_O, math_exp_doc}, From f0d474efbc54d786fda10708dc16bbaaa1a54ff5 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 29 Jul 2018 17:56:45 -0700 Subject: [PATCH 02/11] Acquire input arguments --- Lib/test/test_math.py | 30 ++++++++++++++++++++++++++++++ Modules/mathmodule.c | 19 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 484389921a6916..583ac6f0aaa46d 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -787,6 +787,36 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) + def testDist(self): + from decimal import Decimal + from fractions import Fraction + + dist = math.dist + + # Base case + self.assertEqual(dist((1, 2, 3), (4, 5, 6)), 3.0) + + # Test handling of bad arguments + with self.assertRaises(TypeError): # Reject keyword args + dist(p=(1, 2, 3), q=(4, 5, 6)) + with self.assertRaises(TypeError): # Too few args + dist((1, 2, 3)) + with self.assertRaises(TypeError): # Too many args + dist((1, 2, 3), (4, 5, 6), (7, 8, 9)) + with self.assertRaises(TypeError): # Scalars not allowed + dist(1, 2) + with self.assertRaises(TypeError): # Lists not allowed + dist([1, 2, 3], [4, 5, 6]) + with self.assertRaises(ValueError): # Check dimension agree + dist((1, 2, 3, 4), (5, 6, 7)) + with self.assertRaises(ValueError): # Check dimension agree + dist((1, 2, 3), (4, 5, 6, 7)) + + # Verify tuple subclasses are allowed + class T(tuple): # tuple subclas + pass + self.assertEqual(dist(T((1, 2, 3)), T((4, 5, 6))), 3.0) + def testLdexp(self): self.assertRaises(TypeError, math.ldexp) self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index f5652a2a3f0eec..393ce906b276a9 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2035,7 +2035,24 @@ math_fmod_impl(PyObject *module, double x, double y) static PyObject * math_dist(PyObject *self, PyObject *args) { - return PyFloat_FromDouble(42.0); + PyObject *p, *q; + Py_ssize_t m, n; + + if (!PyArg_ParseTuple(args, "O!O!:dist", + &PyTuple_Type, &p, + &PyTuple_Type, &q)) { + return NULL; + } + m = PyTuple_GET_SIZE(p); + n = PyTuple_GET_SIZE(q); + if (m != n) { + PyErr_SetString(PyExc_ValueError, + "both points must have the same number of dimensions"); + return NULL; + + } + + return PyFloat_FromDouble((double)n); } /* AC: cannot convert yet, waiting for *args support */ From 7fc04ce3d1e438786de9ce13e6f8e708d5bb5c3a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 29 Jul 2018 18:21:12 -0700 Subject: [PATCH 03/11] Working version. Added dist computation logic --- Lib/test/test_math.py | 9 ++++---- Modules/mathmodule.c | 53 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 583ac6f0aaa46d..8fe493004e8ed1 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -788,13 +788,10 @@ def testHypot(self): self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) def testDist(self): - from decimal import Decimal - from fractions import Fraction - dist = math.dist # Base case - self.assertEqual(dist((1, 2, 3), (4, 5, 6)), 3.0) + self.assertEqual(dist((1, 2, 3), (4, 2, -1)), 5.0) # Test handling of bad arguments with self.assertRaises(TypeError): # Reject keyword args @@ -807,6 +804,8 @@ def testDist(self): dist(1, 2) with self.assertRaises(TypeError): # Lists not allowed dist([1, 2, 3], [4, 5, 6]) + with self.assertRaises(TypeError): # Reject values without __float__ + dist((1.1, 'string', 2.2), (1, 2, 3)) with self.assertRaises(ValueError): # Check dimension agree dist((1, 2, 3, 4), (5, 6, 7)) with self.assertRaises(ValueError): # Check dimension agree @@ -815,7 +814,7 @@ def testDist(self): # Verify tuple subclasses are allowed class T(tuple): # tuple subclas pass - self.assertEqual(dist(T((1, 2, 3)), T((4, 5, 6))), 3.0) + self.assertEqual(dist(T((1, 2, 3)), ((4, 2, -1))), 5.0) def testLdexp(self): self.assertRaises(TypeError, math.ldexp) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 393ce906b276a9..86cc5d6c6069b3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2035,8 +2035,13 @@ math_fmod_impl(PyObject *module, double x, double y) static PyObject * math_dist(PyObject *self, PyObject *args) { - PyObject *p, *q; - Py_ssize_t m, n; + PyObject *item, *p, *q; + double *diffs; + double max = 0.0; + double csum = 0.0; + double x, px, qx, result; + Py_ssize_t i, m, n; + int found_nan = 0; if (!PyArg_ParseTuple(args, "O!O!:dist", &PyTuple_Type, &p, @@ -2051,8 +2056,50 @@ math_dist(PyObject *self, PyObject *args) return NULL; } + diffs = (double *) PyObject_Malloc(n * sizeof(double)); + if (diffs == NULL) + return NULL; + for (i=0 ; i max) { + max = x; + } + } + if (Py_IS_INFINITY(max)) { + result = max; + goto done; + } + if (found_nan) { + result = Py_NAN; + goto done; + } + if (max == 0.0) { + result = 0.0; + goto done; + } + for (i=0 ; i Date: Sun, 29 Jul 2018 21:53:34 -0700 Subject: [PATCH 04/11] Add blurb --- .../NEWS.d/next/Library/2018-07-29-21-53-15.bpo-33089.hxbp3g.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2018-07-29-21-53-15.bpo-33089.hxbp3g.rst diff --git a/Misc/NEWS.d/next/Library/2018-07-29-21-53-15.bpo-33089.hxbp3g.rst b/Misc/NEWS.d/next/Library/2018-07-29-21-53-15.bpo-33089.hxbp3g.rst new file mode 100644 index 00000000000000..9b253d0bc7f429 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-29-21-53-15.bpo-33089.hxbp3g.rst @@ -0,0 +1 @@ +Add math.dist() to compute the Euclidean distance between two points. From 43e2f460462fff3560566f8de695d55a85a69bdf Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 29 Jul 2018 22:44:28 -0700 Subject: [PATCH 05/11] Add documentation --- Doc/library/math.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index e60d02919c8f30..76226c282d5e68 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -330,6 +330,18 @@ Trigonometric functions Return the cosine of *x* radians. +.. function:: dist(p, q) + + Return the Euclidean distance between two points *p* and *q*, each + given as a tuple of coordinates. The two tuples must be the same size. + + Roughly equivalent to:: + + sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) + + .. versionadded:: 3.8 + + .. function:: hypot(*coordinates) Return the Euclidean norm, ``sqrt(sum(x**2 for x in coordinates))``. From 9ea61d945abcf6e2143a2608f96b4d18a9a5aaf1 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 29 Jul 2018 23:28:21 -0700 Subject: [PATCH 06/11] Add more tests --- Lib/test/test_math.py | 67 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 8fe493004e8ed1..3cae3de7ba5be5 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -4,9 +4,11 @@ from test.support import run_unittest, verbose, requires_IEEE_754 from test import support import unittest +import itertools import math import os import platform +import random import struct import sys import sysconfig @@ -788,11 +790,53 @@ def testHypot(self): self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) def testDist(self): + from decimal import Decimal as D + from fractions import Fraction as F + dist = math.dist + sqrt = math.sqrt - # Base case + # Simple exact case self.assertEqual(dist((1, 2, 3), (4, 2, -1)), 5.0) + # Test different numbers of arguments (from zero to nine) + # against a straightforward pure python implementation + for i in range(9): + for j in range(5): + p = tuple(random.uniform(-5, 5) for k in range(i)) + q = tuple(random.uniform(-5, 5) for k in range(i)) + self.assertAlmostEqual( + dist(p, q), + sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) + ) + + # Test allowable types (those with __float__) + self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) + self.assertEqual(dist((14, 1), (2, -4)), 13) + self.assertEqual(dist((D(14), D(1)), (D(2), D(-4))), D(13)) + self.assertEqual(dist((F(14, 32), F(1, 32)), (F(2, 32), F(-4, 32))), + F(13, 32)) + self.assertEqual(dist((True, True, False, True, False), + (True, False, True, True, False)), + sqrt(2.0)) + + # Test corner cases + self.assertEqual(dist((13.25, 12.5, -3.25), + (13.25, 12.5, -3.25)), + 0.0) # Distance with self is zero + self.assertEqual(dist((), ()), 0.0) # Zero-dimensional case + self.assertEqual(1.0, # Convert negative zero to positive zero + math.copysign(1.0, dist((-0.0,), (0.0,))) + ) + self.assertEqual(1.0, # Convert negative zero to positive zero + math.copysign(1.0, dist((0.0,), (-0.0,))) + ) + + # Verify tuple subclasses are allowed + class T(tuple): # tuple subclas + pass + self.assertEqual(dist(T((1, 2, 3)), ((4, 2, -1))), 5.0) + # Test handling of bad arguments with self.assertRaises(TypeError): # Reject keyword args dist(p=(1, 2, 3), q=(4, 5, 6)) @@ -811,10 +855,23 @@ def testDist(self): with self.assertRaises(ValueError): # Check dimension agree dist((1, 2, 3), (4, 5, 6, 7)) - # Verify tuple subclasses are allowed - class T(tuple): # tuple subclas - pass - self.assertEqual(dist(T((1, 2, 3)), ((4, 2, -1))), 5.0) + + # Verify that the one dimensional case equivalent to abs() + for i in range(20): + p, q = random.random(), random.random() + self.assertEqual(dist((p,), (q,)), abs(p - q)) + + # Test special values + values = [NINF, -10.5, -0.0, 0.0, 10.5, INF, NAN] + for p in itertools.product(values, repeat=3): + for q in itertools.product(values, repeat=3): + diffs = [px - qx for px, qx in zip(p, q)] + if any(map(math.isinf, diffs)): + # Any infinite difference gives positive infinity. + self.assertEqual(dist(p, q), INF) + elif any(map(math.isnan, diffs)): + # If no infinity, any NaN gives a Nan. + self.assertTrue(math.isnan(dist(p, q))) def testLdexp(self): self.assertRaises(TypeError, math.ldexp) From b5e4d0ff18bcba2b6715705baf0c32023b5081b8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 30 Jul 2018 00:00:20 -0700 Subject: [PATCH 07/11] Add docstring --- Modules/mathmodule.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 86cc5d6c6069b3..76d568ea7c2081 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2102,6 +2102,16 @@ math_dist(PyObject *self, PyObject *args) return PyFloat_FromDouble(result); } +PyDoc_STRVAR(math_dist_doc, + "dist(p, q) -> value\n\n\ +Return the Euclidean distance between two points p and q.\n\ +\n\ +The points should be specified as tuples of coordinates.\n\ +Both tuples must be the same size.\n\ +\n\ +Roughly equivalent to:\n\ + sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))"); + /* AC: cannot convert yet, waiting for *args support */ static PyObject * math_hypot(PyObject *self, PyObject *args) @@ -2429,7 +2439,7 @@ static PyMethodDef math_methods[] = { {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, MATH_DEGREES_METHODDEF - {"dist", math_dist, METH_VARARGS, math_remainder_doc}, // XXX fix doc, then convert to arg clinic + {"dist", math_dist, METH_VARARGS, math_dist_doc}, {"erf", math_erf, METH_O, math_erf_doc}, {"erfc", math_erfc, METH_O, math_erfc_doc}, {"exp", math_exp, METH_O, math_exp_doc}, From 230b4235cd5527b91e068ae2f9f7b28f923a9c1c Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 30 Jul 2018 01:05:10 -0700 Subject: [PATCH 08/11] Add curly braces for neatness --- Modules/mathmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 76d568ea7c2081..60d6266f01d587 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2057,8 +2057,9 @@ math_dist(PyObject *self, PyObject *args) } diffs = (double *) PyObject_Malloc(n * sizeof(double)); - if (diffs == NULL) + if (diffs == NULL) { return NULL; + } for (i=0 ; i Date: Mon, 30 Jul 2018 21:08:03 -0700 Subject: [PATCH 09/11] Convert to METH_FASTCALL --- Modules/mathmodule.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 60d6266f01d587..76e3db36c872c9 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2033,7 +2033,7 @@ math_fmod_impl(PyObject *module, double x, double y) /* AC: XXX Todo */ static PyObject * -math_dist(PyObject *self, PyObject *args) +math_dist(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *item, *p, *q; double *diffs; @@ -2043,7 +2043,7 @@ math_dist(PyObject *self, PyObject *args) Py_ssize_t i, m, n; int found_nan = 0; - if (!PyArg_ParseTuple(args, "O!O!:dist", + if (!_PyArg_ParseStack(args, nargs, "O!O!:dist", &PyTuple_Type, &p, &PyTuple_Type, &q)) { return NULL; @@ -2440,7 +2440,8 @@ static PyMethodDef math_methods[] = { {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, MATH_DEGREES_METHODDEF - {"dist", math_dist, METH_VARARGS, math_dist_doc}, + {"dist", (PyCFunction)math_dist, + METH_FASTCALL, math_dist_doc}, {"erf", math_erf, METH_O, math_erf_doc}, {"erfc", math_erfc, METH_O, math_erfc_doc}, {"exp", math_exp, METH_O, math_exp_doc}, From ec42e1c7d977a399fda124308566ea091c4dbede Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 30 Jul 2018 21:59:16 -0700 Subject: [PATCH 10/11] Convert to ArgClinic --- Modules/clinic/mathmodule.c.h | 37 +++++++++++++++++++++++++++++++- Modules/mathmodule.c | 40 +++++++++++++++++------------------ 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 3a487bdbea23b9..c4d2786341013a 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -269,6 +269,41 @@ math_fmod(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(math_dist__doc__, +"dist($module, p, q, /)\n" +"--\n" +"\n" +"Return the Euclidean distance between two points p and q.\n" +"\n" +"The points should be specified as tuples of coordinates.\n" +"Both tuples must be the same size.\n" +"\n" +"Roughly equivalent to:\n" +" sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))"); + +#define MATH_DIST_METHODDEF \ + {"dist", (PyCFunction)math_dist, METH_FASTCALL, math_dist__doc__}, + +static PyObject * +math_dist_impl(PyObject *module, PyObject *p, PyObject *q); + +static PyObject * +math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *p; + PyObject *q; + + if (!_PyArg_ParseStack(args, nargs, "O!O!:dist", + &PyTuple_Type, &p, &PyTuple_Type, &q)) { + goto exit; + } + return_value = math_dist_impl(module, p, q); + +exit: + return return_value; +} + PyDoc_STRVAR(math_pow__doc__, "pow($module, x, y, /)\n" "--\n" @@ -487,4 +522,4 @@ math_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject exit: return return_value; } -/*[clinic end generated code: output=1c35516a10443902 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d936137c1189b89b input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 76e3db36c872c9..8f11f2cf3184ed 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2031,11 +2031,27 @@ math_fmod_impl(PyObject *module, double x, double y) return PyFloat_FromDouble(r); } -/* AC: XXX Todo */ +/*[clinic input] +math.dist + + p: object(subclass_of='&PyTuple_Type') + q: object(subclass_of='&PyTuple_Type') + / + +Return the Euclidean distance between two points p and q. + +The points should be specified as tuples of coordinates. +Both tuples must be the same size. + +Roughly equivalent to: + sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) +[clinic start generated code]*/ + static PyObject * -math_dist(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +math_dist_impl(PyObject *module, PyObject *p, PyObject *q) +/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/ { - PyObject *item, *p, *q; + PyObject *item; double *diffs; double max = 0.0; double csum = 0.0; @@ -2043,11 +2059,6 @@ math_dist(PyObject *self, PyObject *const *args, Py_ssize_t nargs) Py_ssize_t i, m, n; int found_nan = 0; - if (!_PyArg_ParseStack(args, nargs, "O!O!:dist", - &PyTuple_Type, &p, - &PyTuple_Type, &q)) { - return NULL; - } m = PyTuple_GET_SIZE(p); n = PyTuple_GET_SIZE(q); if (m != n) { @@ -2103,16 +2114,6 @@ math_dist(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return PyFloat_FromDouble(result); } -PyDoc_STRVAR(math_dist_doc, - "dist(p, q) -> value\n\n\ -Return the Euclidean distance between two points p and q.\n\ -\n\ -The points should be specified as tuples of coordinates.\n\ -Both tuples must be the same size.\n\ -\n\ -Roughly equivalent to:\n\ - sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))"); - /* AC: cannot convert yet, waiting for *args support */ static PyObject * math_hypot(PyObject *self, PyObject *args) @@ -2440,8 +2441,7 @@ static PyMethodDef math_methods[] = { {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, MATH_DEGREES_METHODDEF - {"dist", (PyCFunction)math_dist, - METH_FASTCALL, math_dist_doc}, + MATH_DIST_METHODDEF {"erf", math_erf, METH_O, math_erf_doc}, {"erfc", math_erfc, METH_O, math_erfc_doc}, {"exp", math_exp, METH_O, math_exp_doc}, From 628a26184f1d09c0472a038fe314de63e6781c3d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 30 Jul 2018 22:55:41 -0700 Subject: [PATCH 11/11] Add extreme value tests --- Lib/test/test_math.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 3cae3de7ba5be5..448110b1674aed 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -873,6 +873,23 @@ class T(tuple): # tuple subclas # If no infinity, any NaN gives a Nan. self.assertTrue(math.isnan(dist(p, q))) + # Verify scaling for extremely large values + fourthmax = FLOAT_MAX / 4.0 + for n in range(32): + p = (fourthmax,) * n + q = (0.0,) * n + self.assertEqual(dist(p, q), fourthmax * math.sqrt(n)) + self.assertEqual(dist(q, p), fourthmax * math.sqrt(n)) + + # Verify scaling for extremely small values + for exp in range(32): + scale = FLOAT_MIN / 2.0 ** exp + p = (4*scale, 3*scale) + q = (0.0, 0.0) + self.assertEqual(math.dist(p, q), 5*scale) + self.assertEqual(math.dist(q, p), 5*scale) + + def testLdexp(self): self.assertRaises(TypeError, math.ldexp) self.ftest('ldexp(0,1)', math.ldexp(0,1), 0)