1use crate::{Native, arc, exp as e, float::ln2, hyperbolic, ln as l, pow as p, special, trig};
7use num_complex::Complex;
8
9pub(crate) mod bulk;
10
11#[inline(always)]
12fn c<R>(re: R, im: R) -> Complex<R> { Complex::new(re, im) }
13
14#[inline(always)]
16fn turned<R: Native>(z: Complex<R>) -> Complex<R> { c(-z.im, z.re) }
17
18#[inline(always)]
20fn unturned<R: Native>(z: Complex<R>) -> Complex<R> { c(z.im, -z.re) }
21
22#[inline(always)]
24fn pi<R: Native>() -> R { let (hi, _) = arc::half_pi::<R>(); hi + hi }
25
26#[inline(always)]
28fn turns<R: Native>(y: R) -> (R, bool) {
29 let (q, _) = arc::half_pi::<R>();
30 let k = (y * R::narrow(std::f64::consts::FRAC_2_PI)).round_ties_even();
31 (k, (k.abs() <= R::narrow(256.0)) & (k * q == y))
32}
33
34#[inline(always)]
36pub(crate) fn quarter<R: Native>(y: R) -> Option<(R, R)> {
37 let (k, exact) = turns(y);
38 if !exact { return None; }
39 let (zero, one, m) = (R::zero(), R::one(), k - R::narrow(4.0) * (k * R::narrow(0.25)).floor());
40 Some(if m == zero { (one, if y == zero { y }
41 else { zero }) }
42 else if m == one { (zero, one) }
43 else if m == one + one { (-one, zero) }
44 else { (zero, -one) })
45}
46
47#[inline(always)]
49fn part<R: Native>(x: R, d: R, scale: R) -> R { if (d == R::zero()) & !x.is_nan() { d } else { scale * d } }
50
51#[inline(always)]
53pub fn divide<R: Native>(x: Complex<R>, y: Complex<R>) -> Complex<R> {
54 if (x.im == R::zero()) & (y.im == R::zero()) { c(x.re / y.re, R::zero()) } else { x / y }
55}
56
57#[inline(always)]
59pub(crate) fn modulus<R: Native, const FUSED: bool>(x: R, y: R) -> R {
60 let (a, b) = (x.wide(), y.wide());
61 if R::WIDE { p::hypot::<R, FUSED>(x, y) } else { R::narrow((a * a + b * b).sqrt()) }
62}
63
64#[inline]
66pub(crate) fn exp<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
67 let (x, y) = (z.re, z.im);
68 let half = e::exp_times::<R, FUSED>(x, -R::one());
69 let (cy, sy) = quarter(y).unwrap_or_else(|| (trig::cos::<R, FUSED>(y), trig::sin::<R, FUSED>(y)));
70 let r = c(part(x, cy + cy, half), if y == R::zero() { y } else { part(x, sy + sy, half) });
71 let lost = x.is_infinite() & !y.is_finite();
72 if lost { if x > R::zero() { c(x, R::nan()) } else { c(R::zero(), R::zero().copysign(y)) } } else { r }
73}
74
75#[inline(always)]
77fn scaled<R: Native>(z: Complex<R>) -> (Complex<R>, R) {
78 let (low, scale, p) = if R::WIDE { (1.0020841800044864e-292, 8.112963841460668e31, 53.0) } else { (9.860761315262648e-32, 2.81474976710656e14, 24.0) };
79 if z.re.abs().max(z.im.abs()) < R::narrow(low) { (z * R::narrow(scale), R::narrow(p)) } else { (z, R::zero()) }
80}
81
82#[inline]
84pub(crate) fn ln<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
85 let (x, y) = (z.re, z.im);
86 let (w, p) = scaled(z);
87 let (hi, lo) = ln2::<R>();
88 let h = modulus::<R, FUSED>(w.re, w.im);
89 let near = (h > R::narrow(0.5)) & (h < R::narrow(2.0));
90 let t = (x - R::one()).mla::<FUSED>(x + R::one(), y * y);
91 let k = p + p;
92 c(if near { l::log1p::<R, FUSED>(t) * R::narrow(0.5) } else { l::ln::<R, FUSED>(h) - k * hi - k * lo }, arc::atan2::<R, FUSED>(y, x))
93}
94
95#[inline]
97pub(crate) fn ln1p<R: Native, const FUSED: bool>(w: Complex<R>) -> Complex<R> {
98 let (u, v) = (w.re, w.im);
99 let small = modulus::<R, FUSED>(u, v) < R::narrow(0.5);
100 let t = u.mla::<FUSED>(u + R::narrow(2.0), v * v);
101 let re = if small { l::log1p::<R, FUSED>(t) * R::narrow(0.5) } else { l::ln::<R, FUSED>(modulus::<R, FUSED>(R::one() + u, v)) };
102 c(re, arc::atan2::<R, FUSED>(v, R::one() + u))
103}
104
105#[inline(always)]
107fn root<R: Native, const FUSED: bool>(x: R, y: R, h: R) -> (R, R) {
108 let t = x.abs().mla::<FUSED>(R::narrow(0.5), h * R::narrow(0.5)).sqrt();
109 let q = y / (t + t);
110 if x >= R::zero() { (t, q) } else { (q.abs(), t.copysign(y)) }
111}
112
113#[inline]
115pub(crate) fn sqrt<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
116 let (x, y) = (z.re, z.im);
117 let (w, p) = scaled(z);
118 let (re, im) = root::<R, FUSED>(w.re, w.im, modulus::<R, FUSED>(w.re, w.im));
119 let k = R::pow2(R::SHIFT - p);
120 if y.is_infinite() { c(R::infinity(), y) } else if (x == R::zero()) & (y == R::zero()) { c(R::zero(), y) } else { c(re * k, im * k) }
121}
122
123#[inline(always)]
125fn pow_cases<R: Native>(z: Complex<R>, w: Complex<R>) -> (bool, bool, bool) {
126 let unit = (w.re == R::zero()) & (w.im == R::zero());
127 let zero = (z.re == R::zero()) & (z.im == R::zero()) & (w.re > R::zero());
128 (unit, zero, (z.im == R::zero()) & (w.im == R::zero()) & ((z.re >= R::zero()) | (w.re == w.re.trunc())))
129}
130
131#[inline]
133pub(crate) fn pow<R: Native, const FUSED: bool>(z: Complex<R>, w: Complex<R>) -> Complex<R> {
134 let (unit, zero, real) = pow_cases(z, w);
135 let r = p::pow::<R, FUSED>(z.re, w.re);
136 let general = exp::<R, FUSED>(w * ln::<R, FUSED>(z));
137 if unit { c(R::one(), R::zero()) } else if zero { c(R::zero(), R::zero()) } else if real { c(r, R::zero()) } else { general }
138}
139
140pub(crate) fn nth_root<R: Native, const FUSED: bool>(n: Complex<R>, y: Complex<R>) -> Complex<R> {
141 if n == c(R::zero(), R::zero()) || !finite(n) { return c(R::nan(), R::nan()); }
142 if n == c(R::narrow(2.0), R::zero()) { sqrt::<R, FUSED>(y) } else { pow::<R, FUSED>(y, divide(c(R::one(), R::zero()), n)) }
143}
144
145#[inline]
147pub(crate) fn sinh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
148 let (x, y) = (z.re, z.im);
149 let re = hyperbolic::sinh::<R, FUSED>(x) * trig::cos::<R, FUSED>(y);
150 let im = hyperbolic::cosh::<R, FUSED>(x) * trig::sin::<R, FUSED>(y);
151 c(if x == R::zero() { x } else { re }, if y == R::zero() { y } else { im })
152}
153
154#[inline]
156pub(crate) fn cosh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
157 let (x, y) = (z.re, z.im);
158 let re = hyperbolic::cosh::<R, FUSED>(x) * trig::cos::<R, FUSED>(y);
159 let im = hyperbolic::sinh::<R, FUSED>(x) * trig::sin::<R, FUSED>(y);
160 c(re, if (x == R::zero()) | (y == R::zero()) { R::zero().copysign(x) * R::zero().copysign(y) } else { im })
161}
162
163#[inline]
165pub(crate) fn tanh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
166 let (x, y) = (z.re, z.im);
167 let (sy, cy) = (trig::sin::<R, FUSED>(y), trig::cos::<R, FUSED>(y));
168 let t = sy / cy;
169 let b = t.mla::<FUSED>(t, R::one());
170 let s = hyperbolic::sinh::<R, FUSED>(x);
171 let rho = s.mla::<FUSED>(s, R::one()).sqrt();
172 let d = (b * s).mla::<FUSED>(s, R::one());
173 let near = c(b * s * rho / d, t / d);
174 let far = c(R::one().copysign(x), e::exp_times::<R, FUSED>(R::narrow(-2.0) * x.abs(), R::narrow(2.0)) * (sy * cy));
175 let r = if x.abs() > tanh_limit::<R>() { far } else { near };
176 if x.is_infinite() & !y.is_finite() { c(r.re, R::zero().copysign(y)) } else { r }
177}
178
179fn tanh_limit<R: Native>() -> R { R::narrow(if R::WIDE { 22.0 } else { 10.0 }) }
181
182#[inline]
184pub(crate) fn sin<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { unturned(sinh::<R, FUSED>(turned(z))) }
185
186#[inline]
188pub(crate) fn cos<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { cosh::<R, FUSED>(turned(z)) }
189
190#[inline]
192pub(crate) fn tan<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { unturned(tanh::<R, FUSED>(turned(z))) }
193
194#[inline(always)]
196fn roots<R: Native, const FUSED: bool>(z: Complex<R>) -> (Complex<R>, Complex<R>) {
197 (sqrt::<R, FUSED>(c(R::one() - z.re, -z.im)), sqrt::<R, FUSED>(c(R::one() + z.re, z.im)))
198}
199
200#[inline(always)]
202fn shifted_roots<R: Native, const FUSED: bool>(z: Complex<R>) -> (Complex<R>, Complex<R>) {
203 (sqrt::<R, FUSED>(c(z.re - R::one(), z.im)), sqrt::<R, FUSED>(c(z.re + R::one(), z.im)))
204}
205
206#[inline(always)]
208fn balanced<R: Native>(r: Complex<R>, x: R, y: R) -> Complex<R> {
209 let zero = R::zero();
210 let swap = x.abs() < y.abs();
211 let (hi, lo) = (if swap { y } else { x }, if swap { x } else { y });
212 let wide = r.re.abs() >= r.im.abs();
213 let big = if wide { r.re } else { r.im };
214 let keep = (x == zero) | (y == zero) | (big == zero) | !(r.re.is_finite() & r.im.is_finite() & x.is_finite() & y.is_finite());
215 let small = if keep { if wide { r.im } else { r.re } } else { hi / big * lo };
216 if wide { c(r.re, small) } else { c(small, r.im) }
217}
218
219#[inline]
221pub(crate) fn sqrt1mx2<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { let (a, b) = roots::<R, FUSED>(z); balanced(a * b, -z.re, z.im) }
222
223#[inline]
225pub(crate) fn sqrt1px2<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { sqrt1mx2::<R, FUSED>(turned(z)) }
226
227#[inline]
229pub(crate) fn sqrtx2m1<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { let (a, b) = shifted_roots::<R, FUSED>(z); balanced(a * b, z.re, z.im) }
230
231pub(crate) fn sqrtm1mx2<R: Native, const FUSED: bool>(z: Complex<R>, inverse: bool) -> Complex<R> {
232 let r = if z.im == R::zero() { c(R::zero(), crate::sqrts::sqrt1px2::<R, FUSED>(z.re)) } else { sqrt::<R, FUSED>(-c(R::one(), R::zero()) - z * z) };
233 if inverse { -r } else { r }
234}
235
236#[inline]
238pub(crate) fn asin<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
239 let (a, b) = roots::<R, FUSED>(z);
240 let re = arc::atan2::<R, FUSED>(z.re, a.re.mla::<FUSED>(b.re, -(a.im * b.im)));
241 c(re, hyperbolic::asinh::<R, FUSED>(a.re.mla::<FUSED>(b.im, -(a.im * b.re))))
242}
243
244#[inline]
246pub(crate) fn acos<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
247 let (a, b) = roots::<R, FUSED>(z);
248 let re = arc::atan2::<R, FUSED>(a.re, b.re);
249 c(re + re, hyperbolic::asinh::<R, FUSED>(b.re.mla::<FUSED>(a.im, -(b.im * a.re))))
250}
251
252#[inline]
254pub(crate) fn acosh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
255 let (a, b) = shifted_roots::<R, FUSED>(z);
256 let im = arc::atan2::<R, FUSED>(a.im, b.re);
257 c(hyperbolic::asinh::<R, FUSED>(a.re.mla::<FUSED>(b.re, a.im * b.im)), im + im)
258}
259
260#[inline]
262pub(crate) fn asinh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { unturned(asin::<R, FUSED>(turned(z))) }
263
264#[inline]
266pub(crate) fn atanh<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
267 let s = R::one().copysign(z.re);
268 let (x, y) = (z.re.abs(), z.im * s);
269 let (m, quarter, half) = (R::one() - x, R::narrow(0.25), R::narrow(0.5));
270 let re = l::log1p::<R, FUSED>(R::narrow(4.0) * x / m.mla::<FUSED>(m, y * y)) * quarter;
271 let im = arc::atan2::<R, FUSED>(y + y, m.mla::<FUSED>(R::one() + x, -(y * y))) * half;
272 let h = modulus::<R, FUSED>(x, y);
273 let (right, _) = arc::half_pi::<R>();
274 let huge = h > atanh_huge::<R>();
275 let (re, im) = if x.is_infinite() | y.is_infinite() { (R::zero(), right.copysign(y)) } else if huge { (x / h / h, right.copysign(y)) } else { (re, im) };
276 c(re * s, im * s)
277}
278
279fn atanh_huge<R: Native>() -> R { R::narrow(if R::WIDE { 1e150 } else { 1e18 }) }
281
282#[inline]
284pub(crate) fn atan<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { unturned(atanh::<R, FUSED>(turned(z))) }
285
286#[inline(always)]
288fn pole<R: Native>(z: Complex<R>) -> bool { (z.im == R::zero()) & (z.re <= R::zero()) & (z.re == z.re.trunc()) }
289
290#[inline]
292fn stirling<R: Native, const FUSED: bool>(w: Complex<R>) -> Complex<R> {
293 let n = w.re.mla::<FUSED>(w.re, w.im * w.im);
294 let v = c(w.re / n, -w.im / n);
295 let v2 = v * v;
296 let mut s = c(R::narrow(special::STIRLING[0]), R::zero());
297 for &k in &special::STIRLING[1..] { s = s * v2 + c(R::narrow(k), R::zero()); }
298 c(w.re - R::narrow(0.5), w.im) * ln::<R, FUSED>(w) - w + c(R::narrow(special::HALF_LN_2PI), R::zero()) + v * s
299}
300
301#[inline]
303fn shifted<R: Native, const FUSED: bool>(w: Complex<R>) -> Complex<R> {
304 let below = w.im.is_sign_negative();
305 let mut z = if below { w.conj() } else { w };
306 let seven = R::narrow(7.0);
307 let r = if (z.re >= seven) | (z.im >= seven) { stirling::<R, FUSED>(z) } else {
308 let (mut product, mut turns, mut negative) = (z, R::zero(), false);
309 z.re = z.re + R::one();
310 while z.re < seven {
311 product = product * z;
312 let now = product.im.is_sign_negative();
313 if now & !negative { turns = turns + R::one(); }
314 negative = now;
315 z.re = z.re + R::one();
316 }
317 stirling::<R, FUSED>(z) - ln::<R, FUSED>(product) - c(R::zero(), (pi::<R>() + pi::<R>()) * turns)
318 };
319 if below { r.conj() } else { r }
320}
321
322#[inline]
324fn reflected<R: Native, const FUSED: bool>(u: Complex<R>) -> Complex<R> {
325 let (x, y) = (u.re, u.im);
326 let (s, k) = crate::recipe::sincospi(&crate::recipe::Floats, x + x);
327 let m = e::exp::<R, FUSED>(R::narrow(-2.0) * pi::<R>() * y);
328 let t = ln1p::<R, FUSED>(c(-(m * k), -(m * s)));
329 c((R::narrow(1.8378770664093453) - pi::<R>() * y) - t.re, pi::<R>() * (x - R::narrow(0.5)) - t.im)
330}
331
332#[inline]
334pub(crate) fn lgamma<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
335 if !(z.re.is_finite() & z.im.is_finite()) | pole(z) { return c(R::nan(), R::nan()); }
336 if z.re >= R::narrow(0.1) { return shifted::<R, FUSED>(z); }
337 let below = z.im.is_sign_negative();
338 let u = if below { z.conj() } else { z };
339 let r = reflected::<R, FUSED>(u) - shifted::<R, FUSED>(c(R::one() - u.re, -u.im));
340 if below { r.conj() } else { r }
341}
342
343#[inline]
345pub(crate) fn gamma<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { exp::<R, FUSED>(lgamma::<R, FUSED>(z)) }
346#[inline]
348pub(crate) fn factorial<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> { gamma::<R, FUSED>(c(z.re + R::one(), z.im)) }
349
350#[inline]
352pub(crate) fn binom<R: Native, const FUSED: bool>(n: Complex<R>, k: Complex<R>) -> Complex<R> {
353 let one = c(R::one(), R::zero());
354 let (a, b, d) = (n + one, k + one, n - k + one);
355 let r = exp::<R, FUSED>(lgamma::<R, FUSED>(a) - lgamma::<R, FUSED>(b) - lgamma::<R, FUSED>(d));
356 if !pole(a) & (pole(b) | pole(d)) { c(R::zero(), R::zero()) } else { r }
357}
358
359#[inline]
361pub(crate) fn sign<R: Native, const FUSED: bool>(z: Complex<R>) -> Complex<R> {
362 let unit = |v: R| (if v.is_infinite() { R::one() } else { R::zero() }).copysign(v);
363 let w = if z.re.is_infinite() | z.im.is_infinite() { c(unit(z.re), unit(z.im)) } else { z };
364 let h = modulus::<R, FUSED>(w.re, w.im);
365 if h == R::zero() { z } else { c(w.re / h, w.im / h) }
366}
367
368#[inline(always)]
370fn finite<R: Native>(z: Complex<R>) -> bool { z.re.is_finite() & z.im.is_finite() }
371
372#[derive(Clone, Copy, Debug)]
374pub struct Cis;