#![allow(clippy::too_many_arguments)]
use num_complex::Complex;
use crate::algo::asyi::zasyi;
use crate::algo::buni::zbuni;
use crate::algo::mlri::zmlri;
use crate::algo::seri::zseri;
use crate::algo::uoik::zuoik;
use crate::algo::wrsk::zwrsk;
use crate::machine::BesselFloat;
use crate::types::{Error, IkFlag, Scaling};
use crate::utils::zabs;
pub(crate) fn zbinu<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
cy: &mut [Complex<T>],
rl: T,
fnul: T,
tol: T,
elim: T,
alim: T,
) -> Result<usize, Error> {
let zero = T::zero();
let one = T::one();
let two = T::from_f64(2.0);
let czero = Complex::new(zero, zero);
let n = cy.len();
cy.fill(czero);
let mut nz: usize = 0;
let az = zabs(z);
let mut nn = n;
let mut dfnu = fnu + T::from_f64((n - 1) as f64);
if az <= two || az * az * T::from_f64(0.25) <= dfnu + one {
let nw = zseri(z, fnu, kode, &mut cy[..nn], tol, elim, alim);
let inw = nw.unsigned_abs() as usize;
nz += inw;
nn -= inw;
if nn == 0 {
return Ok(nz);
}
if nw >= 0 {
return Ok(nz);
}
dfnu = fnu + T::from_f64((nn - 1) as f64);
}
dispatch_20(
z, fnu, kode, &mut nn, &mut nz, az, &mut dfnu, rl, fnul, tol, elim, alim, cy,
)
}
#[inline]
fn dispatch_20<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
nn: &mut usize,
nz: &mut usize,
az: T,
dfnu: &mut T,
rl: T,
fnul: T,
tol: T,
elim: T,
alim: T,
cy: &mut [Complex<T>],
) -> Result<usize, Error> {
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
if az >= rl {
if *dfnu <= one || az + az >= *dfnu * *dfnu {
let nw = zasyi(z, fnu, kode, &mut cy[..*nn], rl, tol, elim, alim);
if nw < 0 {
return handle_error(nw);
}
return Ok(*nz);
}
} else if *dfnu <= one {
let nw = zmlri(z, fnu, kode, &mut cy[..*nn], tol);
if nw < 0 {
return handle_error(nw);
}
return Ok(*nz);
}
let nw = zuoik(z, fnu, kode, IkFlag::I, &mut cy[..*nn], tol, elim, alim);
if nw < 0 {
return handle_error(nw);
}
*nz += nw as usize;
*nn -= nw as usize;
if *nn == 0 {
return Ok(*nz);
}
*dfnu = fnu + T::from_f64((*nn - 1) as f64);
if *dfnu > fnul || az > fnul {
let nui_f = (fnul - *dfnu).to_f64().unwrap() as i32 + 1;
let nui = nui_f.max(0) as usize;
let result = zbuni(z, fnu, kode, &mut cy[..*nn], nui, fnul, tol, elim, alim);
if result.nz < 0 {
return handle_error(result.nz);
}
*nz += result.nz as usize;
if result.nlast == 0 {
return Ok(*nz);
}
*nn = result.nlast;
}
if az > rl {
let mut cw_buf = [czero; 2];
let nw = zuoik(z, fnu, kode, IkFlag::K, &mut cw_buf, tol, elim, alim);
if nw < 0 {
*nz = *nn;
for cy_item in cy.iter_mut().take(*nn) {
*cy_item = czero;
}
return Ok(*nz);
}
if nw > 0 {
return handle_error(-1);
}
zwrsk(z, fnu, kode, &mut cy[..*nn], tol, elim, alim)?;
return Ok(*nz);
}
let nw = zmlri(z, fnu, kode, &mut cy[..*nn], tol);
if nw < 0 {
return handle_error(nw);
}
Ok(*nz)
}
#[inline]
fn handle_error<T>(nw: i32) -> Result<T, Error> {
if nw == -2 {
Err(Error::ConvergenceFailure)
} else {
Err(Error::Overflow)
}
}