17db96d56Sopenharmony_ci# test interactions between int, float, Decimal and Fraction 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport unittest 47db96d56Sopenharmony_ciimport random 57db96d56Sopenharmony_ciimport math 67db96d56Sopenharmony_ciimport sys 77db96d56Sopenharmony_ciimport operator 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_cifrom decimal import Decimal as D 107db96d56Sopenharmony_cifrom fractions import Fraction as F 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ci# Constants related to the hash implementation; hash(x) is based 137db96d56Sopenharmony_ci# on the reduction of x modulo the prime _PyHASH_MODULUS. 147db96d56Sopenharmony_ci_PyHASH_MODULUS = sys.hash_info.modulus 157db96d56Sopenharmony_ci_PyHASH_INF = sys.hash_info.inf 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ciclass DummyIntegral(int): 197db96d56Sopenharmony_ci """Dummy Integral class to test conversion of the Rational to float.""" 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci def __mul__(self, other): 227db96d56Sopenharmony_ci return DummyIntegral(super().__mul__(other)) 237db96d56Sopenharmony_ci __rmul__ = __mul__ 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci def __truediv__(self, other): 267db96d56Sopenharmony_ci return NotImplemented 277db96d56Sopenharmony_ci __rtruediv__ = __truediv__ 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci @property 307db96d56Sopenharmony_ci def numerator(self): 317db96d56Sopenharmony_ci return DummyIntegral(self) 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci @property 347db96d56Sopenharmony_ci def denominator(self): 357db96d56Sopenharmony_ci return DummyIntegral(1) 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ciclass HashTest(unittest.TestCase): 397db96d56Sopenharmony_ci def check_equal_hash(self, x, y): 407db96d56Sopenharmony_ci # check both that x and y are equal and that their hashes are equal 417db96d56Sopenharmony_ci self.assertEqual(hash(x), hash(y), 427db96d56Sopenharmony_ci "got different hashes for {!r} and {!r}".format(x, y)) 437db96d56Sopenharmony_ci self.assertEqual(x, y) 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ci def test_bools(self): 467db96d56Sopenharmony_ci self.check_equal_hash(False, 0) 477db96d56Sopenharmony_ci self.check_equal_hash(True, 1) 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci def test_integers(self): 507db96d56Sopenharmony_ci # check that equal values hash equal 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci # exact integers 537db96d56Sopenharmony_ci for i in range(-1000, 1000): 547db96d56Sopenharmony_ci self.check_equal_hash(i, float(i)) 557db96d56Sopenharmony_ci self.check_equal_hash(i, D(i)) 567db96d56Sopenharmony_ci self.check_equal_hash(i, F(i)) 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci # the current hash is based on reduction modulo 2**n-1 for some 597db96d56Sopenharmony_ci # n, so pay special attention to numbers of the form 2**n and 2**n-1. 607db96d56Sopenharmony_ci for i in range(100): 617db96d56Sopenharmony_ci n = 2**i - 1 627db96d56Sopenharmony_ci if n == int(float(n)): 637db96d56Sopenharmony_ci self.check_equal_hash(n, float(n)) 647db96d56Sopenharmony_ci self.check_equal_hash(-n, -float(n)) 657db96d56Sopenharmony_ci self.check_equal_hash(n, D(n)) 667db96d56Sopenharmony_ci self.check_equal_hash(n, F(n)) 677db96d56Sopenharmony_ci self.check_equal_hash(-n, D(-n)) 687db96d56Sopenharmony_ci self.check_equal_hash(-n, F(-n)) 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci n = 2**i 717db96d56Sopenharmony_ci self.check_equal_hash(n, float(n)) 727db96d56Sopenharmony_ci self.check_equal_hash(-n, -float(n)) 737db96d56Sopenharmony_ci self.check_equal_hash(n, D(n)) 747db96d56Sopenharmony_ci self.check_equal_hash(n, F(n)) 757db96d56Sopenharmony_ci self.check_equal_hash(-n, D(-n)) 767db96d56Sopenharmony_ci self.check_equal_hash(-n, F(-n)) 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci # random values of various sizes 797db96d56Sopenharmony_ci for _ in range(1000): 807db96d56Sopenharmony_ci e = random.randrange(300) 817db96d56Sopenharmony_ci n = random.randrange(-10**e, 10**e) 827db96d56Sopenharmony_ci self.check_equal_hash(n, D(n)) 837db96d56Sopenharmony_ci self.check_equal_hash(n, F(n)) 847db96d56Sopenharmony_ci if n == int(float(n)): 857db96d56Sopenharmony_ci self.check_equal_hash(n, float(n)) 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci def test_binary_floats(self): 887db96d56Sopenharmony_ci # check that floats hash equal to corresponding Fractions and Decimals 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci # floats that are distinct but numerically equal should hash the same 917db96d56Sopenharmony_ci self.check_equal_hash(0.0, -0.0) 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci # zeros 947db96d56Sopenharmony_ci self.check_equal_hash(0.0, D(0)) 957db96d56Sopenharmony_ci self.check_equal_hash(-0.0, D(0)) 967db96d56Sopenharmony_ci self.check_equal_hash(-0.0, D('-0.0')) 977db96d56Sopenharmony_ci self.check_equal_hash(0.0, F(0)) 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_ci # infinities and nans 1007db96d56Sopenharmony_ci self.check_equal_hash(float('inf'), D('inf')) 1017db96d56Sopenharmony_ci self.check_equal_hash(float('-inf'), D('-inf')) 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci for _ in range(1000): 1047db96d56Sopenharmony_ci x = random.random() * math.exp(random.random()*200.0 - 100.0) 1057db96d56Sopenharmony_ci self.check_equal_hash(x, D.from_float(x)) 1067db96d56Sopenharmony_ci self.check_equal_hash(x, F.from_float(x)) 1077db96d56Sopenharmony_ci 1087db96d56Sopenharmony_ci def test_complex(self): 1097db96d56Sopenharmony_ci # complex numbers with zero imaginary part should hash equal to 1107db96d56Sopenharmony_ci # the corresponding float 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci test_values = [0.0, -0.0, 1.0, -1.0, 0.40625, -5136.5, 1137db96d56Sopenharmony_ci float('inf'), float('-inf')] 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci for zero in -0.0, 0.0: 1167db96d56Sopenharmony_ci for value in test_values: 1177db96d56Sopenharmony_ci self.check_equal_hash(value, complex(value, zero)) 1187db96d56Sopenharmony_ci 1197db96d56Sopenharmony_ci def test_decimals(self): 1207db96d56Sopenharmony_ci # check that Decimal instances that have different representations 1217db96d56Sopenharmony_ci # but equal values give the same hash 1227db96d56Sopenharmony_ci zeros = ['0', '-0', '0.0', '-0.0e10', '000e-10'] 1237db96d56Sopenharmony_ci for zero in zeros: 1247db96d56Sopenharmony_ci self.check_equal_hash(D(zero), D(0)) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci self.check_equal_hash(D('1.00'), D(1)) 1277db96d56Sopenharmony_ci self.check_equal_hash(D('1.00000'), D(1)) 1287db96d56Sopenharmony_ci self.check_equal_hash(D('-1.00'), D(-1)) 1297db96d56Sopenharmony_ci self.check_equal_hash(D('-1.00000'), D(-1)) 1307db96d56Sopenharmony_ci self.check_equal_hash(D('123e2'), D(12300)) 1317db96d56Sopenharmony_ci self.check_equal_hash(D('1230e1'), D(12300)) 1327db96d56Sopenharmony_ci self.check_equal_hash(D('12300'), D(12300)) 1337db96d56Sopenharmony_ci self.check_equal_hash(D('12300.0'), D(12300)) 1347db96d56Sopenharmony_ci self.check_equal_hash(D('12300.00'), D(12300)) 1357db96d56Sopenharmony_ci self.check_equal_hash(D('12300.000'), D(12300)) 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci def test_fractions(self): 1387db96d56Sopenharmony_ci # check special case for fractions where either the numerator 1397db96d56Sopenharmony_ci # or the denominator is a multiple of _PyHASH_MODULUS 1407db96d56Sopenharmony_ci self.assertEqual(hash(F(1, _PyHASH_MODULUS)), _PyHASH_INF) 1417db96d56Sopenharmony_ci self.assertEqual(hash(F(-1, 3*_PyHASH_MODULUS)), -_PyHASH_INF) 1427db96d56Sopenharmony_ci self.assertEqual(hash(F(7*_PyHASH_MODULUS, 1)), 0) 1437db96d56Sopenharmony_ci self.assertEqual(hash(F(-_PyHASH_MODULUS, 1)), 0) 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci # The numbers ABC doesn't enforce that the "true" division 1467db96d56Sopenharmony_ci # of integers produces a float. This tests that the 1477db96d56Sopenharmony_ci # Rational.__float__() method has required type conversions. 1487db96d56Sopenharmony_ci x = F(DummyIntegral(1), DummyIntegral(2), _normalize=False) 1497db96d56Sopenharmony_ci self.assertRaises(TypeError, lambda: x.numerator/x.denominator) 1507db96d56Sopenharmony_ci self.assertEqual(float(x), 0.5) 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci def test_hash_normalization(self): 1537db96d56Sopenharmony_ci # Test for a bug encountered while changing long_hash. 1547db96d56Sopenharmony_ci # 1557db96d56Sopenharmony_ci # Given objects x and y, it should be possible for y's 1567db96d56Sopenharmony_ci # __hash__ method to return hash(x) in order to ensure that 1577db96d56Sopenharmony_ci # hash(x) == hash(y). But hash(x) is not exactly equal to the 1587db96d56Sopenharmony_ci # result of x.__hash__(): there's some internal normalization 1597db96d56Sopenharmony_ci # to make sure that the result fits in a C long, and is not 1607db96d56Sopenharmony_ci # equal to the invalid hash value -1. This internal 1617db96d56Sopenharmony_ci # normalization must therefore not change the result of 1627db96d56Sopenharmony_ci # hash(x) for any x. 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_ci class HalibutProxy: 1657db96d56Sopenharmony_ci def __hash__(self): 1667db96d56Sopenharmony_ci return hash('halibut') 1677db96d56Sopenharmony_ci def __eq__(self, other): 1687db96d56Sopenharmony_ci return other == 'halibut' 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci x = {'halibut', HalibutProxy()} 1717db96d56Sopenharmony_ci self.assertEqual(len(x), 1) 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ciclass ComparisonTest(unittest.TestCase): 1747db96d56Sopenharmony_ci def test_mixed_comparisons(self): 1757db96d56Sopenharmony_ci 1767db96d56Sopenharmony_ci # ordered list of distinct test values of various types: 1777db96d56Sopenharmony_ci # int, float, Fraction, Decimal 1787db96d56Sopenharmony_ci test_values = [ 1797db96d56Sopenharmony_ci float('-inf'), 1807db96d56Sopenharmony_ci D('-1e425000000'), 1817db96d56Sopenharmony_ci -1e308, 1827db96d56Sopenharmony_ci F(-22, 7), 1837db96d56Sopenharmony_ci -3.14, 1847db96d56Sopenharmony_ci -2, 1857db96d56Sopenharmony_ci 0.0, 1867db96d56Sopenharmony_ci 1e-320, 1877db96d56Sopenharmony_ci True, 1887db96d56Sopenharmony_ci F('1.2'), 1897db96d56Sopenharmony_ci D('1.3'), 1907db96d56Sopenharmony_ci float('1.4'), 1917db96d56Sopenharmony_ci F(275807, 195025), 1927db96d56Sopenharmony_ci D('1.414213562373095048801688724'), 1937db96d56Sopenharmony_ci F(114243, 80782), 1947db96d56Sopenharmony_ci F(473596569, 84615), 1957db96d56Sopenharmony_ci 7e200, 1967db96d56Sopenharmony_ci D('infinity'), 1977db96d56Sopenharmony_ci ] 1987db96d56Sopenharmony_ci for i, first in enumerate(test_values): 1997db96d56Sopenharmony_ci for second in test_values[i+1:]: 2007db96d56Sopenharmony_ci self.assertLess(first, second) 2017db96d56Sopenharmony_ci self.assertLessEqual(first, second) 2027db96d56Sopenharmony_ci self.assertGreater(second, first) 2037db96d56Sopenharmony_ci self.assertGreaterEqual(second, first) 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci def test_complex(self): 2067db96d56Sopenharmony_ci # comparisons with complex are special: equality and inequality 2077db96d56Sopenharmony_ci # comparisons should always succeed, but order comparisons should 2087db96d56Sopenharmony_ci # raise TypeError. 2097db96d56Sopenharmony_ci z = 1.0 + 0j 2107db96d56Sopenharmony_ci w = -3.14 + 2.7j 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci for v in 1, 1.0, F(1), D(1), complex(1): 2137db96d56Sopenharmony_ci self.assertEqual(z, v) 2147db96d56Sopenharmony_ci self.assertEqual(v, z) 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci for v in 2, 2.0, F(2), D(2), complex(2): 2177db96d56Sopenharmony_ci self.assertNotEqual(z, v) 2187db96d56Sopenharmony_ci self.assertNotEqual(v, z) 2197db96d56Sopenharmony_ci self.assertNotEqual(w, v) 2207db96d56Sopenharmony_ci self.assertNotEqual(v, w) 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ci for v in (1, 1.0, F(1), D(1), complex(1), 2237db96d56Sopenharmony_ci 2, 2.0, F(2), D(2), complex(2), w): 2247db96d56Sopenharmony_ci for op in operator.le, operator.lt, operator.ge, operator.gt: 2257db96d56Sopenharmony_ci self.assertRaises(TypeError, op, z, v) 2267db96d56Sopenharmony_ci self.assertRaises(TypeError, op, v, z) 2277db96d56Sopenharmony_ci 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ciif __name__ == '__main__': 2307db96d56Sopenharmony_ci unittest.main() 231