blob: 7c5f4a555a5a8ca62dbecb12ae7728657078fd40 [file] [log] [blame]
Guido van Rossumd8faa362007-04-27 19:54:29 +00001from test.test_support import run_unittest
2import unittest
Raymond Hettingerb67ad7e2004-06-14 07:40:10 +00003import cmath, math
Raymond Hettingerb67ad7e2004-06-14 07:40:10 +00004
Guido van Rossumd8faa362007-04-27 19:54:29 +00005class CMathTests(unittest.TestCase):
6 # list of all functions in cmath
7 test_functions = [getattr(cmath, fname) for fname in [
8 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh',
9 'cos', 'cosh', 'exp', 'log', 'log10', 'sin', 'sinh',
10 'sqrt', 'tan', 'tanh']]
11 # test first and second arguments independently for 2-argument log
12 test_functions.append(lambda x : cmath.log(x, 1729. + 0j))
13 test_functions.append(lambda x : cmath.log(14.-27j, x))
Raymond Hettingerb67ad7e2004-06-14 07:40:10 +000014
Guido van Rossumd8faa362007-04-27 19:54:29 +000015 def cAssertAlmostEqual(self, a, b, rel_eps = 1e-10, abs_eps = 1e-100):
16 """Check that two complex numbers are almost equal."""
17 # the two complex numbers are considered almost equal if
18 # either the relative error is <= rel_eps or the absolute error
19 # is tiny, <= abs_eps.
20 if a == b == 0:
21 return
22 absolute_error = abs(a-b)
23 relative_error = absolute_error/max(abs(a), abs(b))
24 if relative_error > rel_eps and absolute_error > abs_eps:
25 self.fail("%s and %s are not almost equal" % (a, b))
Raymond Hettingerb67ad7e2004-06-14 07:40:10 +000026
Guido van Rossumd8faa362007-04-27 19:54:29 +000027 def test_constants(self):
28 e_expected = 2.71828182845904523536
29 pi_expected = 3.14159265358979323846
Jeffrey Yasskin48952d32007-09-07 15:45:05 +000030 self.assertAlmostEqual(cmath.pi, pi_expected, places=9,
31 msg="cmath.pi is %s; should be %s" % (cmath.pi, pi_expected))
32 self.assertAlmostEqual(cmath.e, e_expected, places=9,
33 msg="cmath.e is %s; should be %s" % (cmath.e, e_expected))
Roger E. Masse3daddda1996-12-09 22:59:15 +000034
Guido van Rossumd8faa362007-04-27 19:54:29 +000035 def test_user_object(self):
36 # Test automatic calling of __complex__ and __float__ by cmath
37 # functions
Roger E. Massefab8ab81996-12-20 22:36:52 +000038
Guido van Rossumd8faa362007-04-27 19:54:29 +000039 # some random values to use as test values; we avoid values
40 # for which any of the functions in cmath is undefined
41 # (i.e. 0., 1., -1., 1j, -1j) or would cause overflow
42 cx_arg = 4.419414439 + 1.497100113j
43 flt_arg = -6.131677725
Roger E. Massefab8ab81996-12-20 22:36:52 +000044
Guido van Rossumd8faa362007-04-27 19:54:29 +000045 # a variety of non-complex numbers, used to check that
46 # non-complex return values from __complex__ give an error
47 non_complexes = ["not complex", 1, 5, 2., None,
48 object(), NotImplemented]
49
50 # Now we introduce a variety of classes whose instances might
51 # end up being passed to the cmath functions
52
53 # usual case: new-style class implementing __complex__
54 class MyComplex(object):
55 def __init__(self, value):
56 self.value = value
57 def __complex__(self):
58 return self.value
59
60 # old-style class implementing __complex__
61 class MyComplexOS:
62 def __init__(self, value):
63 self.value = value
64 def __complex__(self):
65 return self.value
66
67 # classes for which __complex__ raises an exception
68 class SomeException(Exception):
69 pass
70 class MyComplexException(object):
71 def __complex__(self):
72 raise SomeException
73 class MyComplexExceptionOS:
74 def __complex__(self):
75 raise SomeException
76
77 # some classes not providing __float__ or __complex__
78 class NeitherComplexNorFloat(object):
79 pass
80 class NeitherComplexNorFloatOS:
81 pass
82 class MyInt(object):
83 def __int__(self): return 2
84 def __long__(self): return 2
85 def __index__(self): return 2
86 class MyIntOS:
87 def __int__(self): return 2
88 def __long__(self): return 2
89 def __index__(self): return 2
90
91 # other possible combinations of __float__ and __complex__
92 # that should work
93 class FloatAndComplex(object):
94 def __float__(self):
95 return flt_arg
96 def __complex__(self):
97 return cx_arg
98 class FloatAndComplexOS:
99 def __float__(self):
100 return flt_arg
101 def __complex__(self):
102 return cx_arg
103 class JustFloat(object):
104 def __float__(self):
105 return flt_arg
106 class JustFloatOS:
107 def __float__(self):
108 return flt_arg
109
110 for f in self.test_functions:
111 # usual usage
112 self.cAssertAlmostEqual(f(MyComplex(cx_arg)), f(cx_arg))
113 self.cAssertAlmostEqual(f(MyComplexOS(cx_arg)), f(cx_arg))
114 # other combinations of __float__ and __complex__
115 self.cAssertAlmostEqual(f(FloatAndComplex()), f(cx_arg))
116 self.cAssertAlmostEqual(f(FloatAndComplexOS()), f(cx_arg))
117 self.cAssertAlmostEqual(f(JustFloat()), f(flt_arg))
118 self.cAssertAlmostEqual(f(JustFloatOS()), f(flt_arg))
119 # TypeError should be raised for classes not providing
120 # either __complex__ or __float__, even if they provide
121 # __int__, __long__ or __index__. An old-style class
122 # currently raises AttributeError instead of a TypeError;
123 # this could be considered a bug.
124 self.assertRaises(TypeError, f, NeitherComplexNorFloat())
125 self.assertRaises(TypeError, f, MyInt())
126 self.assertRaises(Exception, f, NeitherComplexNorFloatOS())
127 self.assertRaises(Exception, f, MyIntOS())
128 # non-complex return value from __complex__ -> TypeError
129 for bad_complex in non_complexes:
130 self.assertRaises(TypeError, f, MyComplex(bad_complex))
131 self.assertRaises(TypeError, f, MyComplexOS(bad_complex))
132 # exceptions in __complex__ should be propagated correctly
133 self.assertRaises(SomeException, f, MyComplexException())
134 self.assertRaises(SomeException, f, MyComplexExceptionOS())
135
136 def test_input_type(self):
137 # ints and longs should be acceptable inputs to all cmath
138 # functions, by virtue of providing a __float__ method
139 for f in self.test_functions:
140 for arg in [2, 2.]:
141 self.cAssertAlmostEqual(f(arg), f(arg.__float__()))
142
143 # but strings should give a TypeError
144 for f in self.test_functions:
145 for arg in ["a", "long_string", "0", "1j", ""]:
146 self.assertRaises(TypeError, f, arg)
147
148 def test_cmath_matches_math(self):
149 # check that corresponding cmath and math functions are equal
150 # for floats in the appropriate range
151
152 # test_values in (0, 1)
153 test_values = [0.01, 0.1, 0.2, 0.5, 0.9, 0.99]
154
155 # test_values for functions defined on [-1., 1.]
156 unit_interval = test_values + [-x for x in test_values] + \
157 [0., 1., -1.]
158
159 # test_values for log, log10, sqrt
160 positive = test_values + [1.] + [1./x for x in test_values]
161 nonnegative = [0.] + positive
162
163 # test_values for functions defined on the whole real line
164 real_line = [0.] + positive + [-x for x in positive]
165
166 test_functions = {
167 'acos' : unit_interval,
168 'asin' : unit_interval,
169 'atan' : real_line,
170 'cos' : real_line,
171 'cosh' : real_line,
172 'exp' : real_line,
173 'log' : positive,
174 'log10' : positive,
175 'sin' : real_line,
176 'sinh' : real_line,
177 'sqrt' : nonnegative,
178 'tan' : real_line,
179 'tanh' : real_line}
180
181 for fn, values in test_functions.items():
182 float_fn = getattr(math, fn)
183 complex_fn = getattr(cmath, fn)
184 for v in values:
185 self.cAssertAlmostEqual(float_fn(v), complex_fn(v))
186
187 # test two-argument version of log with various bases
188 for base in [0.5, 2., 10.]:
189 for v in positive:
190 self.cAssertAlmostEqual(cmath.log(v, base), math.log(v, base))
191
192def test_main():
193 run_unittest(CMathTests)
194
195if __name__ == "__main__":
196 test_main()