#[cfg(feature = "alloc")]
use alloc::vec;
use num_complex::Complex;
use crate::algo::constants::HPI;
use crate::besi::zbesi;
use crate::besk::zbesk;
use crate::machine::BesselFloat;
use crate::types::{Accuracy, Error, Scaling};
use crate::utils::{mul_i, mul_neg_i};
#[inline]
pub(crate) fn zbesy<T: BesselFloat>(
z: Complex<T>,
fnu: T,
scaling: Scaling,
y: &mut [Complex<T>],
) -> Result<(usize, Accuracy), Error> {
let n = y.len();
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
let hpi_t = T::from_f64(HPI);
y.fill(czero);
if n < 1 {
return Err(Error::InvalidInput);
}
if fnu < zero {
return Err(Error::InvalidInput);
}
if z == czero {
return Err(Error::InvalidInput);
}
let zn = Complex::new(z.im.abs(), -z.re);
let ifnu = fnu.to_i32().unwrap();
let ffnu = fnu - T::from_f64(ifnu as f64);
let arg = hpi_t * ffnu;
let mut csgn = Complex::new(arg.cos(), arg.sin());
csgn = match (ifnu % 4) as usize {
0 => csgn,
1 => mul_i(csgn),
2 => -csgn,
_ => mul_neg_i(csgn),
};
let rhpi = one / hpi_t;
let mut cspn = csgn.conj() * rhpi;
csgn = mul_i(csgn);
if n == 1 {
let mut i_buf = [czero];
let mut k_buf = [czero];
let (nz1, _) = zbesi(zn, fnu, scaling, &mut i_buf)?;
let (nz2, _) = zbesk(zn, fnu, scaling, &mut k_buf)?;
let nz = nz1.min(nz2);
if scaling == Scaling::Unscaled {
let cy_val = csgn * i_buf[0] - cspn * k_buf[0];
y[0] = if z.im < zero { cy_val.conj() } else { cy_val };
return Ok((nz, Accuracy::Normal));
}
let tol = T::tol();
let elim = T::elim();
let exr = z.re.cos();
let exi = z.re.sin();
let mut ey = zero;
let tay = (z.im + z.im).abs();
if tay < elim {
ey = (-tay).exp();
}
cspn = Complex::new(exr, exi) * cspn * ey;
let rtol = one / tol;
let ascle = T::MACH_TINY * rtol * T::from_f64(1.0e3);
let (zv_s, zv_atol) = if k_buf[0].re.abs().max(k_buf[0].im.abs()) <= ascle {
(k_buf[0] * rtol, tol)
} else {
(k_buf[0], one)
};
let zv = zv_s * cspn * zv_atol;
let (zu_s, zu_atol) = if i_buf[0].re.abs().max(i_buf[0].im.abs()) <= ascle {
(i_buf[0] * rtol, tol)
} else {
(i_buf[0], one)
};
let zu = zu_s * csgn * zu_atol;
let cy_val = zu - zv;
y[0] = if z.im < zero { cy_val.conj() } else { cy_val };
let nz_out = if cy_val == czero && ey == zero { 1 } else { 0 };
return Ok((nz_out, Accuracy::Normal));
}
#[cfg(feature = "alloc")]
{
let mut i_buf = vec![czero; n];
let mut k_buf = vec![czero; n];
let (nz1, _) = zbesi(zn, fnu, scaling, &mut i_buf)?;
let (nz2, _) = zbesk(zn, fnu, scaling, &mut k_buf)?;
let nz = nz1.min(nz2);
if scaling == Scaling::Unscaled {
for i in 0..n {
let cy_val = csgn * i_buf[i] - cspn * k_buf[i];
y[i] = cy_val;
csgn = mul_i(csgn);
cspn = mul_neg_i(cspn);
}
if z.im < zero {
for yi in y.iter_mut().take(n) {
*yi = yi.conj();
}
}
return Ok((nz, Accuracy::Normal));
}
let tol = T::tol();
let elim = T::elim();
let exr = z.re.cos();
let exi = z.re.sin();
let mut ey = zero;
let tay = (z.im + z.im).abs();
if tay < elim {
ey = (-tay).exp();
}
cspn = Complex::new(exr, exi) * cspn * ey;
let mut nz_out: usize = 0;
let rtol = one / tol;
let ascle = T::MACH_TINY * rtol * T::from_f64(1.0e3);
for i in 0..n {
let (zv_s, zv_atol) = if k_buf[i].re.abs().max(k_buf[i].im.abs()) <= ascle {
(k_buf[i] * rtol, tol)
} else {
(k_buf[i], one)
};
let zv = zv_s * cspn * zv_atol;
let (zu_s, zu_atol) = if i_buf[i].re.abs().max(i_buf[i].im.abs()) <= ascle {
(i_buf[i] * rtol, tol)
} else {
(i_buf[i], one)
};
let zu = zu_s * csgn * zu_atol;
let cy_val = zu - zv;
y[i] = if z.im < zero { cy_val.conj() } else { cy_val };
if cy_val == czero && ey == zero {
nz_out += 1;
}
csgn = mul_i(csgn);
cspn = mul_neg_i(cspn);
}
Ok((nz_out, Accuracy::Normal))
}
#[cfg(not(feature = "alloc"))]
{
Err(Error::InvalidInput)
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_complex::Complex64;
#[test]
fn besy_z_zero_returns_error() {
let z = Complex64::new(0.0, 0.0);
let mut y = [Complex64::new(0.0, 0.0)];
assert!(zbesy(z, 0.0, Scaling::Unscaled, &mut y).is_err());
}
}