#!/usr/bin/env python3
"""Implementation of perl Math::Complex package"""

__author__ = """Pythonizer"""
___email__ = 'snoopyjc@gmail.com'
__version__ = '1.014'

# Generated by "pythonizer -v3 -d5 ../Math/Complex.pm" v0.966 run by JO2742 on Tue Mar 29 17:57:14 2022
#
# Complex numbers and associated mathematical functions
# -- Raphael Manfredi	Since Sep 1996
# -- Jarkko Hietaniemi	Since Mar 1997
# -- Daniel S. Lewart	Since Sep 1997
# -- Joe Cool 3/26/2022 - Minor changes to generated pythonized version
#

import types, builtins, re, signal, perllib, math

_str = lambda s: "" if s is None else str(s)
_locals_stack = []
from perllib import Die

perllib.init_package("Math.Complex", is_class=True, isa="Exporter".split())


def Inf(*_args):
    return Math.Complex.Inf_v


Math.Complex.Inf = Inf

#
# display_format
# ->display_format
#
# Set (get if no argument) the display format for all complex numbers that
# don't happen to have overridden it via ->display_format
#
# When called as an object method, this actually sets the display format for
# the current object.
#
# Valid object formats are 'c' and 'p' for cartesian and polar. The first
# letter is used actually, so the type can be fully spelled out for clarity.
#


def display_format(*_args, wantarray=False):
    _args = list(_args)
    global DISPLAY_FORMAT

    new_h = perllib.Hash()
    obj = perllib.Hash()
    self = _args.pop(0) if _args else None
    display_format_h = perllib.Hash(DISPLAY_FORMAT)

    if perllib.ref(self):
        if "display_format" in self:
            obj = perllib.Hash(self.get("display_format"))
            perllib.assign_hash(display_format_h, obj.keys(), obj.values())

    if len(_args) == 1:
        display_format_h["style"] = _args.pop(0) if _args else None
    else:
        new_h = perllib.Hash({_args[_i]: _args[_i + 1] for _i in range(0, len(_args), 2)})
        perllib.assign_hash(display_format_h, new_h.keys(), new_h.values())

    if perllib.ref(self):
        self["display_format"] = display_format_h.copy()
        return self.get("display_format") if wantarray else self["display_format"].get("style")

    # Called as a class method

    DISPLAY_FORMAT = perllib.Hash(display_format_h)
    return DISPLAY_FORMAT if wantarray else DISPLAY_FORMAT.get("style")


Math.Complex.display_format = display_format

#
# (_stringify)
#
# Show nicely formatted complex number under its cartesian or polar form,
# depending on the current display format:
#
# . If a specific display format has been recorded for this object, use it.
# . Otherwise, use the generic current default for all complex numbers,
#   which is a package global variable.
#

#
# _logofzero
#
# Die on logarithm of zero.
#


def _logofzero(*_args):
    mess = f"{_args[0]}: Logarithm of zero.\n"

    if 1 < len(_args) and _args[1] is not None:
        mess += f"(Because in the definition of {_args[0]}, the argument "
        if not (_str(_args[1]) == "0"):
            mess += f"{_args[1]} "

        mess += "is 0)\n"

    up = perllib.Array(perllib.caller(1))

    mess += f"Died at {up[1]} line {up[2]}.\n"

    raise Die(mess)


Math.Complex._logofzero = _logofzero

#
# (log)
#
# Compute log(z).
#

#
# _rootbad
#
# Die on bad root.
#


def _rootbad(*_args):
    mess = f"Root '{_args[0]}' illegal, root rank must be positive integer.\n"

    up = perllib.Array(perllib.caller(1))

    mess += f"Died at {up[1]} line {up[2]}.\n"

    raise Die(mess)


Math.Complex._rootbad = _rootbad

#
# root
#
# Computes all nth root for z, returning an array whose size is n.
# `n' must be a positive integer.
#
# The roots are given by (for k = 0..n-1):
#
# z^(1/n) = r^(1/n) (cos ((t+2 k pi)/n) + i sin ((t+2 k pi)/n))
#

#
# _divbyzero
#
# Die on division by zero.
#


def _divbyzero(*_args):
    mess = f"{_args[0]}: Division by zero.\n"

    if 1 < len(_args) and _args[1] is not None:
        mess += f"(Because in the definition of {_args[0]}, the divisor "
        if not (f"{_args[1]}" == "0"):
            mess += f"{_args[1]} "

        mess += "is 0)\n"

    up = perllib.Array(perllib.caller(1))

    mess += f"Died at {up[1]} line {up[2]}.\n"

    raise Die(mess)


Math.Complex._divbyzero = _divbyzero

#
# (_divide)
#
# Computes z1/z2.
#


def _set_polar(*_args):
    _args[0]["c_dirty"] = perllib.num(_args[0]["c_dirty"]) + 1
    _args[0]["p_dirty"] = 0
    _args[0]["polar"] = _args[1]
    return _args[0].get("polar")


Math.Complex._set_polar = _set_polar

#
# ->_update_cartesian
#
# Recompute and return the cartesian form, given accurate polar form.
#


def _set_cartesian(*_args):
    _args[0]["p_dirty"] = perllib.num(_args[0]["p_dirty"]) + 1
    _args[0]["c_dirty"] = 0
    _args[0]["cartesian"] = _args[1]
    return _args[0].get("cartesian")


Math.Complex._set_cartesian = _set_cartesian


def _copy(*_args):
    _args = list(_args)
    self = _args.pop(0) if _args else None
    clone = self.copy()
    if self.get("cartesian"):
        clone["cartesian"] = [self.get("cartesian")]

    if self.get("polar"):
        clone["polar"] = [self.get("polar")]

    clone = perllib.bless(clone, "Math.Complex")
    return clone


Math.Complex._copy = _copy

#
# ->make
#
# Create a new complex number (cartesian form)
#


def _make(*_args):
    _args = list(_args)
    arg_v = _args.pop(0) if _args else None
    p = q = None

    if _m := re.search(rf"^{gre.pattern}$", _str(arg_v)):
        [p, q] = perllib.list_of_n((_m.group(1), 0), 2)
    elif _m := re.search(rf"^(?:{gre.pattern})?{gre.pattern}\s*i\s*$", _str(arg_v)):
        [p, q] = perllib.list_of_n((_m.group(1) or 0, _m.group(2)), 2)
    elif _m := re.search(
        rf"^\s*\(\s*{gre.pattern}\s*(?:,\s*{gre.pattern}\s*)?\)\s*$", _str(arg_v)
    ):
        [p, q] = perllib.list_of_n((_m.group(1), _m.group(2) or 0), 2)

    if p is not None:
        p = re.sub(r"^\+", r"", _str(p), count=1)
        if Math.Complex.has_inf_v:

            def _f215(_m):
                return f"{_m.group(1)}9**9**9"

            p = re.sub(re.compile(r"^(-?)inf$"), _f215, _str(p), count=1)

        q = re.sub(r"^\+", r"", _str(q), count=1)
        if Math.Complex.has_inf_v:

            def _f217(_m):
                return f"{_m.group(1)}9**9**9"

            q = re.sub(re.compile(r"^(-?)inf$"), _f217, _str(q), count=1)

    return perllib.Array([p, q])


Math.Complex._make = _make

#
# Object attributes (internal):
# 	cartesian	[real, imaginary] -- cartesian form
# 	polar		[rho, theta] -- polar form
# 	c_dirty		cartesian form not up-to-date
# 	p_dirty		polar form not up-to-date
# 	display		display format (package's global when not set)
#

# Die on bad *make() arguments.


def _cannot_make(*_args):
    raise Die(
        f"{perllib.LIST_SEPARATOR.join(map(_str,(perllib.caller(1))[3]))}: Cannot take {_args[0]} of '{_args[1]}'.\n"
    )


Math.Complex._cannot_make = _cannot_make


def make(*_args):
    _args = list(_args)
    self = perllib.bless(perllib.Hash(), (_args.pop(0) if _args else None))
    re_ = im = None
    if len(_args) == 0:
        [re_, im] = perllib.list_of_n((0, 0), 2)
    elif len(_args) == 1:
        if re.search(r"^\s*\[", _str(_args[0])):
            return getattr(builtins, perllib.ref(self)).emake(_args[0])

        [re_, im] = perllib.list_of_n(_make(_args[0]), 2)
    elif len(_args) == 2:
        [re_, im] = perllib.list_of_n(_args, 2)

    if re_ is not None:
        if not ((_m := re.search(rf"^{gre.pattern}$", _str(re_)))):
            _cannot_make("real part", re_)

    im = im or 0
    if not ((_m := re.search(rf"^{gre.pattern}$", _str(im)))):
        _cannot_make("imaginary part", im)

    self._set_cartesian([re_, im])
    self.display_format("cartesian")

    return self


Math.Complex.make = types.MethodType(make, Math.Complex)

#
# ->emake
#
# Create a new complex number (exponential form)
#
# For backward compatibility only.

#
# cplx
#
# Creates a complex number from a (re, im) tuple.
# This avoids the burden of writing Math::Complex->make(re, im).
#


def cplx(*_args):
    return getattr(builtins, "Math.Complex").make(*_args)


Math.Complex.cplx = cplx

#
# cplxe
#
# Creates a complex number from a (rho, theta) tuple.
# This avoids the burden of writing Math::Complex->emake(rho, theta).
#


def new(*_args):
    return make(*_args)


Math.Complex.new = types.MethodType(new, Math.Complex)


def emake(*_args):
    _args = list(_args)
    self = perllib.bless(perllib.Hash(), (_args.pop(0) if _args else None))
    rho_v = theta_v = None
    if len(_args) == 0:
        [rho_v, theta_v] = perllib.list_of_n((0, 0), 2)
    elif len(_args) == 1:
        if (re.search(r"^\s*\(", _str(_args[0]))) or (re.search(r"i\s*$", _str(_args[0]))):
            return getattr(builtins, perllib.ref(self)).make(_args[0])

        [rho_v, theta_v] = perllib.list_of_n(_emake(_args[0]), 2)
    elif len(_args) == 2:
        [rho_v, theta_v] = perllib.list_of_n(_args, 2)

    if rho_v is not None and theta_v is not None:
        if perllib.num(rho_v) < 0:
            rho_v = -perllib.num(rho_v)
            theta_v = (
                perllib.num(theta_v) + pi()
                if (perllib.num(theta_v) <= 0)
                else perllib.num(theta_v) - pi()
            )

    if rho_v is not None:
        if not ((_m := re.search(rf"^{gre.pattern}$", _str(rho_v)))):
            _cannot_make("rho", rho_v)

    theta_v = theta_v or 0
    if not ((_m := re.search(rf"^{gre.pattern}$", _str(theta_v)))):
        _cannot_make("theta", theta_v)

    self._set_polar([rho_v, theta_v])
    self.display_format("polar")

    return self


Math.Complex.emake = types.MethodType(emake, Math.Complex)

#
# (exp)
#
# Computes exp(z).
#


def exp(*_args):
    [z] = perllib.list_of_n(_args if _args else _d, 1)
    if not perllib.ref(z):
        return math.exp(perllib.flt(z))

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    return getattr(builtins, perllib.ref(z)).emake(math.exp(perllib.flt(x)), y)


Math.Complex.exp = exp

#
# (cos)
#
# Compute cos(z) = (exp(iz) + exp(-iz))/2.
#


def cos(*_args):
    [z] = perllib.list_of_n(_args if _args else _d, 1)
    if not perllib.ref(z):
        return math.cos(perllib.flt(z))

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    ey = math.exp(perllib.flt(y))
    sx = math.sin(perllib.flt(x))
    cx = math.cos(perllib.flt(x))
    ey_1 = 1 / ey if ey else Inf()
    return getattr(builtins, perllib.ref(z)).make(
        cx * (ey + perllib.num(ey_1)) / 2, sx * (perllib.num(ey_1) - ey) / 2
    )


Math.Complex.cos = cos

#
# (sin)
#
# Compute sin(z) = (exp(iz) - exp(-iz))/2.
#

#
# sec
#
# Computes the secant sec(z) = 1 / cos(z).
#


def sec(*_args):
    [z] = perllib.list_of_n(_args, 1)
    cz = cos(z)
    if perllib.num(cz) == 0:
        _divbyzero(f"sec({z})", f"cos({z})")

    return 1 / perllib.num(cz)


Math.Complex.sec = sec

#
# csc
#
# Computes the cosecant csc(z) = 1 / sin(z).
#


def _update_cartesian(*_args):
    _args = list(_args)
    self = _args.pop(0) if _args else None
    [r, t] = perllib.list_of_n(self.get("polar"), 2)
    self["c_dirty"] = 0
    return perllib.set_element(
        self,
        "cartesian",
        [perllib.num(r) * math.cos(perllib.flt(t)), perllib.num(r) * math.sin(perllib.flt(t))],
    )


Math.Complex._update_cartesian = _update_cartesian

#
#
# ->_update_polar
#
# Recompute and return the polar form, given accurate cartesian form.
#

#
# Attribute access/set routines
#


def _cartesian(*_args):
    return _args[0]._update_cartesian() if _args[0].get("c_dirty") else _args[0].get("cartesian")


Math.Complex._cartesian = _cartesian

#
# ->_stringify_cartesian
#
# Stringify as a cartesian representation 'a+bi'.
#


def _stringify_cartesian(*_args):
    _args = list(_args)
    z = _args.pop(0) if _args else None
    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    re_ = im = None

    format_ = perllib.Hash(z.display_format(wantarray=True))
    format_v = format_.get("format")

    if x:
        if re.search(re.compile(r"^NaN[QS]?$", re.I), _str(x)):
            re_ = x
        else:
            if re.search(re.compile(rf"^-?Q{Math.Complex.Inf_v}E$", re.I), _str(x)):
                re_ = x
            else:
                re_ = perllib.format_(_str(format_v), x) if format_v is not None else x
    else:
        re_ = None

    if y:
        if _m := re.search(re.compile(r"^(NaN[QS]?)$", re.I), _str(y)):
            im = y
        else:
            if re.search(re.compile(rf"^-?Q{Math.Complex.Inf_v}E$", re.I), _str(y)):
                im = y
            else:
                im = (
                    perllib.format_(_str(format_v), y)
                    if format_v is not None
                    else ("" if perllib.num(y) == 1 else ("-" if perllib.num(y) == -1 else y))
                )

        im = _str(im) + "i"
    else:
        im = None

    str_ = re_

    if im is not None:
        if perllib.num(y) < 0:
            str_ = _str(str_) + _str(im)
        elif perllib.num(y) > 0 or (re.search(re.compile(r"^NaN[QS]?i$", re.I), _str(im))):
            if re_ is not None:
                str_ = _str(str_) + "+"

            str_ = _str(str_) + _str(im)
    elif re_ is None:
        str_ = "0"

    return str_


Math.Complex._stringify_cartesian = _stringify_cartesian

#
# ->_stringify_polar
#
# Stringify as a polar representation '[r,t]'.
#


def sin(*_args):
    [z] = perllib.list_of_n(_args if _args else _d, 1)
    if not perllib.ref(z):
        return math.sin(perllib.flt(z))

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    ey = math.exp(perllib.flt(y))
    sx = math.sin(perllib.flt(x))
    cx = math.cos(perllib.flt(x))
    ey_1 = 1 / ey if ey else Inf()
    return getattr(builtins, perllib.ref(z)).make(
        sx * (ey + perllib.num(ey_1)) / 2, cx * (ey - perllib.num(ey_1)) / 2
    )


Math.Complex.sin = sin

#
# tan
#
# Compute tan(z) = sin(z) / cos(z).
#

#
# sinh
#
# Computes the hyperbolic sine sinh(z) = (exp(z) - exp(-z))/2.
#


def sinh(*_args):
    [z] = perllib.list_of_n(_args, 1)
    ex = 0.0
    if not perllib.ref(z):
        if perllib.num(z) == 0:
            return 0

        ex = math.exp(perllib.flt(z))
        return (
            (Inf() if ex == perllib.num(Math.Complex.ExpInf_v) else (ex - 1 / ex) / 2)
            if ex
            else -perllib.num(Inf())
        )

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    cy = math.cos(perllib.flt(y))
    sy = math.sin(perllib.flt(y))
    ex = math.exp(perllib.flt(x))
    ex_1 = 1 / ex if ex else Inf()
    return getattr(builtins, perllib.ref(z)).make(
        math.cos(perllib.flt(y)) * (ex - perllib.num(ex_1)) / 2,
        math.sin(perllib.flt(y)) * (ex + perllib.num(ex_1)) / 2,
    )


Math.Complex.sinh = sinh

#
# tanh
#
# Computes the hyperbolic tangent tanh(z) = sinh(z) / cosh(z).
#

#
# csch
#
# Computes the hyperbolic cosecant csch(z) = 1 / sinh(z).
#


def csch(*_args):
    [z] = perllib.list_of_n(_args, 1)
    sz = sinh(z)
    if perllib.num(sz) == 0:
        _divbyzero(f"csch({z})", f"sinh({z})")

    return 1 / perllib.num(sz)


Math.Complex.csch = csch

#
# cosech
#
# Alias for csch().
#


def cosech(*_args):
    return Math.Complex.csch(*_args)


Math.Complex.cosech = cosech

#
# coth
#
# Computes the hyperbolic cotangent coth(z) = cosh(z) / sinh(z).
#

#
# cosh
#
# Computes the hyperbolic cosine cosh(z) = (exp(z) + exp(-z))/2.
#


def cosh(*_args):
    [z] = perllib.list_of_n(_args, 1)
    ex = 0.0
    if not perllib.ref(z):
        ex = math.exp(perllib.flt(z))
        return (
            (Inf() if ex == perllib.num(Math.Complex.ExpInf_v) else (ex + 1 / ex) / 2)
            if ex
            else Inf()
        )

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    ex = math.exp(perllib.flt(x))
    ex_1 = 1 / ex if ex else Inf()
    return getattr(builtins, perllib.ref(z)).make(
        math.cos(perllib.flt(y)) * (ex + perllib.num(ex_1)) / 2,
        math.sin(perllib.flt(y)) * (ex - perllib.num(ex_1)) / 2,
    )


Math.Complex.cosh = cosh


def coth(*_args):
    [z] = perllib.list_of_n(_args, 1)
    sz = sinh(z)
    if perllib.num(sz) == 0:
        _divbyzero(f"coth({z})", f"sinh({z})")

    cz = cosh(z)
    if perllib.num(cz) == perllib.num(sz):
        return 1

    if perllib.num(cz) == -perllib.num(sz):
        return -1

    return perllib.num(cz) / perllib.num(sz)


Math.Complex.coth = coth

#
# cotanh
#
# Alias for coth().
#


def cotanh(*_args):
    return Math.Complex.coth(*_args)


Math.Complex.cotanh = cotanh

#
# acosh
#
# Computes the area/inverse hyperbolic cosine acosh(z) = log(z + sqrt(z*z-1)).
#

#
# sech
#
# Computes the hyperbolic secant sech(z) = 1 / cosh(z).
#


def sech(*_args):
    [z] = perllib.list_of_n(_args, 1)
    cz = cosh(z)
    if perllib.num(cz) == 0:
        _divbyzero(f"sech({z})", f"cosh({z})")

    return 1 / perllib.num(cz)


Math.Complex.sech = sech


def tanh(*_args):
    [z] = perllib.list_of_n(_args, 1)
    cz = cosh(z)
    if perllib.num(cz) == 0:
        _divbyzero(f"tanh({z})", f"cosh({z})")

    sz = sinh(z)
    if perllib.num(cz) == perllib.num(sz):
        return 1

    if perllib.num(cz) == -perllib.num(sz):
        return -1

    return perllib.num(sz) / perllib.num(cz)


Math.Complex.tanh = tanh

#
# cot
#
# Computes cot(z) = cos(z) / sin(z).
#


def cot(*_args):
    [z] = perllib.list_of_n(_args, 1)
    sz = sin(z)
    if perllib.num(sz) == 0:
        _divbyzero(f"cot({z})", f"sin({z})")

    return perllib.num(cos(z)) / perllib.num(sz)


Math.Complex.cot = cot

#
# cotan
#
# Alias for cot().
#


def cotan(*_args):
    return Math.Complex.cot(*_args)


Math.Complex.cotan = cotan

#
# acos
#
# Computes the arc cosine acos(z) = -i log(z + sqrt(z*z-1)).
#


def csc(*_args):
    [z] = perllib.list_of_n(_args, 1)
    sz = sin(z)
    if perllib.num(sz) == 0:
        _divbyzero(f"csc({z})", f"sin({z})")

    return 1 / perllib.num(sz)


Math.Complex.csc = csc

#
# cosec
#
# Alias for csc().
#


def cosec(*_args):
    return Math.Complex.csc(*_args)


Math.Complex.cosec = cosec


def tan(*_args):
    [z] = perllib.list_of_n(_args, 1)
    cz = cos(z)
    if perllib.num(cz) == 0:
        _divbyzero(f"tan({z})", f"cos({z})")

    return perllib.num(sin(z)) / perllib.num(cz)


Math.Complex.tan = tan

#
# Im
#
# Return or set Im(z).
#


def Im(*_args):
    [z, Im_v] = perllib.list_of_n(_args, 2)
    if not perllib.ref(z):
        return 0

    if Im_v is not None:
        z["cartesian"] = [(z._cartesian())[0], Im_v]
        z["c_dirty"] = 0
        z["p_dirty"] = 1
    else:
        return (z._cartesian())[1]


Math.Complex.Im = Im

#
# rho
#
# Return or set rho(w).
#

#
# Re
#
# Return or set Re(z).
#


def Re(*_args):
    [z, Re_v] = perllib.list_of_n(_args, 2)
    if not perllib.ref(z):
        return z

    if Re_v is not None:
        z["cartesian"] = [Re_v, (z._cartesian())[1]]
        z["c_dirty"] = 0
        z["p_dirty"] = 1
    else:
        return (z._cartesian())[0]


Math.Complex.Re = Re

#
# (_numeq)
#
# Computes z1 == z2.
#
# (Required in addition to _spaceship() because of NaNs.)


def _numeq(*_args):
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    [re1, im1] = perllib.list_of_n(z1._cartesian() if perllib.ref(z1) else (z1, 0), 2)
    [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref(z2) else (z2, 0), 2)
    return (
        1 if perllib.num(re1) == perllib.num(re2) and perllib.num(im1) == perllib.num(im2) else 0
    )


Math.Complex._numeq = _numeq

#
# (_negate)
#
# Computes -z.
#

#
# (_spaceship)
#
# Computes z1 <=> z2.
# Sorts on the real part first, then on the imaginary part. Thus 2-4i < 3+8i.
#


def _spaceship(*_args):
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    [re1, im1] = perllib.list_of_n(z1._cartesian() if perllib.ref(z1) else (z1, 0), 2)
    [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref(z2) else (z2, 0), 2)
    sgn = -1 if inverted else 1
    if perllib.num(re1) != perllib.num(re2):
        return sgn * (perllib.spaceship(perllib.num(re1), perllib.num(re2)))

    return sgn * (perllib.spaceship(perllib.num(im1), perllib.num(im2)))


Math.Complex._spaceship = _spaceship

#
# (_minus)
#
# Computes z1-z2.
#


def _minus(*_args):
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    [re1, im1] = perllib.list_of_n(z1._cartesian(), 2)
    if not perllib.ref(z2):
        z2 = cplx(z2)

    [re2, im2] = perllib.list_of_n(z2._cartesian(), 2)
    if inverted is None:
        z1._set_cartesian([re1 - re2, im1 - im2])
        return z1

    return (
        getattr(builtins, perllib.ref(z1)).make(
            perllib.num(re2) - perllib.num(re1), perllib.num(im2) - perllib.num(im1)
        )
        if inverted
        else getattr(builtins, perllib.ref(z1)).make(
            perllib.num(re1) - perllib.num(re2), perllib.num(im1) - perllib.num(im2)
        )
    )


Math.Complex._minus = _minus

#
# (_multiply)
#
# Computes z1*z2.
#

#
# (_plus)
#
# Computes z1+z2.
#


def _plus(*_args):
    [z1, z2, regular] = perllib.list_of_n(_args, 3)
    [re1, im1] = perllib.list_of_n(z1._cartesian(), 2)
    if not perllib.ref(z2):
        z2 = cplx(z2)

    [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref(z2) else (z2, 0), 2)
    if regular is None:
        z1._set_cartesian([re1 + re2, im1 + im2])
        return z1

    return getattr(builtins, perllib.ref(z1)).make(
        perllib.num(re1) + perllib.num(re2), perllib.num(im1) + perllib.num(im2)
    )


Math.Complex._plus = _plus

#
# (sqrt)
#
# Compute sqrt(z).
#
# It is quite tempting to use wantarray here so that in list context
# sqrt() would return the two solutions.  This, however, would
# break things like
#
# 	print "sqrt(z) = ", sqrt($z), "\n";
#
# The two values would be printed side by side without no intervening
# whitespace, quite confusing.
# Therefore if you want the two solutions use the root().
#


def sqrt(*_args):
    [z] = perllib.list_of_n(_args[0] if _args else _d, 1)
    [re_, im] = perllib.list_of_n(z._cartesian() if perllib.ref(z) else (z, 0), 2)
    if perllib.num(im) == 0:
        return (
            cplx(0, math.sqrt(-perllib.num(re_)))
            if perllib.num(re_) < 0
            else math.sqrt(perllib.num(re_))
        )

    [r, t] = perllib.list_of_n(z._polar(), 2)
    return getattr(builtins, perllib.ref(z)).emake(math.sqrt(perllib.num(r)), perllib.num(t) / 2)


Math.Complex.sqrt = sqrt

#
# cbrt
#
# Compute cbrt(z) (cubic root).
#
# Why are we not returning three values?  The same answer as for sqrt().
#


def _update_polar(*_args):
    _args = list(_args)
    self = _args.pop(0) if _args else None
    [x, y] = perllib.list_of_n(self.get("cartesian"), 2)
    self["p_dirty"] = 0
    if perllib.num(x) == 0 and perllib.num(y) == 0:
        return perllib.set_element(self, "polar", [0, 0])

    return perllib.set_element(
        self,
        "polar",
        [
            math.sqrt(perllib.num(x) * perllib.num(x) + perllib.num(y) * perllib.num(y)),
            math.atan2(perllib.num(y), perllib.num(x)),
        ],
    )


Math.Complex._update_polar = _update_polar


def _polar(*_args):
    return _args[0]._update_polar() if _args[0].get("p_dirty") else _args[0].get("polar")


Math.Complex._polar = _polar


def log(*_args):
    [z] = perllib.list_of_n(_args if _args else _d, 1)
    if not perllib.ref(z):
        if perllib.num(z) == 0:
            _logofzero("log")

        return (
            math.log(perllib.flt(z))
            if perllib.num(z) > 0
            else cplx(math.log(-perllib.flt(z)), pi())
        )

    [r, t] = perllib.list_of_n(z._polar(), 2)
    if perllib.num(r) == 0:
        _logofzero("log")

    if perllib.num(t) > pi():
        t = perllib.num(t) - pi2()
    elif perllib.num(t) <= -pi():
        t = perllib.num(t) + pi2()

    return getattr(builtins, perllib.ref(z)).make(math.log(perllib.flt(r)), t)


Math.Complex.log = log

#
# ln
#
# Alias for log().
#

#
# (atan2)
#
# Compute atan(z1/z2), minding the right quadrant.
#


def atan2(*_args):

    r = 0
    i_v = None
    s = 0
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    re1 = im1 = re2 = im2 = None
    if inverted:
        [re1, im1] = perllib.list_of_n(z2._cartesian() if perllib.ref(z2) else (z2, 0), 2)
        [re2, im2] = perllib.list_of_n(z1._cartesian() if perllib.ref(z1) else (z1, 0), 2)
    else:
        [re1, im1] = perllib.list_of_n(z1._cartesian() if perllib.ref(z1) else (z1, 0), 2)
        [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref(z2) else (z2, 0), 2)

    if im1 or im2:
        # In MATLAB the imaginary parts are ignored.
        # warn "atan2: Imaginary parts ignored";
        # http://documents.wolfram.com/mathematica/functions/ArcTan
        # NOTE: Mathematica ArcTan[x,y] while atan2(y,x)
        s = perllib.num(z1) * perllib.num(z1) + perllib.num(z2) * perllib.num(z2)
        if s == 0:
            _divbyzero("atan2")

        i_v = i(*_args)
        r = perllib.num(z2) + perllib.num(z1) * perllib.num(i_v)
        return -perllib.num(i_v) * perllib.num(log(r / perllib.num(sqrt(s))))

    return math.atan2(perllib.num(re1), perllib.num(re2))


Math.Complex.atan2 = atan2

#
# pi
#
# The number defined as pi = 180 degrees
#


def pi(*_args):
    return 4 * math.atan2(1, 1)


Math.Complex.pi = pi

#
# pi2
#
# The full circle
#

#
# pip4
#
# The eighth circle.
#


def pip4(*_args):
    return pi() / 4


Math.Complex.pip4 = pip4

#
# _uplog10
#
# Used in log10().
#

#
# pip2
#
# The quarter circle
#


def pip2(*_args):
    return pi() / 2


Math.Complex.pip2 = pip2

#
# i
#
# The number defined as i*i = -1;
#


def i(*_args):
    global i_v
    if i_v:
        return i_v

    i_v = perllib.bless(perllib.Hash(), "Math.Complex")
    i_v["cartesian"] = [0, 1]
    i_v["polar"] = [1, pip2()]
    i_v["c_dirty"] = 0
    i_v["p_dirty"] = 0
    return i_v


Math.Complex.i = types.MethodType(i, Math.Complex)

#
# _ip2
#
# Half of i.
#


def _ip2(*_args):
    return perllib.num(i()) / 2


Math.Complex._ip2 = _ip2

#
# pi4
#
# The full circle twice.
#


def pi4(*_args):
    return 4 * pi()


Math.Complex.pi4 = pi4


def pi2(*_args):
    return 2 * pi()


Math.Complex.pi2 = pi2


def _stringify_polar(*_args):
    _args = list(_args)

    b = None
    a = 0
    z = _args.pop(0) if _args else None
    [r, t] = perllib.list_of_n(z._polar(), 2)
    theta_v = None

    format_ = perllib.Hash(z.display_format(wantarray=True))
    format_v = format_.get("format")

    if (re.search(re.compile(r"^NaN[QS]?$", re.I), _str(t))) or (
        re.search(re.compile(rf"^-?Q{Math.Complex.Inf_v}E$", re.I), _str(t))
    ):
        theta_v = t
    elif perllib.num(t) == pi():
        theta_v = "pi"
    elif perllib.num(r) == 0 or perllib.num(t) == 0:
        theta_v = perllib.format_(_str(format_v), t) if format_v is not None else t

    if theta_v is not None:
        return f"[{r},{theta_v}]"

    #
    # Try to identify pi/n and friends.
    #

    t = perllib.num(t) - perllib.int_(abs(perllib.num(t)) / pi2()) * pi2()

    if format_.get("polar_pretty_print") and t:
        a = 0
        b = None
        for a in range(2, 9 + 1):
            b = perllib.num(t) * a / pi()
            b = re.sub(
                r"\.0$", r"", _str(b), count=1
            )  # SNOOPYJC: python formats floats always with '.0' at the end - strip it
            if re.search(r"^-?\d+$", _str(b)):
                if abs(perllib.num(b)) == 1:
                    b = "-" if perllib.num(b) < 0 else ""

                theta_v = f"{b}pi/{a}"
                break

    if format_v is not None:
        r = perllib.format_(_str(format_v), r)
        if theta_v is None:
            theta_v = perllib.format_(_str(format_v), t)
    else:
        if theta_v is None:
            theta_v = t

    return f"[{r},{theta_v}]"


Math.Complex._stringify_polar = _stringify_polar


def _stringify(*_args):
    _args = list(_args)
    global DISPLAY_FORMAT
    [z] = perllib.list_of_n((_args.pop(0) if _args else None), 1)

    style = z.display_format()

    if style is None:
        style = DISPLAY_FORMAT.get("style")

    if re.search(re.compile(r"^p", re.I), _str(style)):
        return z._stringify_polar()

    return z._stringify_cartesian()


Math.Complex._stringify = _stringify

# SNOOPYJC: We don't support function out parameters
# SNOOPYJC sub _theta {
# SNOOPYJC     my $theta = $_[0];
# SNOOPYJC
# SNOOPYJC     if    ($$theta >   pi()) { $$theta -= pi2 }
# SNOOPYJC     elsif ($$theta <= -pi()) { $$theta += pi2 }
# SNOOPYJC }


def _theta(*_args):
    theta_v = _args[0]

    if theta_v > pi():
        theta_v -= pi2()
    # SNOOPYJC
    elif theta_v <= -pi():
        theta_v += pi2()
    # SNOOPYJC

    return theta_v  # SNOOPYJC


Math.Complex._theta = _theta

#
# arg
#
# Compute or set complex's argument (theta).
#


def _emake(*_args):
    _args = list(_args)
    arg_v = _args.pop(0) if _args else None
    p = q = None

    if _m := re.search(rf"^\s*\[\s*{gre.pattern}\s*(?:,\s*{gre.pattern}\s*)?\]\s*$", _str(arg_v)):
        [p, q] = perllib.list_of_n((_m.group(1), _m.group(2) or 0), 2)
    elif _m := re.search(
        rf"^\s*\[\s*{gre.pattern}\s*(?:,\s*([-+]?\d*\s*)?pi(?:/\s*(\d+))?\s*)?\]\s*$", _str(arg_v)
    ):
        [p, q] = perllib.list_of_n(
            (
                _m.group(1),
                (-1 if _m.group(2) == "-" else (perllib.num(_m.group(2)) or 1))
                * pi()
                / (perllib.num(_m.group(3)) or 1),
            ),
            2,
        )
    elif _m := re.search(rf"^\s*\[\s*{gre.pattern}\s*\]\s*$", _str(arg_v)):
        [p, q] = perllib.list_of_n((_m.group(1), 0), 2)
    elif _m := re.search(rf"^\s*{gre.pattern}\s*$", _str(arg_v)):
        [p, q] = perllib.list_of_n((_m.group(1), 0), 2)

    if p is not None:
        p = re.sub(r"^\+", r"", _str(p), count=1)
        q = re.sub(r"^\+", r"", _str(q), count=1)
        if Math.Complex.has_inf_v:

            def _f240(_m):
                return f"{_m.group(1)}9**9**9"

            p = re.sub(re.compile(r"^(-?)inf$"), _f240, _str(p), count=1)

        if Math.Complex.has_inf_v:

            def _f241(_m):
                return f"{_m.group(1)}9**9**9"

            q = re.sub(re.compile(r"^(-?)inf$"), _f241, _str(q), count=1)

    return perllib.Array([p, q])


Math.Complex._emake = _emake

#
# acoth
#
# Computes the area/inverse hyperbolic cotangent acoth(z) = 1/2 log((1+z) / (z-1)).
#


def acoth(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero("acoth(0)")

    if not perllib.ref(z):
        if abs(perllib.num(z)) > 1:
            return math.log(perllib.flt((perllib.num(z) + 1) / (perllib.num(z) - 1))) / 2

        z = cplx(z, 0)

    if perllib.num(z) - 1 == 0:
        _divbyzero("acoth(1)", f"{z} - 1")

    if 1 + perllib.num(z) == 0:
        _logofzero("acoth(-1)", f"1 + {z}")

    return perllib.num(log((1 + perllib.num(z)) / (perllib.num(z) - 1))) / 2


Math.Complex.acoth = acoth

#
# acotanh
#
# Alias for acot().
#


def acotanh(*_args):
    return Math.Complex.acoth(*_args)


Math.Complex.acotanh = acotanh

#
# atanh
#
# Computes the area/inverse hyperbolic tangent atanh(z) = 1/2 log((1+z) / (1-z)).
#


def atanh(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if not perllib.ref(z):
        if abs(perllib.num(z)) < 1:
            return math.log(perllib.flt((1 + perllib.num(z)) / (1 - perllib.num(z)))) / 2

        z = cplx(z, 0)

    if 1 - perllib.num(z) == 0:
        _divbyzero("atanh(1)", f"1 - {z}")

    if 1 + perllib.num(z) == 0:
        _logofzero("atanh(-1)")

    return 0.5 * perllib.num(log((1 + perllib.num(z)) / (1 - perllib.num(z))))


Math.Complex.atanh = atanh

#
# asech
#
# Computes the area/inverse hyperbolic secant asech(z) = acosh(1 / z).
#

#
# asinh
#
# Computes the area/inverse hyperbolic sine asinh(z) = log(z + sqrt(z*z+1))
#


def asinh(*_args):

    t = 0
    [z] = perllib.list_of_n(_args, 1)
    if not perllib.ref(z):
        t = perllib.num(z) + math.sqrt(perllib.num(z) * perllib.num(z) + 1)
        if t:
            return math.log(perllib.flt(t))

    t = perllib.num(sqrt(perllib.num(z) * perllib.num(z) + 1)) + perllib.num(z)
    # Try Taylor if looking bad (this usually means that
    # $z was large negative, therefore the sqrt is really
    # close to abs(z), summing that with z...)
    if perllib.num(t) == 0:
        t = (
            1 / (2 * perllib.num(z))
            - 1 / (8 * perllib.num(z) ** 3)
            + 1 / (16 * perllib.num(z) ** 5)
            - 5 / (128 * perllib.num(z) ** 7)
        )

    return log(t)


Math.Complex.asinh = asinh

#
# acsch
#
# Computes the area/inverse hyperbolic cosecant acsch(z) = asinh(1 / z).
#


def acsch(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero("acsch(0)", z)

    return asinh(1 / perllib.num(z))


Math.Complex.acsch = acsch

#
# acosech
#
# Alias for acosh().
#


def acosech(*_args):
    return Math.Complex.acsch(*_args)


Math.Complex.acosech = acosech


def acosh(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if not perllib.ref(z):
        z = cplx(z, 0)

    [re_, im] = perllib.list_of_n(z._cartesian(), 2)
    if perllib.num(im) == 0:
        if perllib.num(re_) >= 1:
            return math.log(perllib.flt(re_) + math.sqrt(perllib.num(re_) * perllib.num(re_) - 1))

        if abs(perllib.num(re_)) < 1:
            return cplx(
                0,
                math.atan2(math.sqrt(1 - perllib.num(re_) * perllib.num(re_)), perllib.num(re_)),
            )

    t = perllib.num(sqrt(perllib.num(z) * perllib.num(z) - 1)) + perllib.num(z)
    # Try Taylor if looking bad (this usually means that
    # $z was large negative, therefore the sqrt is really
    # close to abs(z), summing that with z...)
    if perllib.num(t) == 0:
        t = (
            1 / (2 * perllib.num(z))
            - 1 / (8 * perllib.num(z) ** 3)
            + 1 / (16 * perllib.num(z) ** 5)
            - 5 / (128 * perllib.num(z) ** 7)
        )

    u = log(t)
    if perllib.num(re_) < 0 and perllib.num(im) == 0:
        u.Im(-u.Im())

    return -perllib.num(u) if perllib.num(re_) < 0 else u


Math.Complex.acosh = acosh


def asech(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero("asech(0)", f"{z}")

    return acosh(1 / perllib.num(z))


Math.Complex.asech = asech

#
# atan
#
# Computes the arc tangent atan(z) = i/2 log((i+z) / (i-z)).
#


def atan(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if not perllib.ref(z):
        return math.atan2(perllib.num(z), 1)

    [x, y] = perllib.list_of_n(z._cartesian() if perllib.ref(z) else (z, 0), 2)
    if perllib.num(x) == 0 and perllib.num(y) == 0:
        return 0

    if perllib.num(z) == perllib.num(i()):
        _divbyzero("atan(i)")

    if -perllib.num(z) == perllib.num(i()):  # -i is a bad file test...
        _logofzero("atan(-i)")

    log_v = log((perllib.num(i()) + perllib.num(z)) / (perllib.num(i()) - perllib.num(z)))
    return perllib.num(_ip2()) * perllib.num(log_v)


Math.Complex.atan = atan

#
# asec
#
# Computes the arc secant asec(z) = acos(1 / z).
#

#
# acot
#
# Computes the arc cotangent acot(z) = atan(1 / z)
#


def acot(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero("acot(0)")

    if not perllib.ref(z):
        return (
            math.atan2(1, perllib.num(z))
            if (perllib.num(z) >= 0)
            else math.atan2(-1, -perllib.num(z))
        )

    if perllib.num(z) - perllib.num(i()) == 0:
        _divbyzero("acot(i)")

    if perllib.num(z) + perllib.num(i()) == 0:
        _logofzero("acot(-i)")

    return atan(1 / perllib.num(z))


Math.Complex.acot = acot

#
# acotan
#
# Alias for acot().
#


def acotan(*_args):
    return Math.Complex.acot(*_args)


Math.Complex.acotan = acotan

#
# asin
#
# Computes the arc sine asin(z) = -i log(iz + sqrt(1-z*z)).
#


def asin(*_args):
    z = _args[0]
    if (not perllib.ref(z)) and abs(perllib.num(z)) <= 1:
        return math.atan2(perllib.num(z), math.sqrt(1 - perllib.num(z) * perllib.num(z)))

    if not perllib.ref(z):
        z = cplx(z, 0)

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    if perllib.num(x) == 0 and perllib.num(y) == 0:
        return 0

    t1 = math.sqrt((perllib.num(x) + 1) * (perllib.num(x) + 1) + perllib.num(y) * perllib.num(y))
    t2 = math.sqrt((perllib.num(x) - 1) * (perllib.num(x) - 1) + perllib.num(y) * perllib.num(y))
    alpha = (t1 + t2) / 2
    beta = (t1 - t2) / 2
    if alpha < 1:
        alpha = 1

    if beta > 1:
        beta = 1
    elif beta < -1:
        beta = -1

    u = math.atan2(beta, math.sqrt(1 - beta * beta))
    v = -math.log(alpha + math.sqrt(alpha * alpha - 1))
    if perllib.num(y) > 0 or (perllib.num(y) == 0 and perllib.num(x) < -1):
        v = -v

    return getattr(builtins, perllib.ref(z)).make(u, v)


Math.Complex.asin = asin

#
# acsc
#
# Computes the arc cosecant acsc(z) = asin(1 / z).
#


def acsc(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero(f"acsc({z})", z)

    return asin(1 / perllib.num(z))


Math.Complex.acsc = acsc

#
# acosec
#
# Alias for acsc().
#


def acosec(*_args):
    return Math.Complex.acsc(*_args)


Math.Complex.acosec = acosec


def acos(*_args):
    z = _args[0]
    if (not perllib.ref(z)) and abs(perllib.num(z)) <= 1:
        return math.atan2(math.sqrt(1 - perllib.num(z) * perllib.num(z)), perllib.num(z))

    if not perllib.ref(z):
        z = cplx(z, 0)

    [x, y] = perllib.list_of_n(z._cartesian(), 2)
    if perllib.num(x) == 1 and perllib.num(y) == 0:
        return 0

    t1 = math.sqrt((perllib.num(x) + 1) * (perllib.num(x) + 1) + perllib.num(y) * perllib.num(y))
    t2 = math.sqrt((perllib.num(x) - 1) * (perllib.num(x) - 1) + perllib.num(y) * perllib.num(y))
    alpha = (t1 + t2) / 2
    beta = (t1 - t2) / 2
    if alpha < 1:
        alpha = 1

    if beta > 1:
        beta = 1
    elif beta < -1:
        beta = -1

    u = math.atan2(math.sqrt(1 - beta * beta), beta)
    v = math.log(alpha + math.sqrt(alpha * alpha - 1))
    if perllib.num(y) > 0 or (perllib.num(y) == 0 and perllib.num(x) < -1):
        v = -v

    return getattr(builtins, perllib.ref(z)).make(u, v)


Math.Complex.acos = acos


def asec(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if perllib.num(z) == 0:
        _divbyzero(f"asec({z})", z)

    return acos(1 / perllib.num(z))


Math.Complex.asec = asec

#
# logn
#
# Compute logn(z,n) = log(z) / log(n)
#


def logn(*_args):
    [z, n] = perllib.list_of_n(_args, 2)
    if not perllib.ref(z):
        z = cplx(z, 0)

    logn_v = LOGN.get(_str(n))
    if logn_v is None:  # Cache log(n)
        LOGN[_str(n)] = math.log(perllib.flt(n))
        logn_v = LOGN.get(_str(n))

    return perllib.num(log(z)) / perllib.num(logn_v)


Math.Complex.logn = logn


def ln(*_args):
    return Math.Complex.log(*_args)


Math.Complex.ln = ln

#
# log10
#
# Compute log10(z).
#

#
# (_power)
#
# Computes z1**z2 = exp(z2 * log z1)).
#


def _power(*_args):
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    if inverted:
        if perllib.num(z1) == 0 or perllib.num(z2) == 1:
            return 1

        if perllib.num(z2) == 0 and perllib.num(Re(z1)) > 0:
            return 0
    else:
        if perllib.num(z2) == 0 or perllib.num(z1) == 1:
            return 1

        if perllib.num(z1) == 0 and perllib.num(Re(z2)) > 0:
            return 0

    w = (
        exp(perllib.num(z1) * perllib.num(log(z2)))
        if inverted
        else exp(perllib.num(z2) * perllib.num(log(z1)))
    )
    # If both arguments cartesian, return cartesian, else polar.
    return (
        cplx(*w._cartesian())
        if perllib.num(z1.get("c_dirty")) == 0
        and (not perllib.ref(z2) or perllib.num(z2.get("c_dirty")) == 0)
        else w
    )


Math.Complex._power = _power


def _uplog10(*_args):
    return 1 / math.log(perllib.flt(10))


Math.Complex._uplog10 = _uplog10


def log10(*_args):
    return perllib.num(Math.Complex.log(_args[0])) * _uplog10()


Math.Complex.log10 = log10


def cbrt(*_args):
    [z] = perllib.list_of_n(_args, 1)
    if not perllib.ref(z):
        return (
            -math.exp(math.log(-perllib.flt(z)) / perllib.flt(3))
            if perllib.num(z) < 0
            else (
                math.exp(math.log(perllib.flt(z)) / perllib.flt(3)) if perllib.num(z) > 0 else 0
            )
        )

    [r, t] = perllib.list_of_n(z._polar(), 2)
    if perllib.num(r) == 0:
        return 0

    return getattr(builtins, perllib.ref(z)).emake(
        math.exp(math.log(perllib.flt(r)) / perllib.flt(3)), perllib.num(t) / 3
    )


Math.Complex.cbrt = cbrt


def arg(*_args):
    [z, theta_v] = perllib.list_of_n(_args, 2)
    if not perllib.ref(z):
        return z

    if theta_v is not None:
        # SNOOPYJC	    _theta(\$theta);
        theta_v = _theta(theta_v)  # SNOOPYJC
        z["polar"] = [(z._polar())[0], theta_v]
        z["p_dirty"] = 0
        z["c_dirty"] = 1
    else:
        theta_v = (z._polar())[1]
        # SNOOPYJC	    _theta(\$theta);
        theta_v = _theta(theta_v)  # SNOOPYJC

    return theta_v


Math.Complex.arg = arg

#
# theta
#
# Return or set theta(w).
#


def theta(*_args):
    return Math.Complex.arg(*_args)


Math.Complex.theta = theta

#
# (abs)
#
# Compute or set complex's norm (rho).
#


def abs_(*_args):
    _args = list(_args)
    [z, rho_v] = perllib.list_of_n(_args if _args else _d, 2)
    if not perllib.ref(z):
        if len(_args) == 2:
            _args[0] = _args[1]
        else:
            return abs(perllib.num(z))

    if rho_v is not None:
        z["polar"] = [rho_v, (z._polar())[1]]
        z["p_dirty"] = 0
        z["c_dirty"] = 1
        return rho_v
    else:
        return (z._polar())[0]


Math.Complex.abs_ = abs_


def rho(*_args):
    return Math.Complex.abs_(*_args)


Math.Complex.rho = rho

#
# (_conjugate)
#
# Compute complex's _conjugate.
#


def _conjugate(*_args):

    t = None
    r = None
    [z] = perllib.list_of_n(_args, 1)
    if z.get("c_dirty"):
        [r, t] = perllib.list_of_n(z._polar(), 2)
        return getattr(builtins, perllib.ref(z)).emake(r, -perllib.num(t))

    [re_, im] = perllib.list_of_n(z._cartesian(), 2)
    return getattr(builtins, perllib.ref(z)).make(re_, -perllib.num(im))


Math.Complex._conjugate = _conjugate


def _negate(*_args):

    r = None
    t = None
    [z] = perllib.list_of_n(_args, 1)
    if z.get("c_dirty"):
        [r, t] = perllib.list_of_n(z._polar(), 2)
        t = perllib.num(t) + pi() if (perllib.num(t) <= 0) else perllib.num(t) - pi()
        return getattr(builtins, perllib.ref(z)).emake(r, t)

    [re_, im] = perllib.list_of_n(z._cartesian(), 2)
    return getattr(builtins, perllib.ref(z)).make(-perllib.num(re_), -perllib.num(im))


Math.Complex._negate = _negate


def _divide(*_args):

    t = 0.0
    x2 = None
    x1 = None
    v = 0
    d = 0
    r1 = None
    r2 = None
    y1 = None
    u = 0
    y2 = None
    t2 = None
    t1 = None
    [z1, z2, inverted] = perllib.list_of_n(_args, 3)
    if (
        perllib.num(z1.get("p_dirty")) == 0
        and perllib.ref(z2)
        and perllib.num(z2.get("p_dirty")) == 0
    ):
        # if both polar better use polar to avoid rounding errors
        [r1, t1] = perllib.list_of_n(z1._polar(), 2)
        [r2, t2] = perllib.list_of_n(z2._polar(), 2)
        t = 0.0
        if inverted:
            if perllib.num(r1) == 0:
                _divbyzero(f"{z2}/0")

            t = perllib.num(t2) - perllib.num(t1)
            if t > pi():
                t -= pi2()
            elif t <= -pi():
                t += pi2()

            return getattr(builtins, perllib.ref(z1)).emake(perllib.num(r2) / perllib.num(r1), t)
        else:
            if perllib.num(r2) == 0:
                _divbyzero(f"{z1}/0")

            t = perllib.num(t1) - perllib.num(t2)
            if t > pi():
                t -= pi2()
            elif t <= -pi():
                t += pi2()

            return getattr(builtins, perllib.ref(z1)).emake(perllib.num(r1) / perllib.num(r2), t)
    else:
        d = 0
        x2 = None
        y2 = None
        if inverted:
            [x2, y2] = perllib.list_of_n(z1._cartesian(), 2)
            d = perllib.num(x2) * perllib.num(x2) + perllib.num(y2) * perllib.num(y2)
            if d == 0:
                _divbyzero(f"{z2}/0")

            return getattr(builtins, perllib.ref(z1)).make(
                (perllib.num(x2) * perllib.num(z2)) / d, -(perllib.num(y2) * perllib.num(z2)) / d
            )
        else:
            [x1, y1] = perllib.list_of_n(z1._cartesian(), 2)
            if perllib.ref(z2):
                [x2, y2] = perllib.list_of_n(z2._cartesian(), 2)
                d = perllib.num(x2) * perllib.num(x2) + perllib.num(y2) * perllib.num(y2)
                if d == 0:
                    _divbyzero(f"{z1}/0")

                u = (perllib.num(x1) * perllib.num(x2) + perllib.num(y1) * perllib.num(y2)) / d
                v = (perllib.num(y1) * perllib.num(x2) - perllib.num(x1) * perllib.num(y2)) / d
                return getattr(builtins, perllib.ref(z1)).make(u, v)
            else:
                if perllib.num(z2) == 0:
                    _divbyzero(f"{z1}/0")

                return getattr(builtins, perllib.ref(z1)).make(
                    perllib.num(x1) / perllib.num(z2), perllib.num(y1) / perllib.num(z2)
                )


Math.Complex._divide = _divide


def _multiply(*_args):

    y2 = None
    y1 = None
    t2 = None
    t1 = None
    x1 = None
    x2 = None
    r2 = None
    r1 = None
    t = 0.0
    [z1, z2, regular] = perllib.list_of_n(_args, 3)
    if (
        perllib.num(z1.get("p_dirty")) == 0
        and perllib.ref(z2)
        and perllib.num(z2.get("p_dirty")) == 0
    ):
        # if both polar better use polar to avoid rounding errors
        [r1, t1] = perllib.list_of_n(z1._polar(), 2)
        [r2, t2] = perllib.list_of_n(z2._polar(), 2)
        t = perllib.num(t1) + perllib.num(t2)
        if t > pi():
            t -= pi2()
        elif t <= -pi():
            t += pi2()

        if regular is None:
            z1._set_polar([r1 * r2, t])
            return z1

        return getattr(builtins, perllib.ref(z1)).emake(perllib.num(r1) * perllib.num(r2), t)
    else:
        [x1, y1] = perllib.list_of_n(z1._cartesian(), 2)
        if perllib.ref(z2):
            [x2, y2] = perllib.list_of_n(z2._cartesian(), 2)
            return getattr(builtins, perllib.ref(z1)).make(
                perllib.num(x1) * perllib.num(x2) - perllib.num(y1) * perllib.num(y2),
                perllib.num(x1) * perllib.num(y2) + perllib.num(y1) * perllib.num(x2),
            )
        else:
            return getattr(builtins, perllib.ref(z1)).make(
                perllib.num(x1) * perllib.num(z2), perllib.num(y1) * perllib.num(z2)
            )


Math.Complex._multiply = _multiply


def cplxe(*_args):
    return getattr(builtins, "Math.Complex").emake(*_args)


Math.Complex.cplxe = cplxe


def root(*_args):

    root_a = perllib.Array()
    theta_v = 0
    w = ""
    i_v = 0
    [z, n, k] = perllib.list_of_n(_args, 3)
    if perllib.num(n) < 1 or perllib.int_(n) != perllib.num(n):
        _rootbad(n)

    [r, t] = perllib.list_of_n(
        z._polar()
        if perllib.ref(z)
        else (abs(perllib.num(z)), 0 if perllib.num(z) >= 0 else pi()),
        2,
    )
    theta_inc = pi2() / perllib.num(n)
    rho_v = perllib.num(r) ** (1 / perllib.num(n))
    cartesian = perllib.ref(z) and perllib.num(z.get("c_dirty")) == 0
    if len(_args) == 2:
        root_a = perllib.Array()
        (i_v := 0, theta_v := perllib.num(t) / perllib.num(n))
        while i_v < perllib.num(n):
            w = cplxe(rho_v, theta_v)
            # Yes, $cartesian is loop invariant.
            root_a.extend(perllib.make_list(cplx(*w._cartesian()) if cartesian else w))
            (((i_v := i_v + 1) - 1), (theta_v := theta_v + theta_inc))

        return root_a
    elif len(_args) == 3:
        w = cplxe(rho_v, perllib.num(t) / perllib.num(n) + perllib.num(k) * theta_inc)
        return cplx(*w._cartesian()) if cartesian else w


Math.Complex.root = root


def __gt__(self, other):  # extra overload '>'
    return _spaceship(self, other, False) > 0


Math.Complex.__gt__ = __gt__

#
# Package "privates"
#


def __ge__(self, other):  # extra overload '>='
    return _spaceship(self, other, False) >= 0


Math.Complex.__ge__ = __ge__


def __ne__(self, other):  # extra overload '!='
    return _spaceship(self, other, False) != 0


Math.Complex.__ne__ = __ne__


def __le__(self, other):  # extra overload '<='
    return _spaceship(self, other, False) <= 0


Math.Complex.__le__ = __le__


def __lt__(self, other):  # extra overload '<'
    return _spaceship(self, other, False) < 0


Math.Complex.__lt__ = __lt__


def __str__(self):  # use overload '""'
    return _str(_stringify(self, None, False))


Math.Complex.__str__ = __str__


def __ratan2__(self, other):  # reversed overload 'atan2'
    return atan2(self, other, True)


Math.Complex.__ratan2__ = __ratan2__


def __atan2__(self, other):  # use overload 'atan2'
    return atan2(self, other, False)


Math.Complex.__atan2__ = __atan2__


def __abs__(self):  # use overload 'abs'
    return abs_(self, None, False)


Math.Complex.__abs__ = __abs__


def __invert__(self):  # use overload '~'
    return _conjugate(self, None, False)


Math.Complex.__invert__ = __invert__


def __neg__(self):  # use overload 'neg'
    return _negate(self, None, False)


Math.Complex.__neg__ = __neg__


def __rspaceship__(self, other):  # reversed overload '<=>'
    return _spaceship(self, other, True)


Math.Complex.__rspaceship__ = __rspaceship__


def __spaceship__(self, other):  # use overload '<=>'
    return _spaceship(self, other, False)


Math.Complex.__spaceship__ = __spaceship__


def __eq__(self, other):  # use overload '=='
    return _numeq(self, other, False)


Math.Complex.__eq__ = __eq__


def __rpow__(self, other, modulo=None):  # reversed overload '**'
    return _power(self, other, True)


Math.Complex.__rpow__ = __rpow__


def __pow__(self, other, modulo=None):  # use overload '**'
    return _power(self, other, False)


Math.Complex.__pow__ = __pow__


def __ipow__(self, other, modulo=None):  # use overload '**='
    return _power(self, other, None)


Math.Complex.__ipow__ = __ipow__


def __rtruediv__(self, other):  # reversed overload '/'
    return _divide(self, other, True)


Math.Complex.__rtruediv__ = __rtruediv__


def __truediv__(self, other):  # use overload '/'
    return _divide(self, other, False)


Math.Complex.__truediv__ = __truediv__


def __itruediv__(self, other):  # use overload '/='
    return _divide(self, other, None)


Math.Complex.__itruediv__ = __itruediv__


def __rmul__(self, other):  # reversed overload '*'
    return _multiply(self, other, True)


Math.Complex.__rmul__ = __rmul__


def __mul__(self, other):  # use overload '*'
    return _multiply(self, other, False)


Math.Complex.__mul__ = __mul__


def __imul__(self, other):  # use overload '*='
    return _multiply(self, other, None)


Math.Complex.__imul__ = __imul__


def __rsub__(self, other):  # reversed overload '-'
    return _minus(self, other, True)


Math.Complex.__rsub__ = __rsub__


def __sub__(self, other):  # use overload '-'
    return _minus(self, other, False)


Math.Complex.__sub__ = __sub__


def __isub__(self, other):  # use overload '-='
    return _minus(self, other, None)


Math.Complex.__isub__ = __isub__


def __radd__(self, other):  # reversed overload '+'
    return _plus(self, other, True)


Math.Complex.__radd__ = __radd__


def __add__(self, other):  # use overload '+'
    return _plus(self, other, False)


Math.Complex.__add__ = __add__


def __iadd__(self, other):  # use overload '+='
    return _plus(self, other, None)


Math.Complex.__iadd__ = __iadd__


def __copy__(self):  # use overload '='
    return _copy(self, None, False)


Math.Complex.__copy__ = __copy__

Math.Complex.has_inf_v = perllib.init_global("Math.Complex", "has_inf_v", None)
Math.Complex.ExpInf_v = perllib.init_global("Math.Complex", "ExpInf_v", None)
Math.Complex.Inf_v = perllib.init_global("Math.Complex", "Inf_v", None)
Math.Complex.vax_float_v = perllib.init_global("Math.Complex", "vax_float_v", "")
DISPLAY_FORMAT = perllib.Hash()
Math.Complex.bpi_v = perllib.init_global("Math.Complex", "bpi_v", "")
i_v = None
Math.Complex.has_nan_v = perllib.init_global("Math.Complex", "has_nan_v", "")

builtins.__PACKAGE__ = "Math.Complex"

for _ in range(1):
    pass  # SKIPPED:  use 5.006; }
# SKIPPED: use strict;


Math.Complex.VERSION_v = 1.59_02

import Config as _Config

for _ in range(1):  # BEGIN:
    Math.Complex.vax_float_v = re.search(r"^[\x80\x10]\x40", perllib.pack("d", 1))
    Math.Complex.has_inf_v = not Math.Complex.vax_float_v
    Math.Complex.has_nan_v = not Math.Complex.vax_float_v

    if not Math.Complex.has_inf_v:
        # For example in vax, there is no Inf,
        # and just mentioning the DBL_MAX (1.70141183460469229e+38)
        # causes SIGFPE.

        # These are pretty useless without a real infinity,
        # but setting them makes for less warnings about their
        # undefined values.
        Math.Complex.Inf_v = "Inf"
        Math.Complex.ExpInf_v = "Inf"
        break

    # AFAICT the 10, 12, and 16-byte long doubles
    # all have the same maximum.

    DBL_MAX = perllib.Hash(
        {
            "4": "1.70141183460469229e+38",
            "8": "1.7976931348623157e+308",
            "10": "1.1897314953572317650857593266280070162E+4932",
            "12": "1.1897314953572317650857593266280070162E+4932",
            "16": "1.1897314953572317650857593266280070162E+4932",
        }
    )
    # These are IEEE 754 maxima.

    nvsize = (
        Config.Config_h.get("nvsize")
        or (Config.Config_h.get("uselongdouble") and Config.Config_h.get("longdblsize"))
        or Config.Config_h.get("doublesize")
    )
    if nvsize is None:
        raise Die("Math::Complex: Could not figure out nvsize\n")

    if not (DBL_MAX.get(_str(nvsize)) is not None):
        raise Die(f"Math::Complex: Cannot not figure out max nv (nvsize = {nvsize})\n")

    _eval_result57 = None
    try:
        _eval_result57 = DBL_MAX.get(_str(nvsize))
    except Exception:
        pass

    DBL_MAX_v = _eval_result57
    if DBL_MAX_v is None:
        raise Die(f"Math::Complex: Could not figure out max nv (nvsize = {nvsize})\n")

    BIGGER_THAN_THIS = 1e30  # Must find something bigger than this.
    if perllib.os_name() == "unicosmk":
        Math.Complex.Inf_v = DBL_MAX_v
    else:
        try:
            _locals_stack.append(signal.getsignal(signal.SIGFPE))
            _locals_stack.append(perllib.OS_ERROR)

            def _f64(*_args):
                pass

            signal.signal(signal.SIGFPE, _f64)
            perllib.OS_ERROR = ""
            # We do want an arithmetic overflow, Inf INF inf Infinity.
            for t in (
                "exp(99999)",
                "inf",
                "Inf",
                "INF",
                "infinity",
                "Infinity",
                "INFINITY",
                "1e99999",
            ):
                # Enough even with 128-bit long doubles.
                try:
                    _locals_stack.append(perllib.WARNING)
                    perllib.WARNING = 0
                    _eval_result78 = None
                    try:
                        _eval_result78 = perllib.num(t) + 1.0  # FAILTRAN
                    except Exception:
                        pass

                    i_v = _eval_result78
                    if i_v is not None and perllib.num(i_v) > BIGGER_THAN_THIS:
                        Math.Complex.Inf_v = i_v
                        break

                finally:
                    perllib.WARNING = _locals_stack.pop()

            if Math.Complex.Inf_v is None:  # Oh well, close enough.
                Math.Complex.Inf_v = DBL_MAX_v

            if not (perllib.num(Math.Complex.Inf_v) > BIGGER_THAN_THIS):
                raise Die("Math::Complex: Could not get Infinity")

            _eval_result87 = None
            try:
                _eval_result87 = math.exp(perllib.flt(99999))
            except Exception:
                pass

            Math.Complex.ExpInf_v = _eval_result87

        finally:
            perllib.OS_ERROR = _locals_stack.pop()
            signal.signal(signal.SIGFPE, _locals_stack.pop())
    # print "# On this machine, Inf = '$Inf'\n";

# SNOOPYJC: set_prototype not implemented and not needed
# SNOOPYJC use Scalar::Util qw(set_prototype);


perllib.WARNING = 1
perllib.WARNING = 0  # To avoid the (_) warnings.

# SNOOPYJC BEGIN {
# SNOOPYJC     # For certain functions that we override, in 5.10 or better
# SNOOPYJC     # we can set a smarter prototype that will handle the lexical $_
# SNOOPYJC     # (also a 5.10+ feature).
# SNOOPYJC     if ($] >= 5.010000) {
# SNOOPYJC         set_prototype \&abs, '_';
# SNOOPYJC         set_prototype \&cos, '_';
# SNOOPYJC         set_prototype \&exp, '_';
# SNOOPYJC         set_prototype \&log, '_';
# SNOOPYJC         set_prototype \&sin, '_';
# SNOOPYJC         set_prototype \&sqrt, '_';
# SNOOPYJC     }
# SNOOPYJC }

i_v = None
LOGN = perllib.Hash()

# Regular expression for floating point numbers.
# These days we could use Scalar::Util::lln(), I guess.
gre = re.compile(
    r"(?i:\s*([\+\-]?(?:(?:(?:\d+(?:_\d+)*(?:\.\d*(?:_\d+)*)?|\.\d+(?:_\d+)*)(?:[eE][\+\-]?\d+(?:_\d+)*)?))|inf))"
)

# SKIPPED: require Exporter;

Math.Complex.ISA_a = "Exporter".split()

trig = "pi tan csc cosec sec cot cotan asin acos atan acsc acosec asec acot acotan sinh cosh tanh csch cosech sech coth cotanh asinh acosh atanh acsch acosech asech acoth acotanh".split()

Math.Complex.EXPORT_a = perllib.Array(
    perllib.flatten(
        ["i Re Im rho theta arg sqrt log ln log10 logn cbrt root cplx cplxe atan2".split(), trig]
    )
)

pi_a = "pi pi2 pi4 pip2 pip4 Inf".split()

Math.Complex.EXPORT_OK_a = pi_a.copy()

Math.Complex.EXPORT_TAGS_h = perllib.Hash(
    {
        "trig": [trig],
        "pi": [pi_a],
    }
)
DISPLAY_FORMAT = perllib.Hash({"style": "cartesian", "polar_pretty_print": 1})
eps = 1e-14  # Epsilon
