# cmath

Mathematical functions for complex numbers.

https://docs.python.org/3/library/cmath.html

#### Source code

    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: int | float):
            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)

        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)