Source code for Math.Complex

#!/usr/bin/env python3
# Generated by "pythonizer -aM ../Math/Complex.pm" v1.026 run by SNOOPYJC on Sat Feb 11 16:48:18 2023
__author__ = """Joe Cool"""
__email__ = "snoopyjc@gmail.com"
__version__ = "1.026"
#
# 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 builtins, math, perllib, re, signal, types

_bn = lambda s: "" if s is None else s
_pb = lambda b: 1 if b else ""
_str = lambda s: "" if s is None else str(s)
_locals_stack = []
from perllib import Die
import Config as _Config

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


[docs]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. #
[docs]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_scalar(self): # Called as an object method 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_scalar(self): # Called as an object method 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"{_bn(_args[0])}: Logarithm of zero.\n" if 1 < len(_args) and _args[1] is not None: mess += f"(Because in the definition of {_bn(_args[0])}, the argument " if not (_str(_args[1]) == "0"): mess += f"{_bn(_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 '{_bn(_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"{_bn(_args[0])}: Division by zero.\n" if 1 < len(_args) and _args[1] is not None: mess += f"(Because in the definition of {_bn(_args[0])}, the divisor " if not (f"{_bn(_args[1])}" == "0"): mess += f"{_bn(_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 # # 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 _m := re.search( re.compile(rf"^-?{perllib.quotemeta(_bn(Math.Complex.Inf_v))}$", 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 _m := re.search( re.compile(rf"^-?{perllib.quotemeta(_bn(Math.Complex.Inf_v))}$", 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]'. # # # (exp) # # Computes exp(z). #
[docs]def exp(*_args): global _d [z] = perllib.list_of_n(_args if _args else _d, 1) if not perllib.ref_scalar(z): return math.exp(perllib.flt(z)) [x, y] = perllib.list_of_n(z._cartesian(), 2) return perllib.method_call((perllib.ref_scalar(z)), "emake", math.exp(perllib.flt(x)), y)
Math.Complex.exp = exp # # (cos) # # Compute cos(z) = (exp(iz) + exp(-iz))/2. #
[docs]def cos(*_args): global _d [z] = perllib.list_of_n(_args if _args else _d, 1) if not perllib.ref_scalar(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 perllib.num(Inf()) return perllib.method_call( (perllib.ref_scalar(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). #
[docs]def sec(*_args): [z] = perllib.list_of_n(_args, 1) cz = cos(z) if perllib.num(cz) == 0: _divbyzero(f"sec({_bn(z)})", f"cos({_bn(z)})") return 1 / perllib.num(cz)
Math.Complex.sec = sec # # csc # # Computes the cosecant csc(z) = 1 / sin(z). #
[docs]def sin(*_args): global _d [z] = perllib.list_of_n(_args if _args else _d, 1) if not perllib.ref_scalar(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 perllib.num(Inf()) return perllib.method_call( (perllib.ref_scalar(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. #
[docs]def sinh(*_args): [z] = perllib.list_of_n(_args, 1) ex = 0.0 if not perllib.ref_scalar(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 perllib.num(Inf()) return perllib.method_call( (perllib.ref_scalar(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). #
[docs]def csch(*_args): [z] = perllib.list_of_n(_args, 1) sz = sinh(z) if perllib.num(sz) == 0: _divbyzero(f"csch({_bn(z)})", f"sinh({_bn(z)})") return 1 / perllib.num(sz)
Math.Complex.csch = csch # # cosech # # Alias for csch(). #
[docs]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. #
[docs]def cosh(*_args): [z] = perllib.list_of_n(_args, 1) ex = 0.0 if not perllib.ref_scalar(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 perllib.num(Inf()) return perllib.method_call( (perllib.ref_scalar(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
[docs]def coth(*_args): [z] = perllib.list_of_n(_args, 1) sz = sinh(z) if perllib.num(sz) == 0: _divbyzero(f"coth({_bn(z)})", f"sinh({_bn(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(). #
[docs]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). #
[docs]def sech(*_args): [z] = perllib.list_of_n(_args, 1) cz = cosh(z) if perllib.num(cz) == 0: _divbyzero(f"sech({_bn(z)})", f"cosh({_bn(z)})") return 1 / perllib.num(cz)
Math.Complex.sech = sech
[docs]def tanh(*_args): [z] = perllib.list_of_n(_args, 1) cz = cosh(z) if perllib.num(cz) == 0: _divbyzero(f"tanh({_bn(z)})", f"cosh({_bn(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). #
[docs]def cot(*_args): [z] = perllib.list_of_n(_args, 1) sz = sin(z) if perllib.num(sz) == 0: _divbyzero(f"cot({_bn(z)})", f"sin({_bn(z)})") return perllib.num(cos(z)) / perllib.num(sz)
Math.Complex.cot = cot # # cotan # # Alias for cot(). #
[docs]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)). #
[docs]def csc(*_args): [z] = perllib.list_of_n(_args, 1) sz = sin(z) if perllib.num(sz) == 0: _divbyzero(f"csc({_bn(z)})", f"sin({_bn(z)})") return 1 / perllib.num(sz)
Math.Complex.csc = csc # # cosec # # Alias for csc(). #
[docs]def cosec(*_args): return Math.Complex.csc(*_args)
Math.Complex.cosec = cosec
[docs]def tan(*_args): [z] = perllib.list_of_n(_args, 1) cz = cos(z) if perllib.num(cz) == 0: _divbyzero(f"tan({_bn(z)})", f"cos({_bn(z)})") return perllib.num(sin(z)) / perllib.num(cz)
Math.Complex.tan = tan 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. # # # Im # # Return or set Im(z). #
[docs]def Im(*_args): [z, Im_v] = perllib.list_of_n(_args, 2) if not perllib.ref_scalar(z): return 0 if Im_v is not None: z["cartesian"] = perllib.Array([(z._cartesian())[0], Im_v]) z["c_dirty"] = 0 z["p_dirty"] = 1 return z.get("p_dirty") else: return (z._cartesian())[1]
Math.Complex.Im = Im # # rho # # Return or set rho(w). # # # Re # # Return or set Re(z). #
[docs]def Re(*_args): [z, Re_v] = perllib.list_of_n(_args, 2) if not perllib.ref_scalar(z): return z if Re_v is not None: z["cartesian"] = perllib.Array([Re_v, (z._cartesian())[1]]) z["c_dirty"] = 0 z["p_dirty"] = 1 return z.get("p_dirty") 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_scalar(z1) else (z1, 0), 2) [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref_scalar(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_scalar(z1) else (z1, 0), 2) [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref_scalar(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 # # cplxe # # Creates a complex number from a (rho, theta) tuple. # This avoids the burden of writing Math::Complex->emake(rho, theta). #
[docs]def cplxe(*_args): return perllib.method_call("Math.Complex", "emake", *_args)
Math.Complex.cplxe = cplxe # # pi # # The number defined as pi = 180 degrees # # 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). #
[docs]def cplx(*_args): return perllib.method_call("Math.Complex", "make", *_args)
Math.Complex.cplx = cplx # # (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(). #
[docs]def sqrt(*_args): global _d [z] = perllib.list_of_n(_args[0] if _args else _d, 1) [re_, im] = perllib.list_of_n(z._cartesian() if perllib.ref_scalar(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 perllib.method_call( (perllib.ref_scalar(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 # # (_plus) # # Computes z1+z2. # def _polar(*_args): return (_args[0]._update_polar()) if _args[0].get("p_dirty") else _args[0].get("polar") Math.Complex._polar = _polar
[docs]def log(*_args): global _d [z] = perllib.list_of_n(_args if _args else _d, 1) if not perllib.ref_scalar(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 perllib.method_call((perllib.ref_scalar(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. #
[docs]def atan2(*_args): i_v = None r = 0 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_scalar(z2) else (z2, 0), 2) [re2, im2] = perllib.list_of_n(z1._cartesian() if perllib.ref_scalar(z1) else (z1, 0), 2) else: [re1, im1] = perllib.list_of_n(z1._cartesian() if perllib.ref_scalar(z1) else (z1, 0), 2) [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref_scalar(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
[docs]def pi(*_args): return 4 * math.atan2(1, 1)
Math.Complex.pi = pi # # pip4 # # The eighth circle. #
[docs]def pip4(*_args): return pi() / 4
Math.Complex.pip4 = pip4 # # _uplog10 # # Used in log10(). # # # pip2 # # The quarter circle #
[docs]def pip2(*_args): return pi() / 2
Math.Complex.pip2 = pip2 # # i # # The number defined as i*i = -1; #
[docs]def i(*_args): global i_v if i_v: return i_v i_v = perllib.bless(perllib.Hash(), "Math.Complex") i_v["cartesian"] = perllib.Array([0, 1]) i_v["polar"] = perllib.Array([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. #
[docs]def pi4(*_args): return 4 * pi()
Math.Complex.pi4 = pi4 # # pi2 # # The full circle #
[docs]def pi2(*_args): return 2 * pi()
Math.Complex.pi2 = pi2 def _stringify_polar(*_args): _args = list(_args) a = 0 b = None 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 ( _m := re.search( re.compile(rf"^-?{perllib.quotemeta(_bn(Math.Complex.Inf_v))}$", 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"[{_bn(r)},{_bn(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"{_bn(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"[{_bn(r)},{_bn(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 perllib.num(theta_v) > pi(): theta_v = perllib.num(theta_v) - pi2() # SNOOPYJC elif perllib.num(theta_v) <= -pi(): theta_v = perllib.num(theta_v) + pi2() # SNOOPYJC return theta_v # SNOOPYJC Math.Complex._theta = _theta # # arg # # Compute or set complex's argument (theta). # # # acoth # # Computes the area/inverse hyperbolic cotangent acoth(z) = 1/2 log((1+z) / (z-1)). #
[docs]def acoth(*_args): [z] = perllib.list_of_n(_args, 1) if perllib.num(z) == 0: _divbyzero("acoth(0)") if not perllib.ref_scalar(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"{_bn(z)} - 1") if 1 + perllib.num(z) == 0: _logofzero("acoth(-1)", f"1 + {_bn(z)}") return perllib.num(log((1 + perllib.num(z)) / (perllib.num(z) - 1))) / 2
Math.Complex.acoth = acoth # # acotanh # # Alias for acot(). #
[docs]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)). #
[docs]def atanh(*_args): [z] = perllib.list_of_n(_args, 1) if not perllib.ref_scalar(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 - {_bn(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)) #
[docs]def asinh(*_args): t = 0 [z] = perllib.list_of_n(_args, 1) if not perllib.ref_scalar(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). #
[docs]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(). #
[docs]def acosech(*_args): return Math.Complex.acsch(*_args)
Math.Complex.acosech = acosech
[docs]def acosh(*_args): [z] = perllib.list_of_n(_args, 1) if not perllib.ref_scalar(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 perllib.num(u)
Math.Complex.acosh = acosh
[docs]def asech(*_args): [z] = perllib.list_of_n(_args, 1) if perllib.num(z) == 0: _divbyzero("asech(0)", f"{_bn(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)). #
[docs]def atan(*_args): [z] = perllib.list_of_n(_args, 1) if not perllib.ref_scalar(z): return math.atan2(perllib.num(z), 1) [x, y] = perllib.list_of_n(z._cartesian() if perllib.ref_scalar(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) #
[docs]def acot(*_args): [z] = perllib.list_of_n(_args, 1) if perllib.num(z) == 0: _divbyzero("acot(0)") if not perllib.ref_scalar(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(). #
[docs]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)). #
[docs]def asin(*_args): z = _args[0] if (not perllib.ref_scalar(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_scalar(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 perllib.method_call((perllib.ref_scalar(z)), "make", u, v)
Math.Complex.asin = asin # # acsc # # Computes the arc cosecant acsc(z) = asin(1 / z). #
[docs]def acsc(*_args): [z] = perllib.list_of_n(_args, 1) if perllib.num(z) == 0: _divbyzero(f"acsc({_bn(z)})", z) return asin(1 / perllib.num(z))
Math.Complex.acsc = acsc # # acosec # # Alias for acsc(). #
[docs]def acosec(*_args): return Math.Complex.acsc(*_args)
Math.Complex.acosec = acosec
[docs]def acos(*_args): z = _args[0] if (not perllib.ref_scalar(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_scalar(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 perllib.method_call((perllib.ref_scalar(z)), "make", u, v)
Math.Complex.acos = acos
[docs]def asec(*_args): [z] = perllib.list_of_n(_args, 1) if perllib.num(z) == 0: _divbyzero(f"asec({_bn(z)})", z) return acos(1 / perllib.num(z))
Math.Complex.asec = asec # # logn # # Compute logn(z,n) = log(z) / log(n) #
[docs]def logn(*_args): global LOGN [z, n] = perllib.list_of_n(_args, 2) if not perllib.ref_scalar(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
[docs]def ln(*_args): return Math.Complex.log(*_args)
Math.Complex.ln = ln # # log10 # # Compute log10(z). # def _uplog10(*_args): return 1 / math.log(10.0) Math.Complex._uplog10 = _uplog10
[docs]def log10(*_args): return perllib.num(Math.Complex.log(_args[0])) * _uplog10()
Math.Complex.log10 = log10
[docs]def root(*_args): i_l = 0 root_a = perllib.Array() theta_l = 0 w = "" [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_scalar(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 = _pb(perllib.ref_scalar(z) and perllib.num(z.get("c_dirty")) == 0) if len(_args) == 2: root_a = perllib.Array() (i_l := 0, theta_l := perllib.num(t) / perllib.num(n)) while i_l < perllib.num(n): w = cplxe(rho_v, theta_l) # Yes, $cartesian is loop invariant. root_a.extend( perllib.make_list(cplx(*perllib.method_call(w, "_cartesian")) if cartesian else w) ) (((i_l := i_l + 1) - 1), (theta_l := theta_l + 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(*perllib.method_call(w, "_cartesian")) if cartesian else w
Math.Complex.root = root
[docs]def cbrt(*_args): [z] = perllib.list_of_n(_args, 1) if not perllib.ref_scalar(z): return ( -math.exp(perllib.flt(math.log(-perllib.flt(z)) / 3.0)) if perllib.num(z) < 0 else ( math.exp(perllib.flt(math.log(perllib.flt(z)) / 3.0)) if perllib.num(z) > 0 else 0 ) ) [r, t] = perllib.list_of_n(z._polar(), 2) if perllib.num(r) == 0: return 0 return perllib.method_call( (perllib.ref_scalar(z)), "emake", math.exp(perllib.flt(math.log(perllib.flt(r)) / 3.0)), perllib.num(t) / 3, )
Math.Complex.cbrt = cbrt
[docs]def arg(*_args): [z, theta_v] = perllib.list_of_n(_args, 2) if not perllib.ref_scalar(z): return z if theta_v is not None: # SNOOPYJC _theta(\$theta); theta_v = _theta(theta_v) # SNOOPYJC z["polar"] = perllib.Array([(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). #
[docs]def theta(*_args): return Math.Complex.arg(*_args)
Math.Complex.theta = theta # # (abs) # # Compute or set complex's norm (rho). #
[docs]def abs_(*_args): _args = list(_args) perllib.init_out_parameters(_args, 0) global _d [z, rho_v] = perllib.list_of_n(_args if _args else _d, 2) if not perllib.ref_scalar(z): if len(_args) == 2: perllib.store_out_parameter(_args, 0, _args[1]) else: return abs(perllib.num(z)) if rho_v is not None: z["polar"] = perllib.Array([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_
[docs]def rho(*_args): _args = list(_args) perllib.init_out_parameters(_args, 0) return Math.Complex.abs_(*_args)
Math.Complex.rho = rho # # (_conjugate) # # Compute complex's _conjugate. # def _conjugate(*_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) return perllib.method_call((perllib.ref_scalar(z)), "emake", r, -perllib.num(t)) [re_, im] = perllib.list_of_n(z._cartesian(), 2) return perllib.method_call((perllib.ref_scalar(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 perllib.method_call((perllib.ref_scalar(z)), "emake", r, t) [re_, im] = perllib.list_of_n(z._cartesian(), 2) return perllib.method_call( (perllib.ref_scalar(z)), "make", -perllib.num(re_), -perllib.num(im) ) Math.Complex._negate = _negate def _divide(*_args): d = 0 r1 = None r2 = None t = 0 t1 = None t2 = None u = 0 v = 0 x1 = None x2 = None y1 = None y2 = None [z1, z2, inverted] = perllib.list_of_n(_args, 3) if ( perllib.num(z1.get("p_dirty")) == 0 and perllib.ref_scalar(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 if inverted: if perllib.num(r1) == 0: _divbyzero(f"{_bn(z2)}/0") t = perllib.num(t2) - perllib.num(t1) if t > pi(): t -= pi2() elif t <= -pi(): t += pi2() return perllib.method_call( (perllib.ref_scalar(z1)), "emake", perllib.num(r2) / perllib.num(r1), t ) else: if perllib.num(r2) == 0: _divbyzero(f"{_bn(z1)}/0") t = perllib.num(t1) - perllib.num(t2) if t > pi(): t -= pi2() elif t <= -pi(): t += pi2() return perllib.method_call( (perllib.ref_scalar(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"{_bn(z2)}/0") return perllib.method_call( (perllib.ref_scalar(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_scalar(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"{_bn(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 perllib.method_call((perllib.ref_scalar(z1)), "make", u, v) else: if perllib.num(z2) == 0: _divbyzero(f"{_bn(z1)}/0") return perllib.method_call( (perllib.ref_scalar(z1)), "make", perllib.num(x1) / perllib.num(z2), perllib.num(y1) / perllib.num(z2), ) Math.Complex._divide = _divide # # (_power) # # Computes z1**z2 = exp(z2 * log z1)). # # # (_multiply) # # Computes z1*z2. # def _multiply(*_args): r1 = None r2 = None t = 0 t1 = None t2 = None x1 = None x2 = None y1 = None y2 = None [z1, z2, regular] = perllib.list_of_n(_args, 3) if ( perllib.num(z1.get("p_dirty")) == 0 and perllib.ref_scalar(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(perllib.Array([r1 * r2, t])) return z1 return perllib.method_call( (perllib.ref_scalar(z1)), "emake", perllib.num(r1) * perllib.num(r2), t ) else: [x1, y1] = perllib.list_of_n(z1._cartesian(), 2) if perllib.ref_scalar(z2): [x2, y2] = perllib.list_of_n(z2._cartesian(), 2) return perllib.method_call( (perllib.ref_scalar(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 perllib.method_call( (perllib.ref_scalar(z1)), "make", perllib.num(x1) * perllib.num(z2), perllib.num(y1) * perllib.num(z2), ) Math.Complex._multiply = _multiply 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 ( _pb(perllib.num(z1.get("c_dirty")) == 0) and cplx(*w._cartesian()) if (not perllib.ref_scalar(z2) or perllib.num(z2.get("c_dirty")) == 0) else w ) Math.Complex._power = _power # # (_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_scalar(z2): z2 = cplx(z2) [re2, im2] = perllib.list_of_n(z2._cartesian(), 2) if inverted is None: z1._set_cartesian(perllib.Array([re1 - re2, im1 - im2])) return z1 return ( perllib.method_call( (perllib.ref_scalar(z1)), "make", perllib.num(re2) - perllib.num(re1), perllib.num(im2) - perllib.num(im1), ) if inverted else perllib.method_call( (perllib.ref_scalar(z1)), "make", perllib.num(re1) - perllib.num(re2), perllib.num(im1) - perllib.num(im2), ) ) Math.Complex._minus = _minus 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_scalar(z2): z2 = cplx(z2) [re2, im2] = perllib.list_of_n(z2._cartesian() if perllib.ref_scalar(z2) else (z2, 0), 2) if regular is None: z1._set_cartesian(perllib.Array([re1 + re2, im1 + im2])) return z1 return perllib.method_call( (perllib.ref_scalar(z1)), "make", perllib.num(re1) + perllib.num(re2), perllib.num(im1) + perllib.num(im2), ) Math.Complex._plus = _plus def _copy(*_args): _args = list(_args) self = _args.pop(0) if _args else None clone = self.copy() if self.get("cartesian"): clone["cartesian"] = perllib.Array([*self.get("cartesian")]) if self.get("polar"): clone["polar"] = perllib.Array([*self.get("polar")]) clone = perllib.bless(clone, "Math.Complex") return clone Math.Complex._copy = _copy # # ->make # # Create a new complex number (cartesian form) # 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*{_bn(gre).pattern}\s*(?:,\s*{_bn(gre).pattern}\s*)?\]\s*$", _str(arg_v) ): [p, q] = (_m.group(1), _m.group(2) or 0) elif _m := re.search( rf"^\s*\[\s*{_bn(gre).pattern}\s*(?:,\s*([-+]?\d*\s*)?pi(?:/\s*(\d+))?\s*)?\]\s*$", _str(arg_v), ): [p, q] = ( _m.group(1), (-1 if _m.group(2) == "-" else (perllib.num(_m.group(2)) or 1)) * pi() / (perllib.num(_m.group(3)) or 1), ) elif _m := re.search(rf"^\s*\[\s*{_bn(gre).pattern}\s*\]\s*$", _str(arg_v)): [p, q] = (_m.group(1), 0) elif _m := re.search(rf"^\s*{_bn(gre).pattern}\s*$", _str(arg_v)): [p, q] = (_m.group(1), 0) 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 _f241(_m_): global _m _m = _m_ return f"{_bn(_m.group(1))}9**9**9" p = re.sub(re.compile(r"^(-?)inf$"), _f241, _str(p), count=1) if Math.Complex.has_inf_v: def _f242(_m_): global _m _m = _m_ return f"{_bn(_m.group(1))}9**9**9" q = re.sub(re.compile(r"^(-?)inf$"), _f242, _str(q), count=1) return perllib.Array([p, q]) Math.Complex._emake = _emake def _make(*_args): _args = list(_args) arg_v = _args.pop(0) if _args else None p = q = None if _m := re.search(rf"^{_bn(gre).pattern}$", _str(arg_v)): [p, q] = (_m.group(1), 0) elif _m := re.search(rf"^(?:{_bn(gre).pattern})?{_bn(gre).pattern}\s*i\s*$", _str(arg_v)): [p, q] = (_m.group(1) or 0, _m.group(2)) elif _m := re.search( rf"^\s*\(\s*{_bn(gre).pattern}\s*(?:,\s*{_bn(gre).pattern}\s*)?\)\s*$", _str(arg_v) ): [p, q] = (_m.group(1), _m.group(2) or 0) if p is not None: p = re.sub(r"^\+", r"", _str(p), count=1) if Math.Complex.has_inf_v: def _f216(_m_): global _m _m = _m_ return f"{_bn(_m.group(1))}9**9**9" p = re.sub(re.compile(r"^(-?)inf$"), _f216, _str(p), count=1) q = re.sub(r"^\+", r"", _str(q), count=1) if Math.Complex.has_inf_v: def _f218(_m_): global _m _m = _m_ return f"{_bn(_m.group(1))}9**9**9" q = re.sub(re.compile(r"^(-?)inf$"), _f218, _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 {_bn(_args[0])} of '{_bn(_args[1])}'.\n" ) Math.Complex._cannot_make = _cannot_make # # ->emake # # Create a new complex number (exponential form) #
[docs]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] = (0, 0) elif len(_args) == 1: if (re.search(r"^\s*\(", _str(_args[0]))) or (re.search(r"i\s*$", _str(_args[0]))): return perllib.method_call((perllib.ref_scalar(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"^{_bn(gre).pattern}$", _str(rho_v)))): _cannot_make("rho", rho_v) theta_v = theta_v or 0 if not ((_m := re.search(rf"^{_bn(gre).pattern}$", _str(theta_v)))): _cannot_make("theta", theta_v) self._set_polar(perllib.Array([rho_v, theta_v])) self.display_format("polar") return self
Math.Complex.emake = types.MethodType(emake, Math.Complex)
[docs]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] = (0, 0) elif len(_args) == 1: if re.search(r"^\s*\[", _str(_args[0])): return perllib.method_call((perllib.ref_scalar(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"^{_bn(gre).pattern}$", _str(re_)))): _cannot_make("real part", re_) im = im or 0 if not ((_m := re.search(rf"^{_bn(gre).pattern}$", _str(im)))): _cannot_make("imaginary part", im) self._set_cartesian(perllib.Array([re_, im])) self.display_format("cartesian") return self
Math.Complex.make = types.MethodType(make, Math.Complex)
[docs]def new(*_args): return make(*_args)
Math.Complex.new = types.MethodType(new, Math.Complex) def __gt__(self, other): # extra overload 'gt' return str(self) > str(other) Math.Complex.__gt__ = __gt__ def __ge__(self, other): # extra overload 'ge' return str(self) >= str(other) Math.Complex.__ge__ = __ge__ def __ne__(self, other): # extra overload 'ne' return str(self) != str(other) Math.Complex.__ne__ = __ne__ def __le__(self, other): # extra overload 'le' return str(self) <= str(other) Math.Complex.__le__ = __le__ def __lt__(self, other): # extra overload 'lt' return str(self) < str(other) 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__ def __BEGIN__21(): global i_v, t_l 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" return Math.Complex.DBL_MAX_h = perllib.Hash( { "4": "1.70141183460469229e+38", "8": "1.7976931348623157e+308", # AFAICT the 10, 12, and 16-byte long doubles # all have the same maximum. "10": "1.1897314953572317650857593266280070162E+4932", "12": "1.1897314953572317650857593266280070162E+4932", "16": "1.1897314953572317650857593266280070162E+4932", } ) # These are IEEE 754 maxima. Math.Complex.nvsize_v = ( Config.Config_h.get("nvsize") or (Config.Config_h.get("uselongdouble") and Config.Config_h.get("longdblsize")) or Config.Config_h.get("doublesize") ) if Math.Complex.nvsize_v is None: raise Die("Math::Complex: Could not figure out nvsize\n") if not (Math.Complex.DBL_MAX_h.get(_str(Math.Complex.nvsize_v)) is not None): raise Die( f"Math::Complex: Cannot not figure out max nv (nvsize = {_bn(Math.Complex.nvsize_v)})\n" ) _eval_result57 = None try: _eval_result57 = Math.Complex.DBL_MAX_h.get(_str(Math.Complex.nvsize_v)) except Exception: pass Math.Complex.DBL_MAX_v = _eval_result57 if Math.Complex.DBL_MAX_v is None: raise Die( f"Math::Complex: Could not figure out max nv (nvsize = {_bn(Math.Complex.nvsize_v)})\n" ) Math.Complex.BIGGER_THAN_THIS_v = 1e30 # Must find something bigger than this. if perllib.os_name() == "unicosmk": Math.Complex.Inf_v = Math.Complex.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 = 0 # We do want an arithmetic overflow, Inf INF inf Infinity. for Math.Complex.t_l in ( "exp(99999)", # Enough even with 128-bit long doubles. "inf", "Inf", "INF", "infinity", "Infinity", "INFINITY", "1e99999", ): try: _locals_stack.append(perllib.WARNING) perllib.WARNING = 0 _eval_result78 = None try: _eval_result78 = perllib.num(t_l) + 1.0 # FAILTRAN except Exception: pass i_v = _eval_result78 if i_v is not None and perllib.num(i_v) > perllib.num( Math.Complex.BIGGER_THAN_THIS_v ): 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 = Math.Complex.DBL_MAX_v if not ( perllib.num(Math.Complex.Inf_v) > perllib.num(Math.Complex.BIGGER_THAN_THIS_v) ): raise Die("Math::Complex: Could not get Infinity") _eval_result87 = None try: _eval_result87 = exp(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"; Math.Complex.__BEGIN__21 = __BEGIN__21 # SNOOPYJC: set_prototype not implemented and not needed # SNOOPYJC use Scalar::Util qw(set_prototype); DISPLAY_FORMAT = perllib.Hash() LOGN = perllib.Hash() 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.has_inf_v = perllib.init_global("Math.Complex", "has_inf_v", None) Math.Complex.has_nan_v = perllib.init_global("Math.Complex", "has_nan_v", "") Math.Complex.vax_float_v = perllib.init_global("Math.Complex", "vax_float_v", "") _d = "" i_v = None t_l = "" __BEGIN__21() builtins.__PACKAGE__ = "Math.Complex" for _ in range(1): pass # SKIPPED: use 5.006; } # SKIPPED: use strict; Math.Complex.VERSION_v = 1.59_02 perllib.WARNING = 1 perllib.WARNING = 0 # 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 } # SKIPPED: use subs qw/abs cos exp log sin sqrt atan2/; # 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.copy(), "pi": pi_a.copy(), } ) setattr(Math.Complex, "(=", _copy) setattr(Math.Complex, "(+=", _plus) setattr(Math.Complex, "(+", _plus) setattr(Math.Complex, "(-=", _minus) setattr(Math.Complex, "(-", _minus) setattr(Math.Complex, "(*=", _multiply) setattr(Math.Complex, "(*", _multiply) setattr(Math.Complex, "(/=", _divide) setattr(Math.Complex, "(/", _divide) setattr(Math.Complex, "(**=", _power) setattr(Math.Complex, "(**", _power) setattr(Math.Complex, "(==", _numeq) setattr(Math.Complex, "(<=>", _spaceship) setattr(Math.Complex, "(neg", _negate) setattr(Math.Complex, "(~", _conjugate) setattr(Math.Complex, "(abs", abs_) setattr(Math.Complex, "(sqrt", sqrt) setattr(Math.Complex, "(exp", exp) setattr(Math.Complex, "(log", log) setattr(Math.Complex, "(sin", sin) setattr(Math.Complex, "(cos", cos) setattr(Math.Complex, "(atan2", atan2) setattr(Math.Complex, '(""', _stringify) def __lt__(self, other): # extra overload '<' return _spaceship(self, other, False) < 0 Math.Complex.__lt__ = __lt__ def __le__(self, other): # extra overload '<=' return _spaceship(self, other, False) <= 0 Math.Complex.__le__ = __le__ def __ne__(self, other): # extra overload '!=' return _spaceship(self, other, False) != 0 Math.Complex.__ne__ = __ne__ def __ge__(self, other): # extra overload '>=' return _spaceship(self, other, False) >= 0 Math.Complex.__ge__ = __ge__ def __gt__(self, other): # extra overload '>' return _spaceship(self, other, False) > 0 Math.Complex.__gt__ = __gt__ _stringify # # Package "privates" # DISPLAY_FORMAT = perllib.Hash({"style": "cartesian", "polar_pretty_print": 1}) eps = 1e-14 # Epsilon