diff options
author | Raymond Hettinger <rhettinger@users.noreply.github.com> | 2018-07-31 07:45:49 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-31 07:45:49 (GMT) |
commit | 9c18b1ae527346bc178250ad1ca07bffdacde5dd (patch) | |
tree | 117fc5de5a05d5f9da9bed73921d3ef9c02409bc /Lib/test | |
parent | 9d5727326af53ddd91016d98e16ae7cf829caa95 (diff) | |
download | cpython-9c18b1ae527346bc178250ad1ca07bffdacde5dd.zip cpython-9c18b1ae527346bc178250ad1ca07bffdacde5dd.tar.gz cpython-9c18b1ae527346bc178250ad1ca07bffdacde5dd.tar.bz2 |
bpo-33089: Add math.dist() for computing the Euclidean distance between two points (GH-8561)
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_math.py | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 4843899..448110b 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 @@ -787,6 +789,107 @@ class MathTests(unittest.TestCase): scale = FLOAT_MIN / 2.0 ** exp 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 + + # 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)) + 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(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 + dist((1, 2, 3), (4, 5, 6, 7)) + + + # 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))) + + # 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) |