codon/stdlib/numpy/zmath.codon

1085 lines
26 KiB
Python

# Copyright (C) 2022-2025 Exaloop Inc. <https://exaloop.io>
# Adapted from NumPy's "npy_core_math_complex"
import util
SCALED_CEXP_LOWERF = 88.722839 # f
SCALED_CEXP_UPPERF = 192.69492 # f
SCALED_CEXP_LOWER = 710.47586007394386
SCALED_CEXP_UPPER = 1454.9159319953251
#SCALED_CEXP_LOWERL = 11357.216553474703895 # L
#SCALED_CEXP_UPPERL = 22756.021937783004509 # L
def _bad_input(F: type):
compile_error("internal error: bad input type '" + F.__name__ + "'")
def scaled_cexp_lower(z):
if isinstance(z, complex):
return SCALED_CEXP_LOWER
elif isinstance(z, complex64):
return float32(SCALED_CEXP_LOWERF)
else:
_bad_input(type(z))
def scaled_cexp_upper(z):
if isinstance(z, complex):
return SCALED_CEXP_UPPER
elif isinstance(z, complex64):
return float32(SCALED_CEXP_UPPERF)
else:
_bad_input(type(z))
def scaled_cexp_k(z):
if isinstance(z, float):
return 1799
elif isinstance(z, float32):
return 235
else:
_bad_input(type(z))
def scaled_cexp(x, y, expt: int, C: type):
F = type(x)
k = scaled_cexp_k(x)
kln2 = F(k * util.LOGE2)
mant, ex = util.frexp(util.exp(x - kln2))
mantcos, excos = util.frexp(util.cos(y))
mantsin, exsin = util.frexp(util.sin(y))
expt += ex + k
return C(util.ldexp(mant * mantcos, expt + excos),
util.ldexp(mant * mantsin, expt + exsin))
def cosh_big(z):
if isinstance(z, float):
return 22.0
elif isinstance(z, float32):
return float32(9.0)
else:
_bad_input(type(z))
def cosh_huge(z):
if isinstance(z, float):
return 8.9884656743115795e+307
elif isinstance(z, float32):
return float32(1.70141183e+38)
else:
_bad_input(type(z))
def tanh_huge(z):
if isinstance(z, float):
return 22.0
elif isinstance(z, float32):
return float32(11.0)
else:
_bad_input(type(z))
def arg(z):
return util.atan2(z.imag, z.real)
def exp(z):
@pure
@C
def cexp(r: float, i: float) -> complex:
pass
@C
def cnp_cexpf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return cexp(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_cexpf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex exp(): got non-complex input type")
def _exp_impl(z):
r = z.real
i = z.imag
C = type(z)
F = type(r)
ret = C()
nan = util.nan(F)
if util.isfinite(r):
if r >= scaled_cexp_lower(z) and r <= scaled_cexp_upper(z):
ret = scaled_cexp(r, i, 0, C)
else:
x = util.exp(r)
c = util.cos(i)
s = util.sin(i)
if util.isfinite(i):
ret = C(x * c, x * s)
else:
ret = C(nan, util.copysign(nan, i))
elif util.isnan(r):
if i == F(0):
ret = z
else:
ret = C(r, util.copysign(nan, i))
else:
if r > F(0):
if i == F(0):
return C(r, i)
elif util.isfinite(i):
c = util.cos(i)
s = util.sin(i)
ret = C(r * c, r * s)
else:
# npy_set_floatstatus_invalid()
ret = C(r, nan)
else:
if util.isfinite(i):
x = util.exp(r)
c = util.cos(i)
s = util.sin(i)
r = C(x * c, x * s)
else:
ret = C()
return ret
def log(z):
@pure
@C
def clog(r: float, i: float) -> complex:
pass
@C
def cnp_clogf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return clog(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_clogf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex log(): got non-complex input type")
def _log_impl(z):
C = type(z)
F = type(z.real)
ax = util.fabs(z.real)
ay = util.fabs(z.imag)
rr = F()
ri = F()
maxnum = util.maxnum(F)
minnum = util.minnum(F)
mantdig = util.mantdig(F)
if ax > maxnum / F(4) or ay > maxnum / F(4):
rr = util.log(util.hypot(ax / F(2), ay / F(2))) + F(util.LOGE2)
elif ax < minnum and ay < minnum:
if ax > F(0) or ay > F(0):
rr = util.log(util.hypot(util.ldexp(ax, mantdig),
util.ldexp(ay, mantdig))) - F(mantdig)*F(util.LOGE2)
else:
rr = F(-1) / z.real
rr = util.copysign(rr, F(-1))
ri = arg(z)
return C(rr, ri)
else:
h = util.hypot(ax, ay)
if F(0.71) <= h <= F(1.73):
am = ax if ax > ay else ay
an = ay if ax > ay else ax
rr = util.log1p((am-F(1))*(am+F(1))+an*an)/F(2)
else:
rr = util.log(h)
ri = arg(z)
return C(rr, ri)
def sqrt(z):
@pure
@C
def csqrt(r: float, i: float) -> complex:
pass
@C
def cnp_csqrtf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return csqrt(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_csqrtf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex sqrt(): got non-complex input type")
def _sqrt_impl(z):
a = z.real
b = z.imag
C = type(z)
F = type(a)
thresh = util.maxnum(F) / F(1 + util.SQRT2)
scale = False
result = C()
if a == F(0) and b == F(0):
return C(F(0), b)
if util.isinf(b):
return C(util.inf(F), b)
if util.isnan(a):
t = (b - b) / (b - b)
return C(a, t)
if util.isinf(a):
if util.signbit(a):
return C(util.fabs(b - b), util.copysign(a, b))
else:
return C(a, util.copysign(b - b, b))
if util.fabs(a) >= thresh or util.fabs(b) >= thresh:
a *= F(0.25)
b *= F(0.25)
scale = True
else:
scale = False
if a >= F(0):
t = util.sqrt((a + util.hypot(a, b)) * F(0.5))
result = C(t, b / (F(2) * t))
else:
t = util.sqrt((-a + util.hypot(a, b)) * F(0.5))
result = C(util.fabs(b) / (F(2) * t), util.copysign(t, b))
if scale:
return C(result.real * F(2), result.imag)
else:
return result
def cosh(z):
@pure
@C
def ccosh(r: float, i: float) -> complex:
pass
@C
def cnp_ccoshf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return ccosh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_ccoshf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex cosh(): got non-complex input type")
def _cosh_impl(z):
x = z.real
y = z.imag
xfinite = util.isfinite(x)
yfinite = util.isfinite(y)
big = cosh_big(x)
huge = cosh_huge(x)
C = type(z)
F = type(x)
if xfinite and yfinite:
if y == F(0):
return C(util.cosh(x), x * y)
absx = util.fabs(x)
if absx < big:
return C(util.cosh(x) * util.cos(y),
util.sinh(x) * util.sin(y))
if absx < scaled_cexp_lower(z):
h = util.exp(absx) * F(0.5)
return C(h * util.cos(y),
util.copysign(h, x) * util.sin(y))
elif absx < scaled_cexp_upper(z):
z = scaled_cexp(absx, y, -1, C)
return C(z.real, z.imag * util.copysign(F(1), x))
else:
h = huge * x
return C(h * h * util.cos(y), h * util.sin(y))
if x == F(0) and not yfinite:
return C(y - y, util.copysign(F(0), x * (y - y)))
if y == F(0) and not xfinite:
return C(x * x, util.copysign(F(0), x) * y)
if xfinite and not yfinite:
return C(y - y, x * (y - y))
if util.isinf(x):
if not yfinite:
return C(x * x, x * (y - y))
return C((x * x) * util.cos(y), x * util.sin(y))
return C((x * x) * (y - y), (x + x) * (y - y))
def sinh(z):
@pure
@C
def csinh(r: float, i: float) -> complex:
pass
@C
def cnp_csinhf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return csinh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_csinhf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex sinh(): got non-complex input type")
def _sinh_impl(z):
x = z.real
y = z.imag
xfinite = util.isfinite(x)
yfinite = util.isfinite(y)
big = cosh_big(x)
huge = cosh_huge(x)
C = type(z)
F = type(x)
if xfinite and yfinite:
if y == F(0):
return C(util.sinh(x), y)
absx = util.fabs(x)
if absx < big:
return C(util.sinh(x) * util.cos(y),
util.cosh(x) * util.sin(y))
if absx < scaled_cexp_lower(z):
h = util.exp(absx) * F(0.5)
return C(util.copysign(h, x) * util.cos(y),
h * util.sin(y))
elif absx < scaled_cexp_upper(z):
z = scaled_cexp(absx, y, -1, C)
return C(z.real * util.copysign(F(1), x), z.imag)
else:
h = huge * x
return C(h * util.cos(y), h * h * util.sin(y))
if x == F(0) and not yfinite:
return C(util.copysign(F(0), x * (y - y)), y - y)
if y == F(0) and not xfinite:
if util.isnan(x):
return z
return C(x, util.copysign(F(0), y))
if xfinite and not yfinite:
return C(y - y, x * (y - y))
if not xfinite and not util.isnan(x):
if not yfinite:
return C(x * x, x * (y - y))
return C(x * util.cos(y),
util.inf(F) * util.sin(y))
return C((x * x) * (y - y), (x + x) * (y - y))
def tanh(z):
@pure
@C
def ctanh(r: float, i: float) -> complex:
pass
@C
def cnp_ctanhf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return ctanh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_ctanhf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex tanh(): got non-complex input type")
def _tanh_impl(z):
x = z.real
y = z.imag
xfinite = util.isfinite(x)
yfinite = util.isfinite(y)
huge = tanh_huge(x)
C = type(z)
F = type(x)
if not util.isfinite(x):
if util.isnan(x):
return C(x, y if y == F(0) else x * y)
return C(util.copysign(F(1), x),
util.copysign(F(0), y if util.isinf(y) else util.sin(y) * util.cos(y)))
if not util.isfinite(y):
return C(y - y, y - y)
if util.fabs(x) >= huge:
exp_mx = util.exp(-util.fabs(x))
return C(util.copysign(F(1), x),
F(4) * util.sin(y) * util.cos(y) *
exp_mx * exp_mx)
t = util.tan(y)
beta = F(1) + t * t
s = util.sinh(x)
rho = util.sqrt(F(1) + s * s)
denom = F(1) + beta * s * s
return C((beta * rho * s) / denom, t / denom)
def _f(a, b, hypot_a_b):
F = type(a)
if b < F(0):
return (hypot_a_b - b) / F(2)
if b == F(0):
return a / F(2)
return a * a / (hypot_a_b + b) / F(2);
def _hard_work_params(F: type): # -> (A_crossover, B_crossover, FOUR_SQRT_MIN)
if F is float:
return (10.0, 0.6417, 5.9666725849601654e-154)
elif F is float32:
return (float32(10.0), float32(0.6417), float32(4.3368086899420177e-19))
else:
_bad_input(F)
def _do_hard_work(x, y): # -> (rx, B_is_usable, B, sqrt_A2my2, new_y)
F = type(x)
A_crossover, B_crossover, four_sqrt_min = _hard_work_params(F)
eps = util.eps(F)
rx = F()
sqrt_A2my2 = F()
R = util.hypot(x, y + F(1))
S = util.hypot(x, y - F(1))
A = (R + S) / F(2)
B = F()
if A < F(1):
A = F(1)
if A < A_crossover:
if y == F(1) and x < eps * eps / F(128):
rx = util.sqrt(x)
elif x >= eps * util.fabs(y - F(1)):
Am1 = _f(x, F(1) + y, R) + _f(x, F(1) - y, S)
rx = util.log1p(Am1 + util.sqrt(Am1 * (A + F(1))))
elif y < F(1):
rx = x / util.sqrt((F(1) - y) * (F(1) + y))
else:
rx = util.log1p(y - F(1) + util.sqrt((y - F(1)) * (y + F(1))))
else:
rx = util.log(A + util.sqrt(A * A - F(1)))
new_y = y
B_is_usable = False
if y < four_sqrt_min:
B_is_usable = False
sqrt_A2my2 = A * (F(2) / eps)
new_y = y * (F(2) / eps)
return rx, B_is_usable, B, sqrt_A2my2, new_y
B = y / A
B_is_usable = True
if B > B_crossover:
B_is_usable = False
if y == F(1) and x < eps / F(128):
sqrt_A2my2 = util.sqrt(x) * util.sqrt((A + y) / F(2))
elif x >= eps * util.fabs(y - F(1)):
Amy = _f(x, y + F(1), R) + _f(x, y - F(1), S)
sqrt_A2my2 = util.sqrt(Amy * (A + y))
elif y > F(1):
sqrt_A2my2 = (x * (F(4) / eps / eps) * y /
util.sqrt((y + F(1)) * (y - F(1))))
new_y = y * (F(4) / eps / eps)
else:
sqrt_A2my2 = util.sqrt((F(1) - y) * (F(1) + y))
return rx, B_is_usable, B, sqrt_A2my2, new_y
def _clog_for_large_values_params(F: type): # -> (QUARTER_SQRT_MAX, SQRT_MIN)
if F is float:
return (3.3519519824856489e+153, 1.4916681462400413e-154)
elif F is float32:
return (float32(4.611685743549481e+18), float32(1.0842021724855044e-19))
else:
_bad_input(F)
def _clog_for_large_values(x: F, y: F, F: type):
quarter_sqrt_max, sqrt_min = _clog_for_large_values_params(F)
rr = F()
ri = F()
ax = util.fabs(x)
ay = util.fabs(y)
if ax < ay:
ax, ay = ay, ax
if ax > util.maxnum(F) / F(2):
rr = util.log(util.hypot(x / F(util.E), y / F(util.E))) + F(1)
elif ax > quarter_sqrt_max or ay < sqrt_min:
rr = util.log(util.hypot(x, y))
else:
rr = util.log(ax * ax + ay * ay) / F(2)
ri = util.atan2(y, x)
return rr, ri
def _acos_params(F: type): # -> (SQRT_6_EPSILON, pio2_lo)
if F is float:
return (3.65002414998885671e-08, 6.1232339957367659e-17)
elif F is float32:
return (float32(8.4572793338e-4), float32(7.5497899549e-9))
else:
_bad_input(F)
def acos(z):
@pure
@C
def cacos(r: float, i: float) -> complex:
pass
@C
def cnp_cacosf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return cacos(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_cacosf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex acos(): got non-complex input type")
def _acos_impl(z):
x = z.real
y = z.imag
C = type(z)
F = type(x)
sqrt_6_epsilon, pio2_lo = _acos_params(F)
pio2_hi = F(util.PI_2)
eps = util.eps(F)
recip_epsilon = F(1) / eps
sx = util.signbit(x)
sy = util.signbit(y)
ax = util.fabs(x)
ay = util.fabs(y)
rx = F()
ry = F()
if util.isnan(x) or util.isnan(y):
if util.isinf(x):
return C(y + y, -util.inf(F))
if util.isinf(y):
return C(x + x, -y)
if x == F(0):
return C(pio2_hi + pio2_lo, y + y)
return C(util.nan(F), util.nan(F))
if ax > recip_epsilon or ay > recip_epsilon:
wx, wy = _clog_for_large_values(x, y)
rx = util.fabs(wy)
ry = wx + F(util.LOGE2)
if not sy:
ry = -ry
return C(rx, ry)
if x == F(1) and y == F(0):
return C(F(0), -y)
# raise inexact
if ax < sqrt_6_epsilon / F(4) and ay < sqrt_6_epsilon / F(4):
return C(pio2_hi - (x - pio2_lo), -y)
ry, B_is_usable, B, sqrt_A2my2, new_x = _do_hard_work(ay, ax)
if B_is_usable:
if not sx:
rx = util.acos(B)
else:
rx = util.acos(-B)
else:
if not sx:
rx = util.atan2(sqrt_A2my2, new_x)
else:
rx = util.atan2(sqrt_A2my2, -new_x)
if not sy:
ry = -ry
return C(rx, ry)
def acosh(z):
@pure
@C
def cacosh(r: float, i: float) -> complex:
pass
@C
def cnp_cacoshf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return cacosh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_cacoshf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex acosh(): got non-complex input type")
def _acosh_impl(z):
C = type(z)
w = acos(z)
rx = w.real
ry = w.imag
if util.isnan(rx) and util.isnan(ry):
return C(ry, rx)
if util.isnan(rx):
return C(util.fabs(ry), rx)
if util.isnan(ry):
return C(ry, ry)
return C(util.fabs(ry), util.copysign(rx, z.imag))
def _asinh_params(F: type): # -> SQRT_6_EPSILON
if F is float:
return 3.65002414998885671e-08
elif F is float32:
return float32(8.4572793338e-4)
else:
_bad_input(F)
def asinh(z):
@pure
@C
def casinh(r: float, i: float) -> complex:
pass
@C
def cnp_casinhf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return casinh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_casinhf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex asinh(): got non-complex input type")
def _asinh_impl(z):
x = z.real
y = z.imag
C = type(z)
F = type(x)
sqrt_6_epsilon = _asinh_params(F)
recip_epsilon = F(1) / util.eps(F)
ax = util.fabs(x)
ay = util.fabs(y)
rx = F()
ry = F()
if util.isnan(x) or util.isnan(y):
if util.isinf(x):
return C(x, y + y)
if util.isinf(y):
return C(y, x + x)
if y == F(0):
return C(x + x, y)
return C(util.nan(F), util.nan(F))
if ax > recip_epsilon or ay > recip_epsilon:
if not util.signbit(x):
wx, wy = _clog_for_large_values(x, y)
wx += F(util.LOGE2)
else:
wx, wy = _clog_for_large_values(-x, -y)
wx += F(util.LOGE2)
return C(util.copysign(wx, x), util.copysign(wy, y))
if x == F(0) and y == F(0):
return z
# raise_inexact()
if ax < sqrt_6_epsilon / F(4) and ay < sqrt_6_epsilon / F(4):
return z
rx, B_is_usable, B, sqrt_A2my2, new_y = _do_hard_work(ax, ay)
if B_is_usable:
ry = util.asin(B)
else:
ry = util.atan2(new_y, sqrt_A2my2)
return C(util.copysign(rx, x), util.copysign(ry, y))
def _sum_squares_params(F: type): # -> SQRT_MIN
if F is float:
return 1.4916681462400413e-154
elif F is float32:
return float32(1.0842022e-19)
else:
_bad_input(F)
def _sum_squares(x, y):
F = type(x)
sqrt_min = _sum_squares_params(F)
if y < sqrt_min:
return x * x
return x * x + y * y
def _bitcast(x, T: type):
return Ptr[T](__ptr__(x).as_byte())[0]
def _get_float_word(x: float32):
return _bitcast(x, u32)
def _set_float_word(x: u32):
return _bitcast(x, float32)
def _real_part_reciprocal32(x, y):
F = type(x)
bias = util.maxexp(F) - 1
cutoff = util.mantdig(F) // 2 + 1
hx = _get_float_word(x)
ix = hx & u32(0x7f800000)
hy = _get_float_word(y)
iy = hy & u32(0x7f800000)
if ix - iy >= u32(cutoff << 23) or util.isinf(x):
return F(1) / x
if iy - ix >= u32(cutoff << 23):
return x / y / y
if ix <= u32((bias + util.maxexp(F) // 2 - cutoff) << 23):
return (x / (x * x + y * y))
scale = _set_float_word(u32(0x7f800000) - ix)
x *= scale
y *= scale
return x / (x * x + y * y) * scale
def _get_low_word(x):
return _bitcast(x, Tuple[u32, u32])[0]
def _get_high_word(x):
return _bitcast(x, Tuple[u32, u32])[1]
def _set_high_word(d, v):
w = (_get_low_word(d), v)
return _bitcast(w, float)
def _real_part_reciprocal64(x, y):
F = type(x)
bias = util.maxexp(F) - 1
cutoff = util.mantdig(F) // 2 + 1
hx = _get_high_word(x)
ix = hx & u32(0x7ff00000)
hy = _get_high_word(y)
iy = hy & u32(0x7ff00000)
if ix - iy >= u32(cutoff << 20) or util.isinf(x):
return F(1) / x
if iy - ix >= u32(cutoff << 20):
return x / y / y
if ix <= u32((bias + util.maxexp(F) // 2 - cutoff) << 20):
return (x / (x * x + y * y))
scale = _set_high_word(1.0, u32(0x7ff00000) - ix)
x *= scale
y *= scale
return x / (x * x + y * y) * scale
def _real_part_reciprocal(x, y):
if isinstance(x, float) and isinstance(y, float):
return _real_part_reciprocal64(x, y)
elif isinstance(x, float32) and isinstance(y, float32):
return _real_part_reciprocal32(x, y)
else:
_bad_input(type(x))
def _atanh_params(F: type): # -> (SQRT_3_EPSILON, pio2_lo)
if F is float:
return (2.5809568279517849e-8, 6.1232339957367659e-17)
elif F is float32:
return (float32(5.9801995673e-4), float32(7.5497899549e-9))
else:
_bad_input(F)
def atanh(z):
@pure
@C
def catanh(r: float, i: float) -> complex:
pass
@C
def cnp_catanhf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return catanh(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_catanhf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex atanh(): got non-complex input type")
def _atanh_impl(z):
x = z.real
y = z.imag
C = type(z)
F = type(x)
sqrt_3_epsilon, pio2_lo = _atanh_params(F)
pio2_hi = F(util.PI_2)
recip_epsilon = F(1) / util.eps(F)
ax = util.fabs(x)
ay = util.fabs(y)
rx = F()
ry = F()
if y == F(0) and ax <= F(1):
return C(util.atanh(x), y)
if x == F(0):
return C(x, util.atan(y))
if util.isnan(x) or util.isnan(y):
if util.isinf(x):
return C(util.copysign(F(0), x), y + y)
if util.isinf(y):
return C(util.copysign(F(0), x),
util.copysign(pio2_hi + pio2_lo, y))
return C(util.nan(F), util.nan(F))
if ax > recip_epsilon or ay > recip_epsilon:
return C(_real_part_reciprocal(x, y),
util.copysign(pio2_hi + pio2_lo, y))
if ax < sqrt_3_epsilon / F(2) and ay < sqrt_3_epsilon / F(2):
# raise_inexact()
return z
if ax == F(1) and ay < util.eps(F):
rx = F(util.LOGE2) - util.log(ay) / F(2)
else:
rx = util.log1p(F(4) * ax / _sum_squares(ax - F(1), ay)) / F(4)
if ax == F(1):
ry = util.atan2(F(2), -ay) / F(2)
elif ay < util.eps(F):
ry = util.atan2(F(2) * ay, (F(1) - ax) * (F(1) + ax)) / F(2)
else:
ry = util.atan2(F(2) * ay, (F(1) - ax) * (F(1) + ax) - ay * ay) / F(2)
return C(util.copysign(rx, x), util.copysign(ry, y))
def asin(z):
@pure
@C
def casin(r: float, i: float) -> complex:
pass
@C
def cnp_casinf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return casin(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_casinf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex asin(): got non-complex input type")
def _asin_impl(z):
C = type(z)
z = asinh(C(z.imag, z.real))
return C(z.imag, z.real)
def atan(z):
@pure
@C
def catan(r: float, i: float) -> complex:
pass
@C
def cnp_catanf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return catan(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_catanf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex atan(): got non-complex input type")
def _atan_impl(z):
C = type(z)
z = atanh(C(z.imag, z.real))
return C(z.imag, z.real)
def cos(z):
@pure
@C
def ccos(r: float, i: float) -> complex:
pass
@C
def cnp_ccosf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return ccos(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_ccosf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex cos(): got non-complex input type")
def _cos_impl(z):
C = type(z)
return cosh(C(-z.imag, z.real))
def sin(z):
@pure
@C
def csin(r: float, i: float) -> complex:
pass
@C
def cnp_csinf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return csin(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_csinf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex sin(): got non-complex input type")
def _sin_impl(z):
C = type(z)
z = sinh(C(-z.imag, z.real))
return C(z.imag, -z.real)
def tan(z):
@pure
@C
def ctan(r: float, i: float) -> complex:
pass
@C
def cnp_ctanf(r: float32, i: float32, z: Ptr[complex64]) -> None:
pass
if isinstance(z, complex):
return ctan(z.real, z.imag)
elif isinstance(z, complex64):
o = complex64()
cnp_ctanf(z.real, z.imag, __ptr__(o))
return o
else:
compile_error("complex tan(): got non-complex input type")
def _tan_impl(z):
C = type(z)
z = tanh(C(-z.imag, z.real))
return C(z.imag, -z.real)
def log2(z):
C = type(z)
F = type(z.real)
z = log(z)
z = C(z.real * F(util.LOG2E), z.imag * F(util.LOG2E))
return z
def log10(z):
C = type(z)
F = type(z.real)
z = log(z)
z = C(z.real * F(util.LOG10E), z.imag * F(util.LOG10E))
return z
def exp2(z):
C = type(z)
F = type(z.real)
z = C(z.real * F(util.LOGE2), z.imag * F(util.LOGE2))
return exp(z)
def expm1(z):
x = z.real
y = z.imag
C = type(z)
F = type(x)
a = util.sin(y / F(2))
rx = util.expm1(x) * util.cos(y) - F(2) * a * a
ry = util.exp(x) * util.sin(y)
return C(rx, ry)
def log1p(z):
x = z.real
y = z.imag
C = type(z)
F = type(x)
l = util.hypot(x + F(1), y)
ry = util.atan2(y, x + F(1))
rx = util.log(l)
return C(rx, ry)