import itertools import math from testutils import assert_raises, skip_if_unsupported NAN = float("nan") INF = float("inf") NINF = float("-inf") def test_math_315_apis(): import math.integer import sys import _math_integer assert math.integer is _math_integer assert sys.modules["math.integer"] is math.integer assert math.integer.__name__ == "math.integer" for name, args, expected in ( ("comb", (5, 2), 10), ("factorial", (5,), 120), ("gcd", (12, 18), 6), ("isqrt", (17,), 4), ("lcm", (4, 6), 12), ("perm", (5, 2), 20), ): function = getattr(math, name) assert function is getattr(math.integer, name) assert function.__module__ == "math.integer" assert function(*args) == expected for value in (1.0, -1.0, sys.float_info.min, sys.float_info.max): assert math.isnormal(value) is True assert math.issubnormal(value) is False for value in (sys.float_info.min / 2, -sys.float_info.min / 2): assert math.isnormal(value) is False assert math.issubnormal(value) is True for value in (0.0, -0.0, INF, NINF, NAN): assert math.isnormal(value) is False assert math.issubnormal(value) is False for value in (0.0, 1.0, INF, NAN): assert math.signbit(math.copysign(value, 1.0)) is False assert math.signbit(math.copysign(value, -1.0)) is True class FloatArgument: def __float__(self): return -2.0 class IndexArgument: def __index__(self): return 2 for function in (math.isnormal, math.issubnormal, math.signbit): assert function(FloatArgument()) == function(-2.0) assert function(IndexArgument()) == function(2.0) assert_raises(TypeError, function, object()) assert_raises(TypeError, function, x=1.0) assert_raises(TypeError, function) assert_raises(TypeError, function, 1.0, 2.0) for function, expected in ((math.fmax, 2.0), (math.fmin, -2.0)): assert function(-2.0, 2.0) == expected assert function(2.0, -2.0) == expected assert type(function(1, 2)) is float assert function(FloatArgument(), IndexArgument()) == expected assert math.isnan(function(NAN, NAN)) for value in (0.0, -0.0, 1.0, -1.0, INF, NINF): for result in (function(NAN, value), function(value, NAN)): assert result == value assert math.signbit(result) == math.signbit(value) assert function(0.0, -0.0) == 0.0 assert function(-0.0, 0.0) == 0.0 assert_raises(TypeError, function, object(), 1.0) assert_raises(TypeError, function, 1.0, object()) assert_raises(TypeError, function, x=1.0, y=2.0) assert_raises(TypeError, function, 1.0) assert_raises(TypeError, function, 1.0, 2.0, 3.0) assert math.fmax(NINF, INF) == INF assert math.fmin(NINF, INF) == NINF test_math_315_apis() # assert(math.exp(2) == math.exp(2.0)) # assert(math.exp(True) == math.exp(1.0)) # # class Conversible(): # def __float__(self): # print("Converting to float now!") # return 1.1111 # # assert math.log(1.1111) == math.log(Conversible()) # A logarithm can fit in a float even when its integer base cannot. assert math.isclose(math.log(2**4000, 2**2000), 2.0) assert math.isclose(math.log(2.0, 2**2000), 1.0 / 2000) for value in (1, 8.0, INF, NAN): assert_raises(ZeroDivisionError, math.log, value, 1) assert_raises(TypeError, math.log, object(), 1) assert_raises(ValueError, math.log, 8, -(2**2000)) log_conversions = [] class LogFloat: def __init__(self, name, value): self.name = name self.value = value def __float__(self): log_conversions.append(self.name) return self.value assert math.isclose(math.log(LogFloat("x", 8.0), LogFloat("base", 2.0)), 3.0) assert log_conversions == ["x", "base"] log_conversions.clear() assert_raises(ValueError, math.log, -1, LogFloat("base", 2.0)) assert log_conversions == [] class LogFloatOverflow: def __float__(self): log_conversions.append("float") raise OverflowError("float conversion failed") def __index__(self): log_conversions.append("index") return 2**2000 class LogIndexOnly: def __index__(self): return 2**2000 for logarithm in (math.log, math.log2, math.log10): log_conversions.clear() assert math.isclose(logarithm(LogFloatOverflow()), logarithm(2**2000)) assert log_conversions == ["float", "index"] assert math.isclose(logarithm(LogIndexOnly()), logarithm(2**2000)) for error_type in (TypeError, ValueError, RuntimeError): error = error_type("float conversion failed") class FailedFloat(LogFloatOverflow): def __float__(self): raise error with assert_raises(error_type) as caught: logarithm(FailedFloat()) assert caught.exception is error error = RuntimeError("index conversion failed") class FailedIndex(LogFloatOverflow): def __index__(self): raise error with assert_raises(RuntimeError) as caught: logarithm(FailedIndex()) assert caught.exception is error class NegativeIndex(LogFloatOverflow): def __index__(self): return -1 assert_raises(ValueError, logarithm, NegativeIndex()) class InvalidIndex(LogFloatOverflow): def __index__(self): return 1.0 assert_raises(TypeError, logarithm, InvalidIndex()) class NoIndex: __float__ = LogFloatOverflow.__float__ assert_raises(OverflowError, logarithm, NoIndex()) class SuccessfulFloat: def __float__(self): return 8.0 def __index__(self): raise AssertionError("successful float conversion must not retry index") assert logarithm(SuccessfulFloat()) == logarithm(8.0) assert math.isclose(math.log(8, LogFloatOverflow()), 3 / 2000) assert math.isclose(math.log(8, LogIndexOnly()), 3 / 2000) class LogInt(int): def __float__(self): raise AssertionError("integer logarithms use the native integer value") assert math.isclose(math.log(LogInt(2**4000), LogInt(2**2000)), 2.0) # roundings assert int.__trunc__ assert int.__floor__ assert int.__ceil__ # assert float.__trunc__ def float_floor_exists(): assert float.__floor__ def float_ceil_exists(): assert float.__ceil__ skip_if_unsupported(3, 9, float_floor_exists) skip_if_unsupported(3, 9, float_ceil_exists) assert math.trunc(2) == 2 assert math.ceil(3) == 3 assert math.floor(4) == 4 assert math.trunc(2.2) == 2 assert math.ceil(3.3) == 4 assert math.floor(4.4) == 4 assert isinstance(math.trunc(2.2), int) assert isinstance(math.ceil(3.3), int) assert isinstance(math.floor(4.4), int) class A(object): def __trunc__(self): return 2 def __ceil__(self): return 3 def __floor__(self): return 4 assert math.trunc(A()) == 2 assert math.ceil(A()) == 3 assert math.floor(A()) == 4 class A(object): def __trunc__(self): return 2.2 def __ceil__(self): return 3.3 def __floor__(self): return 4.4 assert math.trunc(A()) == 2.2 assert math.ceil(A()) == 3.3 assert math.floor(A()) == 4.4 class A(object): def __trunc__(self): return "trunc" def __ceil__(self): return "ceil" def __floor__(self): return "floor" assert math.trunc(A()) == "trunc" assert math.ceil(A()) == "ceil" assert math.floor(A()) == "floor" with assert_raises(TypeError): math.trunc(object()) with assert_raises(TypeError): math.ceil(object()) with assert_raises(TypeError): math.floor(object()) with assert_raises(TypeError) as cm: math.floor("1.5") assert str(cm.exception) == "must be real number, not str" with assert_raises(TypeError) as cm: math.ceil(None) assert str(cm.exception) == "must be real number, not NoneType" assert math.floor(-0.5) == -1 and math.ceil(-0.5) == 0 with assert_raises(OverflowError): math.floor(INF) with assert_raises(ValueError): math.ceil(NAN) class FloatFloor(float): def __floor__(self): return "sub floor" def __ceil__(self): return "sub ceil" assert math.floor(FloatFloor(1.5)) == "sub floor" assert math.ceil(FloatFloor(1.5)) == "sub ceil" isclose = math.isclose def assertIsClose(a, b, *args, **kwargs): assert isclose(a, b, *args, **kwargs) == True, "%s and %s should be close!" % (a, b) def assertIsNotClose(a, b, *args, **kwargs): assert isclose(a, b, *args, **kwargs) == False, "%s and %s should not be close!" % ( a, b, ) def assertAllClose(examples, *args, **kwargs): for a, b in examples: assertIsClose(a, b, *args, **kwargs) def assertAllNotClose(examples, *args, **kwargs): for a, b in examples: assertIsNotClose(a, b, *args, **kwargs) # test_negative_tolerances: ValueError should be raised if either tolerance is less than zero assert_raises(ValueError, lambda: isclose(1, 1, rel_tol=-1e-100)) assert_raises(ValueError, lambda: isclose(1, 1, rel_tol=1e-100, abs_tol=-1e10)) # test_identical: identical values must test as close identical_examples = [ (2.0, 2.0), (0.1e200, 0.1e200), (1.123e-300, 1.123e-300), (12345, 12345.0), (0.0, -0.0), (345678, 345678), ] assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0) # test_eight_decimal_places: examples that are close to 1e-8, but not 1e-9 eight_decimal_places_examples = [ (1e8, 1e8 + 1), (-1e-8, -1.000000009e-8), (1.12345678, 1.12345679), ] assertAllClose(eight_decimal_places_examples, rel_tol=1e-08) assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-09) # test_near_zero: values close to zero near_zero_examples = [(1e-9, 0.0), (-1e-9, 0.0), (-1e-150, 0.0)] # these should not be close to any rel_tol assertAllNotClose(near_zero_examples, rel_tol=0.9) # these should be close to abs_tol=1e-8 assertAllClose(near_zero_examples, abs_tol=1e-8) # test_identical_infinite: these are close regardless of tolerance -- i.e. they are equal assertIsClose(INF, INF) assertIsClose(INF, INF, abs_tol=0.0) assertIsClose(NINF, NINF) assertIsClose(NINF, NINF, abs_tol=0.0) # test_inf_ninf_nan(self): these should never be close (following IEEE 754 rules for equality) not_close_examples = [ (NAN, NAN), (NAN, 1e-100), (1e-100, NAN), (INF, NAN), (NAN, INF), (INF, NINF), (INF, 1.0), (1.0, INF), (INF, 1e308), (1e308, INF), ] # use largest reasonable tolerance assertAllNotClose(not_close_examples, abs_tol=0.999999999999999) # test_zero_tolerance: test with zero tolerance zero_tolerance_close_examples = [(1.0, 1.0), (-3.4, -3.4), (-1e-300, -1e-300)] assertAllClose(zero_tolerance_close_examples, rel_tol=0.0) zero_tolerance_not_close_examples = [ (1.0, 1.000000000000001), (0.99999999999999, 1.0), (1.0e200, 0.999999999999999e200), ] assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0) # test_asymmetry: test the asymmetry example from PEP 485 assertAllClose([(9, 10), (10, 9)], rel_tol=0.1) # test_integers: test with integer values integer_examples = [(100000001, 100000000), (123456789, 123456788)] assertAllClose(integer_examples, rel_tol=1e-8) assertAllNotClose(integer_examples, rel_tol=1e-9) # test_decimals: test with Decimal values # test_fractions: test with Fraction values assert math.copysign(1, 42) == 1.0 assert math.copysign(0.0, 42) == 0.0 assert math.copysign(1.0, -42) == -1.0 assert math.copysign(3, 0.0) == 3.0 assert math.copysign(4.0, -0.0) == -4.0 assert_raises(TypeError, math.copysign) # copysign should let us distinguish signs of zeros assert math.copysign(1.0, 0.0) == 1.0 assert math.copysign(1.0, -0.0) == -1.0 assert math.copysign(INF, 0.0) == INF assert math.copysign(INF, -0.0) == NINF assert math.copysign(NINF, 0.0) == INF assert math.copysign(NINF, -0.0) == NINF # and of infinities assert math.copysign(1.0, INF) == 1.0 assert math.copysign(1.0, NINF) == -1.0 assert math.copysign(INF, INF) == INF assert math.copysign(INF, NINF) == NINF assert math.copysign(NINF, INF) == INF assert math.copysign(NINF, NINF) == NINF assert math.isnan(math.copysign(NAN, 1.0)) assert math.isnan(math.copysign(NAN, INF)) assert math.isnan(math.copysign(NAN, NINF)) assert math.isnan(math.copysign(NAN, NAN)) # copysign(INF, NAN) may be INF or it may be NINF, since # we don't know whether the sign bit of NAN is set on any # given platform. assert math.isinf(math.copysign(INF, NAN)) # similarly, copysign(2., NAN) could be 2. or -2. assert abs(math.copysign(2.0, NAN)) == 2.0 assert str(math.frexp(0.0)) == str((+0.0, 0)) assert str(math.frexp(-0.0)) == str((-0.0, 0)) assert math.frexp(1) == (0.5, 1) assert math.frexp(1.5) == (0.75, 1) assert_raises(TypeError, lambda: math.frexp(None)) assert str(math.ldexp(+0.0, 0)) == str(0.0) assert str(math.ldexp(-0.0, 0)) == str(-0.0) assert math.ldexp(0.5, 1) == 1 assert math.ldexp(0.75, 1) == 1.5 assert_raises(TypeError, lambda: math.ldexp(None, None)) assert math.frexp(INF) == (INF, 0) assert str(math.frexp(NAN)) == str((NAN, 0)) assert_raises(TypeError, lambda: math.frexp(None)) assert math.gcd(0, 0) == 0 assert math.gcd(1, 0) == 1 assert math.gcd(0, 1) == 1 assert math.gcd(1, 1) == 1 assert math.gcd(-1, 1) == 1 assert math.gcd(1, -1) == 1 assert math.gcd(-1, -1) == 1 assert math.gcd(125, -255) == 5 assert_raises(TypeError, lambda: math.gcd(1.1, 2)) assert math.factorial(0) == 1 assert math.factorial(1) == 1 assert math.factorial(2) == 2 assert math.factorial(3) == 6 assert math.factorial(10) == 3628800 assert math.factorial(20) == 2432902008176640000 assert_raises(ValueError, lambda: math.factorial(-1)) if hasattr(math, "nextafter"): try: assert math.nextafter(4503599627370496.0, -INF) == 4503599627370495.5 assert math.nextafter(4503599627370496.0, INF) == 4503599627370497.0 assert math.nextafter(9223372036854775808.0, 0.0) == 9223372036854774784.0 assert math.nextafter(-9223372036854775808.0, 0.0) == -9223372036854774784.0 assert math.nextafter(4503599627370496, -INF) == 4503599627370495.5 assert math.nextafter(2.0, 2.0) == 2.0 assert math.isnan(math.nextafter(NAN, 1.0)) except NotImplementedError: # WASM pass assert math.modf(1.25) == (0.25, 1.0) assert math.modf(-1.25) == (-0.25, -1.0) assert math.modf(2.56) == (0.56, 2.0) assert math.modf(-2.56) == (-0.56, -2.0) assert math.modf(1) == (0.0, 1.0) assert math.modf(INF) == (0.0, INF) assert math.modf(NINF) == (-0.0, NINF) modf_nan = math.modf(NAN) assert math.isnan(modf_nan[0]) assert math.isnan(modf_nan[1]) assert math.fmod(10, 1) == 0.0 assert math.fmod(10, 0.5) == 0.0 assert math.fmod(10, 1.5) == 1.0 assert math.fmod(-10, 1) == -0.0 assert math.fmod(-10, 0.5) == -0.0 assert math.fmod(-10, 1.5) == -1.0 assert math.isnan(math.fmod(NAN, 1.0)) == True assert math.isnan(math.fmod(1.0, NAN)) == True assert math.isnan(math.fmod(NAN, NAN)) == True assert_raises(ValueError, lambda: math.fmod(1.0, 0.0)) assert_raises(ValueError, lambda: math.fmod(INF, 1.0)) assert_raises(ValueError, lambda: math.fmod(NINF, 1.0)) assert_raises(ValueError, lambda: math.fmod(INF, 0.0)) assert math.fmod(3.0, INF) == 3.0 assert math.fmod(-3.0, INF) == -3.0 assert math.fmod(3.0, NINF) == 3.0 assert math.fmod(-3.0, NINF) == -3.0 assert math.fmod(0.0, 3.0) == 0.0 assert math.fmod(0.0, NINF) == 0.0 assert math.gamma(1) == 1.0 # sumprod compares the two lengths as it goes; it must not drain either # argument first. assert_raises(ValueError, lambda: math.sumprod(itertools.count(), [1, 2, 3])) assert_raises(ValueError, lambda: math.sumprod([1, 2, 3], itertools.count())) assert math.sumprod(iter([1, 2, 3]), iter([4, 5, 6])) == 32 # perm/comb reject a k above 2**63 - 1 instead of computing, and perm(n) is factorial(n). for f in [ lambda: math.perm(2**63), lambda: math.perm(2**64 + 1, 2**63), lambda: math.comb(2**64 + 1, 2**63), ]: assert_raises(OverflowError, f) assert_raises( ValueError, lambda: math.perm(-1), _msg="factorial() not defined for negative values", ) assert math.perm(5) == math.perm(5, None) == 120 assert math.perm(2**64, 0) == 1 assert math.comb(2**64, 2**64 - 1) == 2**64