Use py_resource module
[python/dscho.git] / Lib / Complex.py
blobf4892f30b6a3350948932301ecf6e97ab951e1e9
1 # Complex numbers
2 # ---------------
4 # This module represents complex numbers as instances of the class Complex.
5 # A Complex instance z has two data attribues, z.re (the real part) and z.im
6 # (the imaginary part). In fact, z.re and z.im can have any value -- all
7 # arithmetic operators work regardless of the type of z.re and z.im (as long
8 # as they support numerical operations).
10 # The following functions exist (Complex is actually a class):
11 # Complex([re [,im]) -> creates a complex number from a real and an imaginary part
12 # IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes)
13 # Polar([r [,phi [,fullcircle]]]) ->
14 # the complex number z for which r == z.radius() and phi == z.angle(fullcircle)
15 # (r and phi default to 0)
17 # Complex numbers have the following methods:
18 # z.abs() -> absolute value of z
19 # z.radius() == z.abs()
20 # z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units
21 # z.phi([fullcircle]) == z.angle(fullcircle)
23 # These standard functions and unary operators accept complex arguments:
24 # abs(z)
25 # -z
26 # +z
27 # not z
28 # repr(z) == `z`
29 # str(z)
30 # hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero
31 # the result equals hash(z.re)
32 # Note that hex(z) and oct(z) are not defined.
34 # These conversions accept complex arguments only if their imaginary part is zero:
35 # int(z)
36 # long(z)
37 # float(z)
39 # The following operators accept two complex numbers, or one complex number
40 # and one real number (int, long or float):
41 # z1 + z2
42 # z1 - z2
43 # z1 * z2
44 # z1 / z2
45 # pow(z1, z2)
46 # cmp(z1, z2)
47 # Note that z1 % z2 and divmod(z1, z2) are not defined,
48 # nor are shift and mask operations.
50 # The standard module math does not support complex numbers.
51 # (I suppose it would be easy to implement a cmath module.)
53 # Idea:
54 # add a class Polar(r, phi) and mixed-mode arithmetic which
55 # chooses the most appropriate type for the result:
56 # Complex for +,-,cmp
57 # Polar for *,/,pow
60 import types, math
62 if not hasattr(math, 'hypot'):
63 def hypot(x, y):
64 # XXX I know there's a way to compute this without possibly causing
65 # overflow, but I can't remember what it is right now...
66 return math.sqrt(x*x + y*y)
67 math.hypot = hypot
69 twopi = math.pi*2.0
70 halfpi = math.pi/2.0
72 def IsComplex(obj):
73 return hasattr(obj, 're') and hasattr(obj, 'im')
75 def Polar(r = 0, phi = 0, fullcircle = twopi):
76 phi = phi * (twopi / fullcircle)
77 return Complex(math.cos(phi)*r, math.sin(phi)*r)
79 class Complex:
81 def __init__(self, re=0, im=0):
82 if IsComplex(re):
83 im = im + re.im
84 re = re.re
85 if IsComplex(im):
86 re = re - im.im
87 im = im.re
88 self.re = re
89 self.im = im
91 def __setattr__(self, name, value):
92 if hasattr(self, name):
93 raise TypeError, "Complex numbers have set-once attributes"
94 self.__dict__[name] = value
96 def __repr__(self):
97 if not self.im:
98 return 'Complex(%s)' % `self.re`
99 else:
100 return 'Complex(%s, %s)' % (`self.re`, `self.im`)
102 def __str__(self):
103 if not self.im:
104 return `self.re`
105 else:
106 return 'Complex(%s, %s)' % (`self.re`, `self.im`)
108 def __coerce__(self, other):
109 if IsComplex(other):
110 return self, other
111 return self, Complex(other) # May fail
113 def __cmp__(self, other):
114 return cmp(self.re, other.re) or cmp(self.im, other.im)
116 def __hash__(self):
117 if not self.im: return hash(self.re)
118 mod = sys.maxint + 1L
119 return int((hash(self.re) + 2L*hash(self.im) + mod) % (2L*mod) - mod)
121 def __neg__(self):
122 return Complex(-self.re, -self.im)
124 def __pos__(self):
125 return self
127 def __abs__(self):
128 return math.hypot(self.re, self.im)
129 ##return math.sqrt(self.re*self.re + self.im*self.im)
132 def __int__(self):
133 if self.im:
134 raise ValueError, "can't convert Complex with nonzero im to int"
135 return int(self.re)
137 def __long__(self):
138 if self.im:
139 raise ValueError, "can't convert Complex with nonzero im to long"
140 return long(self.re)
142 def __float__(self):
143 if self.im:
144 raise ValueError, "can't convert Complex with nonzero im to float"
145 return float(self.re)
147 def __nonzero__(self):
148 return not (self.re == self.im == 0)
150 abs = radius = __abs__
152 def angle(self, fullcircle = twopi):
153 return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi)
155 phi = angle
157 def __add__(self, other):
158 return Complex(self.re + other.re, self.im + other.im)
160 __radd__ = __add__
162 def __sub__(self, other):
163 return Complex(self.re - other.re, self.im - other.im)
165 def __rsub__(self, other):
166 return Complex(other.re - self.re, other.im - self.im)
168 def __mul__(self, other):
169 return Complex(self.re*other.re - self.im*other.im,
170 self.re*other.im + self.im*other.re)
172 __rmul__ = __mul__
174 def __div__(self, other):
175 # Deviating from the general principle of not forcing re or im
176 # to be floats, we cast to float here, otherwise division
177 # of Complex numbers with integer re and im parts would use
178 # the (truncating) integer division
179 d = float(other.re*other.re + other.im*other.im)
180 if not d: raise ZeroDivisionError, 'Complex division'
181 return Complex((self.re*other.re + self.im*other.im) / d,
182 (self.im*other.re - self.re*other.im) / d)
184 def __rdiv__(self, other):
185 return other / self
187 def __pow__(self, n, z=None):
188 if z is not None:
189 raise TypeError, 'Complex does not support ternary pow()'
190 if IsComplex(n):
191 if n.im: raise TypeError, 'Complex to the Complex power'
192 n = n.re
193 r = pow(self.abs(), n)
194 phi = n*self.angle()
195 return Complex(math.cos(phi)*r, math.sin(phi)*r)
197 def __rpow__(self, base):
198 return pow(base, self)
201 # Everything below this point is part of the test suite
203 def checkop(expr, a, b, value, fuzz = 1e-6):
204 import sys
205 print ' ', a, 'and', b,
206 try:
207 result = eval(expr)
208 except:
209 result = sys.exc_type
210 print '->', result
211 if (type(result) == type('') or type(value) == type('')):
212 ok = result == value
213 else:
214 ok = abs(result - value) <= fuzz
215 if not ok:
216 print '!!\t!!\t!! should be', value, 'diff', abs(result - value)
219 def test():
220 testsuite = {
221 'a+b': [
222 (1, 10, 11),
223 (1, Complex(0,10), Complex(1,10)),
224 (Complex(0,10), 1, Complex(1,10)),
225 (Complex(0,10), Complex(1), Complex(1,10)),
226 (Complex(1), Complex(0,10), Complex(1,10)),
228 'a-b': [
229 (1, 10, -9),
230 (1, Complex(0,10), Complex(1,-10)),
231 (Complex(0,10), 1, Complex(-1,10)),
232 (Complex(0,10), Complex(1), Complex(-1,10)),
233 (Complex(1), Complex(0,10), Complex(1,-10)),
235 'a*b': [
236 (1, 10, 10),
237 (1, Complex(0,10), Complex(0, 10)),
238 (Complex(0,10), 1, Complex(0,10)),
239 (Complex(0,10), Complex(1), Complex(0,10)),
240 (Complex(1), Complex(0,10), Complex(0,10)),
242 'a/b': [
243 (1., 10, 0.1),
244 (1, Complex(0,10), Complex(0, -0.1)),
245 (Complex(0, 10), 1, Complex(0, 10)),
246 (Complex(0, 10), Complex(1), Complex(0, 10)),
247 (Complex(1), Complex(0,10), Complex(0, -0.1)),
249 'pow(a,b)': [
250 (1, 10, 1),
251 (1, Complex(0,10), 'TypeError'),
252 (Complex(0,10), 1, Complex(0,10)),
253 (Complex(0,10), Complex(1), Complex(0,10)),
254 (Complex(1), Complex(0,10), 'TypeError'),
255 (2, Complex(4,0), 16),
257 'cmp(a,b)': [
258 (1, 10, -1),
259 (1, Complex(0,10), 1),
260 (Complex(0,10), 1, -1),
261 (Complex(0,10), Complex(1), -1),
262 (Complex(1), Complex(0,10), 1),
265 exprs = testsuite.keys()
266 exprs.sort()
267 for expr in exprs:
268 print expr + ':'
269 t = (expr,)
270 for item in testsuite[expr]:
271 apply(checkop, t+item)
274 if __name__ == '__main__':
275 test()