use num_complex::Complex;
use crate::algo::uchk::zuchk;
use crate::machine::BesselFloat;
use crate::utils::{mul_add, zabs};
pub(crate) fn zkscl<T: BesselFloat>(
zr: Complex<T>,
fnu: T,
y: &mut [Complex<T>],
rz: Complex<T>,
ascle: T,
tol: T,
elim: T,
) -> usize {
let zero = T::zero();
let one = T::one();
let czero = Complex::new(zero, zero);
let n = y.len();
if n == 0 {
return 0;
}
let mut nz: usize = 0;
let mut ic: usize = 0;
let nn = n.min(2);
let mut cy = [czero; 2];
for i in 0..nn {
let s1 = y[i];
cy[i] = s1;
let az = zabs(s1);
let acs = -zr.re + az.ln();
nz += 1;
y[i] = czero;
if acs >= -elim {
let cs = s1.ln() - zr;
let cs_scaled = cs.exp() / tol;
if !zuchk(cs_scaled, ascle, tol) {
y[i] = cs_scaled;
ic = i + 1; nz -= 1;
}
}
}
if n == 1 {
return nz;
}
if ic <= 1 {
y[0] = czero;
nz = 2;
}
if n == 2 || nz == 0 {
return nz;
}
let fn_val = fnu + one;
let mut ck = rz * fn_val;
let mut s1 = cy[0];
let mut s2 = cy[1];
let helim = T::from_f64(0.5) * elim;
let celmr = (-elim).exp();
let mut zd = zr;
let mut found_two = false;
let mut kk_found = 0_usize;
#[allow(clippy::needless_range_loop)]
for i in 2..n {
let kk = i + 1;
let cs = s2;
s2 = mul_add(ck, cs, s1);
s1 = cs;
ck = ck + rz;
let az = zabs(s2);
let alas = az.ln();
let acs = -zd.re + alas;
y[i] = czero;
if acs >= -elim {
let cs_log = s2.ln() - zd;
let cs_scaled = cs_log.exp() / tol;
if !zuchk(cs_scaled, ascle, tol) {
y[i] = cs_scaled;
if ic == kk - 1 {
found_two = true;
kk_found = kk;
break;
}
ic = kk;
continue;
}
}
if alas >= helim {
zd = zd - elim;
s1 = s1 * celmr;
s2 = s2 * celmr;
}
}
if found_two {
nz = kk_found - 2;
} else {
nz = n;
if ic == n {
nz = n - 1;
}
}
for val in y.iter_mut().take(nz) {
*val = czero;
}
nz
}
#[cfg(test)]
mod tests {
use super::*;
use num_complex::Complex64;
#[test]
fn kscl_single_element_large() {
let zr = Complex64::new(1.0, 0.0);
let rz = Complex64::new(2.0, 0.0);
let mut y = [Complex64::new(1e100, 0.0)];
let nz = zkscl(zr, 0.5, &mut y, rz, 1e-300, 1e-16, 700.0);
assert_eq!(nz, 0);
assert!(y[0] != Complex64::new(0.0, 0.0));
}
#[test]
fn kscl_all_underflow() {
let zr = Complex64::new(700.0, 0.0);
let rz = Complex64::new(2.0 / 700.0, 0.0);
let mut y = [Complex64::new(1e-100, 0.0), Complex64::new(1e-100, 0.0)];
let nz = zkscl(zr, 0.5, &mut y, rz, 1e-290, 1e-16, 700.0);
assert_eq!(nz, 2);
assert_eq!(y[0], Complex64::new(0.0, 0.0));
assert_eq!(y[1], Complex64::new(0.0, 0.0));
}
#[test]
fn kscl_empty() {
let zr = Complex64::new(1.0, 0.0);
let rz = Complex64::new(2.0, 0.0);
let mut y: [Complex64; 0] = [];
let nz = zkscl(zr, 0.5, &mut y, rz, 1e-300, 1e-16, 700.0);
assert_eq!(nz, 0);
}
}