#![allow(clippy::excessive_precision)]
#![allow(clippy::approx_constant)]
#![allow(clippy::too_many_arguments)]
use num_complex::Complex;
use crate::algo::constants::PI;
use crate::machine::BesselFloat;
use crate::types::Scaling;
use crate::utils::{mul_add, mul_add_scalar, reciprocal_z, zabs, zdiv};
pub(crate) fn zasyi<T: BesselFloat>(
z: Complex<T>,
fnu: T,
kode: Scaling,
y: &mut [Complex<T>],
rl: T,
tol: T,
elim: T,
alim: T,
) -> i32 {
let zero = T::zero();
let one = T::one();
let eight = T::from_f64(8.0);
let czero = Complex::new(zero, zero);
let pi = T::from_f64(PI);
let rtpi = T::from_f64(0.159154943091895336);
let n = y.len();
let nz: i32 = 0;
let az = zabs(z);
let arm = T::from_f64(1.0e3) * T::MACH_TINY;
let rtr1 = arm.sqrt();
let il = if n < 2 { n } else { 2 };
let dfnu0 = fnu + T::from_f64((n - il) as f64);
let raz = one / az;
let mut ak1 = (z.conj() * (rtpi * raz * raz)).sqrt();
let cz = if kode == Scaling::Exponential {
Complex::new(zero, z.im)
} else {
z
};
if cz.re.abs() > elim {
return -1;
}
let dnu2 = dfnu0 + dfnu0;
let deferred_exp = cz.re.abs() > alim && n > 2;
if !deferred_exp {
ak1 = ak1 * cz.exp();
}
let mut fdn = if dnu2 > rtr1 { dnu2 * dnu2 } else { zero };
let ezr = z.re * eight;
let ezi = z.im * eight;
let aez = eight * az;
let s = tol / aez;
let jl = (rl + rl).to_i32().unwrap() + 2;
let mut p1 = czero;
if z.im != zero {
let inu = fnu.to_i32().unwrap();
let arg = (fnu - T::from_f64(inu as f64)) * pi;
let inu_adj = inu + n as i32 - il as i32;
let ak_sign = -arg.sin();
let mut bk_sign = arg.cos();
if z.im < zero {
bk_sign = -bk_sign;
}
p1 = Complex::new(ak_sign, bk_sign);
if inu_adj % 2 != 0 {
p1 = -p1;
}
}
let mut dfnu_val = dfnu0;
for k in 0..il {
let sqk0 = fdn - one;
let atol = s * sqk0.abs();
let mut sgn = one;
let mut cs1 = Complex::from(one);
let mut cs2 = Complex::from(one);
let mut ck = Complex::from(one);
let mut ak_val = zero;
let mut aa = one;
let mut bb = aez;
let mut dk = Complex::new(ezr, ezi);
let mut sqk = sqk0;
let mut converged = false;
for _j in 1..=jl {
ck = zdiv(ck, dk) * sqk;
cs2 = cs2 + ck;
sgn = -sgn;
cs1 = cs1 + ck * sgn;
dk = dk + Complex::new(ezr, ezi);
aa = aa * sqk.abs() / bb;
bb = bb + aez;
ak_val = ak_val + eight;
sqk = sqk - ak_val;
if aa <= atol {
converged = true;
break;
}
}
if !converged {
return -2;
}
let mut s2 = cs1;
if z.re + z.re < elim {
let exp_2z = (-(z + z)).exp();
s2 = mul_add(exp_2z * p1, cs2, s2);
}
fdn = fdn + eight * dfnu_val + T::from_f64(4.0);
p1 = -p1;
let m = n - il + k;
y[m] = s2 * ak1;
if k == 0 {
dfnu_val = dfnu_val + one; }
}
if n <= 2 {
return nz;
}
let nn = n;
let mut k_idx: isize = nn as isize - 3;
let mut ak_rec = T::from_f64((nn - 2) as f64);
let rz = reciprocal_z(z);
for _ in 2..nn {
let ki = k_idx as usize;
y[ki] = mul_add_scalar(rz * y[ki + 1], ak_rec + fnu, y[ki + 2]);
ak_rec = ak_rec - one;
k_idx -= 1;
}
if deferred_exp {
let cz_exp = cz.exp();
for y_item in y.iter_mut().take(nn) {
*y_item = *y_item * cz_exp;
}
}
nz
}