use num_complex::Complex;
use crate::machine::BesselFloat;
use crate::utils::zabs;
#[derive(Debug, Clone, Copy)]
pub(crate) struct S1S2Output<T> {
pub s1: Complex<T>,
pub s2: Complex<T>,
pub nz: i32,
pub iuf: i32,
}
pub(crate) fn zs1s2<T: BesselFloat>(
zr: Complex<T>,
s1: Complex<T>,
s2: Complex<T>,
ascle: T,
alim: T,
iuf: i32,
) -> S1S2Output<T> {
let zero = T::zero();
let czero = Complex::new(zero, zero);
let mut s1_out = s1;
let mut iuf_out = iuf;
let mut as1 = zabs(s1);
let as2 = zabs(s2);
if s1 != czero && as1 != zero {
let aln = -zr.re - zr.re + as1.ln();
let s1d = s1; s1_out = czero;
as1 = zero;
if aln >= -alim {
let c1 = s1d.ln() - (zr + zr);
s1_out = c1.exp();
as1 = zabs(s1_out);
iuf_out += 1;
}
}
let aa = as1.max(as2);
let s2_out = if aa > ascle {
s2
} else {
s1_out = czero;
return S1S2Output {
s1: s1_out,
s2: czero,
nz: 1,
iuf: 0,
};
};
S1S2Output {
s1: s1_out,
s2: s2_out,
nz: 0,
iuf: iuf_out,
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_complex::Complex64;
#[test]
fn s1s2_both_zero_input() {
let zr = Complex64::new(1.0, 0.5);
let s1 = Complex64::new(0.0, 0.0);
let s2 = Complex64::new(0.0, 0.0);
let result = zs1s2(zr, s1, s2, 1e-300, 700.0, 0);
assert_eq!(result.nz, 1);
assert_eq!(result.s1, Complex64::new(0.0, 0.0));
assert_eq!(result.s2, Complex64::new(0.0, 0.0));
}
#[test]
fn s1s2_large_values_no_underflow() {
let zr = Complex64::new(0.1, 0.0);
let s1 = Complex64::new(10.0, 5.0);
let s2 = Complex64::new(20.0, 10.0);
let result = zs1s2(zr, s1, s2, 1e-300, 700.0, 0);
assert_eq!(result.nz, 0);
assert_eq!(result.iuf, 1); }
#[test]
fn s1s2_s1_underflows() {
let zr = Complex64::new(500.0, 0.0);
let s1 = Complex64::new(1e-10, 0.0);
let s2 = Complex64::new(1.0, 0.0);
let result = zs1s2(zr, s1, s2, 1e-300, 700.0, 0);
assert_eq!(result.s1, Complex64::new(0.0, 0.0));
assert_eq!(result.nz, 0);
assert_eq!(result.iuf, 0); }
#[test]
fn s1s2_rescale_s1() {
let zr = Complex64::new(0.5, 0.0);
let s1 = Complex64::new(1.0, 0.0);
let s2 = Complex64::new(1.0, 0.0);
let result = zs1s2(zr, s1, s2, 1e-300, 700.0, 0);
assert!((result.s1.re - (-1.0_f64).exp()).abs() < 1e-14);
assert!(result.s1.im.abs() < 1e-14);
assert_eq!(result.nz, 0);
assert_eq!(result.iuf, 1);
}
}