#![allow(clippy::excessive_precision)]
#![allow(clippy::approx_constant)]
#![allow(clippy::too_many_arguments)]
use num_complex::Complex;
use crate::algo::asyi::zasyi;
use crate::algo::bknu::zbknu;
use crate::algo::constants::PI;
use crate::algo::mlri::zmlri;
use crate::algo::s1s2::zs1s2;
use crate::algo::seri::zseri;
use crate::machine::BesselFloat;
use crate::types::{Error, Scaling};
use crate::utils::{mul_add, zabs};
pub(crate) fn zacai<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
mr: i32,
y: &mut [Complex<T>],
rl: T,
tol: T,
elim: T,
alim: T,
) -> Result<i32, Error> {
let zero = T::zero();
let one = T::one();
let two = T::from_f64(2.0);
let pi_t = T::from_f64(PI);
let czero = Complex::new(zero, zero);
let mut nz: i32 = 0;
let n = y.len();
y.fill(czero);
let zn = -z;
let az = zabs(z);
let dfnu = fnu + T::from_f64((n - 1) as f64);
let mut i_buf = [czero];
if az <= two || az * az * T::from_f64(0.25) <= dfnu + one {
zseri(zn, fnu, kode, &mut i_buf, tol, elim, alim);
} else if az >= rl {
let nw = zasyi(zn, fnu, kode, &mut i_buf, rl, tol, elim, alim);
if nw < 0 {
return Err(if nw == -2 {
Error::ConvergenceFailure
} else {
Error::Overflow
});
}
} else {
let nw = zmlri(zn, fnu, kode, &mut i_buf, tol);
if nw < 0 {
return Err(if nw == -2 {
Error::ConvergenceFailure
} else {
Error::Overflow
});
}
}
let mut k_buf = [czero];
let nw_k = zbknu(zn, fnu, kode, &mut k_buf, tol, elim, alim)?;
if nw_k != 0 {
return Err(Error::Overflow);
}
let fmr = T::from_f64(mr as f64);
let sgn = -pi_t.copysign(fmr);
let mut csgn = Complex::new(zero, sgn);
if kode == Scaling::Exponential {
let yy = -zn.im;
csgn = csgn * Complex::new(yy.cos(), yy.sin());
}
let inu = fnu.to_i32().unwrap();
let arg = (fnu - T::from_f64(inu as f64)) * sgn;
let mut cspn = Complex::new(arg.cos(), arg.sin());
if inu % 2 != 0 {
cspn = -cspn;
}
let mut c1 = k_buf[0];
let mut c2 = i_buf[0];
if kode == Scaling::Exponential {
let iuf: i32 = 0;
let ascle = T::from_f64(1.0e3) * T::MACH_TINY / tol;
let s1s2_out = zs1s2(zn, c1, c2, ascle, alim, iuf);
nz += s1s2_out.nz;
c1 = s1s2_out.s1;
c2 = s1s2_out.s2;
}
y[0] = mul_add(cspn, c1, csgn * c2);
Ok(nz)
}