#![allow(clippy::too_many_arguments)]
#![allow(clippy::excessive_precision)]
use num_complex::Complex;
use crate::algo::constants::AIC;
use crate::algo::uchk::zuchk;
use crate::algo::unhj::zunhj;
use crate::algo::unik::zunik;
use crate::machine::BesselFloat;
use crate::types::{IkFlag, Scaling, SumOption};
use crate::utils::zabs;
pub(crate) fn zuoik<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
ikflg: IkFlag,
y: &mut [Complex<T>],
tol: T,
elim: T,
alim: T,
) -> i32 {
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
let n = y.len();
y.fill(czero);
let mut nuf: i32 = 0;
let mut nn = n;
let aic = T::from_f64(AIC);
let zr = if z.re >= zero { z } else { -z };
let ax = z.re.abs() * T::from_f64(1.7321);
let ay = z.im.abs();
let iform: i32 = if ay > ax { 2 } else { 1 };
let mut gnu = fnu.max(one);
if ikflg == IkFlag::K {
let fnn = T::from_f64(nn as f64);
let gnn = fnu + fnn - one;
gnu = gnn.max(fnn);
}
let zn = if iform == 2 {
let znr = if z.im <= zero { -zr.im } else { zr.im };
Complex::new(znr, -zr.re)
} else {
czero };
let (mut cz, phi, aarg) = if iform == 1 {
let result = zunik(zr, gnu, ikflg, SumOption::SkipSum, tol, None);
(result.zeta2 - result.zeta1, result.phi, zero)
} else {
let result = zunhj(zn, gnu, SumOption::SkipSum, tol);
(result.zeta2 - result.zeta1, result.phi, zabs(result.arg))
};
if kode == Scaling::Exponential {
cz = cz - zr;
}
if ikflg == IkFlag::K {
cz = -cz;
}
let aphi = zabs(phi);
let mut rcz = cz.re;
if rcz > elim {
return -1; }
if rcz >= alim {
rcz = rcz + aphi.ln();
if iform == 2 {
rcz = rcz - T::from_f64(0.25) * aarg.ln() - aic;
}
if rcz > elim {
return -1; }
} else {
if rcz < -elim {
y[..nn].fill(czero);
return nn as i32;
}
if rcz <= -alim {
rcz = rcz + aphi.ln();
if iform == 2 {
rcz = rcz - T::from_f64(0.25) * aarg.ln() - aic;
}
if rcz <= -elim {
y[..nn].fill(czero);
return nn as i32;
}
let ascle = T::from_f64(1.0e3) * T::MACH_TINY / tol;
let log_phi = phi.ln();
cz.im = cz.im + log_phi.im;
if iform == 2 {
let log_arg = Complex::new(aarg, zero).ln();
cz.im = cz.im - T::from_f64(0.25) * log_arg.im;
}
let cz_check = Complex::new(rcz, cz.im).exp() / tol;
if zuchk(cz_check, ascle, tol) {
y[..nn].fill(czero);
return nn as i32;
}
}
}
if ikflg == IkFlag::K {
return nuf;
}
if n == 1 {
return nuf;
}
loop {
gnu = fnu + T::from_f64((nn - 1) as f64);
let (phi2, aarg2);
if iform == 1 {
let result2 = zunik(zr, gnu, ikflg, SumOption::SkipSum, tol, None);
cz = result2.zeta2 - result2.zeta1;
phi2 = result2.phi;
aarg2 = zero;
} else {
let result2 = zunhj(zn, gnu, SumOption::SkipSum, tol);
cz = result2.zeta2 - result2.zeta1;
phi2 = result2.phi;
aarg2 = zabs(result2.arg);
}
if kode == Scaling::Exponential {
cz = cz - zr;
}
let aphi2 = zabs(phi2);
rcz = cz.re;
if rcz < -elim {
y[nn - 1] = czero;
nn -= 1;
nuf += 1;
if nn == 0 {
return nuf;
}
continue;
}
if rcz > -alim {
return nuf;
}
rcz = rcz + aphi2.ln();
if iform == 2 {
rcz = rcz - T::from_f64(0.25) * aarg2.ln() - aic;
}
if rcz <= -elim {
y[nn - 1] = czero;
nn -= 1;
nuf += 1;
if nn == 0 {
return nuf;
}
continue;
}
let ascle = T::from_f64(1.0e3) * T::MACH_TINY / tol;
let log_phi2 = phi2.ln();
cz.im = cz.im + log_phi2.im;
if iform == 2 {
let log_arg2 = Complex::new(aarg2, zero).ln();
cz.im = cz.im - T::from_f64(0.25) * log_arg2.im;
}
let cz_check = Complex::new(rcz, cz.im).exp() / tol;
if zuchk(cz_check, ascle, tol) {
y[nn - 1] = czero;
nn -= 1;
nuf += 1;
if nn == 0 {
return nuf;
}
continue;
}
return nuf;
}
}