mirror of
https://github.com/pocketpy/pocketpy
synced 2026-05-06 18:23:38 +00:00
- Add __rtruediv__, __rpow__, __pos__, __bool__, __hash__, and coercion errors - Extend __pow__ for complex exponents and real-only complex unwrap - Tests for rdiv, rpow, bool, pos, float/int errors, hash, complex**complex - lru_cache: accept typed= and **kwargs for host-Python compatibility when PYTHONPATH shadows this module - Regenerate _generated.c from prebuild.py Made-with: Cursor
235 lines
6.5 KiB
Python
235 lines
6.5 KiB
Python
import math
|
|
|
|
class complex:
|
|
def __init__(self, real, imag=0):
|
|
self._real = float(real)
|
|
self._imag = float(imag)
|
|
|
|
@property
|
|
def real(self):
|
|
return self._real
|
|
|
|
@property
|
|
def imag(self):
|
|
return self._imag
|
|
|
|
def conjugate(self):
|
|
return complex(self.real, -self.imag)
|
|
|
|
def __repr__(self):
|
|
s = ['(', str(self.real)]
|
|
s.append('-' if self.imag < 0 else '+')
|
|
s.append(str(abs(self.imag)))
|
|
s.append('j)')
|
|
return ''.join(s)
|
|
|
|
def __eq__(self, other):
|
|
if type(other) is complex:
|
|
return self.real == other.real and self.imag == other.imag
|
|
if type(other) in (int, float):
|
|
return self.real == other and self.imag == 0
|
|
return NotImplemented
|
|
|
|
def __ne__(self, other):
|
|
res = self == other
|
|
if res is NotImplemented:
|
|
return res
|
|
return not res
|
|
|
|
def __add__(self, other):
|
|
if type(other) is complex:
|
|
return complex(self.real + other.real, self.imag + other.imag)
|
|
if type(other) in (int, float):
|
|
return complex(self.real + other, self.imag)
|
|
return NotImplemented
|
|
|
|
def __radd__(self, other):
|
|
return self.__add__(other)
|
|
|
|
def __sub__(self, other):
|
|
if type(other) is complex:
|
|
return complex(self.real - other.real, self.imag - other.imag)
|
|
if type(other) in (int, float):
|
|
return complex(self.real - other, self.imag)
|
|
return NotImplemented
|
|
|
|
def __rsub__(self, other):
|
|
if type(other) is complex:
|
|
return complex(other.real - self.real, other.imag - self.imag)
|
|
if type(other) in (int, float):
|
|
return complex(other - self.real, -self.imag)
|
|
return NotImplemented
|
|
|
|
def __mul__(self, other):
|
|
if type(other) is complex:
|
|
return complex(self.real * other.real - self.imag * other.imag,
|
|
self.real * other.imag + self.imag * other.real)
|
|
if type(other) in (int, float):
|
|
return complex(self.real * other, self.imag * other)
|
|
return NotImplemented
|
|
|
|
def __rmul__(self, other):
|
|
return self.__mul__(other)
|
|
|
|
def __truediv__(self, other):
|
|
if type(other) is complex:
|
|
denominator = other.real ** 2 + other.imag ** 2
|
|
real_part = (self.real * other.real + self.imag * other.imag) / denominator
|
|
imag_part = (self.imag * other.real - self.real * other.imag) / denominator
|
|
return complex(real_part, imag_part)
|
|
if type(other) in (int, float):
|
|
return complex(self.real / other, self.imag / other)
|
|
return NotImplemented
|
|
|
|
def __pow__(self, other):
|
|
if type(other) is complex and other.imag == 0:
|
|
other = other.real
|
|
if type(other) is complex:
|
|
r = self.__abs__()
|
|
th = phase(self)
|
|
lr = math.log(r) if r > 0 else float("-inf")
|
|
ox, oy = other.real, other.imag
|
|
wx = ox * lr - oy * th
|
|
wy = ox * th + oy * lr
|
|
if r == 0:
|
|
if oy == 0 and ox > 0:
|
|
return complex(0.0, 0.0)
|
|
return complex(float("nan"), float("nan"))
|
|
ex = math.exp(wx)
|
|
return complex(ex * math.cos(wy), ex * math.sin(wy))
|
|
if type(other) in (int, float):
|
|
return complex(self.__abs__() ** other * math.cos(other * phase(self)),
|
|
self.__abs__() ** other * math.sin(other * phase(self)))
|
|
return NotImplemented
|
|
|
|
def __abs__(self) -> float:
|
|
return math.sqrt(self.real ** 2 + self.imag ** 2)
|
|
|
|
def __neg__(self):
|
|
return complex(-self.real, -self.imag)
|
|
|
|
# New methods added below
|
|
def __rtruediv__(self, other):
|
|
if type(other) is complex:
|
|
return other / self
|
|
if type(other) in (int, float):
|
|
return complex(other) / self
|
|
return NotImplemented
|
|
|
|
def __rpow__(self, other):
|
|
if type(other) in (int, float):
|
|
return complex(other) ** self
|
|
if type(other) is complex:
|
|
return other ** self
|
|
return NotImplemented
|
|
|
|
def __pos__(self):
|
|
return complex(self.real, self.imag)
|
|
|
|
def __bool__(self):
|
|
return self.real != 0 or self.imag != 0
|
|
|
|
def __complex__(self):
|
|
return complex(self.real, self.imag)
|
|
|
|
def __float__(self):
|
|
raise TypeError("can't convert complex to float")
|
|
|
|
def __int__(self):
|
|
raise TypeError("can't convert complex to int")
|
|
|
|
def __index__(self):
|
|
raise TypeError("complex cannot be used as an index")
|
|
|
|
def __hash__(self):
|
|
return hash((self.real, self.imag))
|
|
|
|
|
|
# Conversions to and from polar coordinates
|
|
|
|
def phase(z: complex):
|
|
return math.atan2(z.imag, z.real)
|
|
|
|
def polar(z: complex):
|
|
return z.__abs__(), phase(z)
|
|
|
|
def rect(r: float, phi: float):
|
|
return r * math.cos(phi) + r * math.sin(phi) * 1j
|
|
|
|
# Power and logarithmic functions
|
|
|
|
def exp(z: complex):
|
|
return math.exp(z.real) * rect(1, z.imag)
|
|
|
|
def log(z: complex, base=2.718281828459045):
|
|
return math.log(z.__abs__(), base) + phase(z) * 1j
|
|
|
|
def log10(z: complex):
|
|
return log(z, 10)
|
|
|
|
def sqrt(z: complex):
|
|
return z ** 0.5
|
|
|
|
# Trigonometric functions
|
|
|
|
def acos(z: complex):
|
|
return -1j * log(z + sqrt(z * z - 1))
|
|
|
|
def asin(z: complex):
|
|
return -1j * log(1j * z + sqrt(1 - z * z))
|
|
|
|
def atan(z: complex):
|
|
return 1j / 2 * log((1 - 1j * z) / (1 + 1j * z))
|
|
|
|
def cos(z: complex):
|
|
return (exp(1j * z) + exp(-1j * z)) / 2
|
|
|
|
def sin(z: complex):
|
|
return (exp(1j * z) - exp(-1j * z)) / (2 * 1j)
|
|
|
|
def tan(z: complex):
|
|
return sin(z) / cos(z)
|
|
|
|
# Hyperbolic functions
|
|
|
|
def acosh(z: complex):
|
|
return log(z + sqrt(z * z - 1))
|
|
|
|
def asinh(z: complex):
|
|
return log(z + sqrt(z * z + 1))
|
|
|
|
def atanh(z: complex):
|
|
return 1 / 2 * log((1 + z) / (1 - z))
|
|
|
|
def cosh(z: complex):
|
|
return (exp(z) + exp(-z)) / 2
|
|
|
|
def sinh(z: complex):
|
|
return (exp(z) - exp(-z)) / 2
|
|
|
|
def tanh(z: complex):
|
|
return sinh(z) / cosh(z)
|
|
|
|
# Classification functions
|
|
|
|
def isfinite(z: complex):
|
|
return math.isfinite(z.real) and math.isfinite(z.imag)
|
|
|
|
def isinf(z: complex):
|
|
return math.isinf(z.real) or math.isinf(z.imag)
|
|
|
|
def isnan(z: complex):
|
|
return math.isnan(z.real) or math.isnan(z.imag)
|
|
|
|
def isclose(a: complex, b: complex):
|
|
return math.isclose(a.real, b.real) and math.isclose(a.imag, b.imag)
|
|
|
|
# Constants
|
|
|
|
pi = math.pi
|
|
e = math.e
|
|
tau = 2 * pi
|
|
inf = math.inf
|
|
infj = complex(0, inf)
|
|
nan = math.nan
|
|
nanj = complex(0, nan) |