use num_complex::Complex;
use super::MieSolution;
use crate::scalar::Real;
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct Efficiencies<T> {
pub q_ext: T,
pub q_sca: T,
pub q_abs: T,
pub q_back: T,
pub g: T,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct EfficiencyJacobian<T> {
pub x: Efficiencies<T>,
pub m_re: Efficiencies<T>,
pub m_im: Efficiencies<T>,
}
fn re_mul_conj<T: Real>(u: Complex<T>, v: Complex<T>) -> T {
u.re * v.re + u.im * v.im
}
fn int<T: Real>(k: usize) -> T {
T::from_f64(k as f64)
}
struct Sums<T> {
sca: T,
back: Complex<T>,
g: T,
}
fn value_sums<T: Real>(a: &[Complex<T>], b: &[Complex<T>]) -> Sums<T> {
let n = a.len();
let mut sca = T::zero();
let mut back = Complex::new(T::zero(), T::zero());
let mut g = T::zero();
for k in 1..=n {
let (ak, bk) = (a[k - 1], b[k - 1]);
let w: T = int(2 * k + 1);
sca += w * (ak.norm_sqr() + bk.norm_sqr());
let t = (ak - bk).scale(w);
back = if k % 2 == 0 { back + t } else { back - t };
let kf: T = int(k);
if k < n {
let (a1, b1) = (a[k], b[k]);
g += kf * (kf + T::from_f64(2.0)) / (kf + T::one())
* (re_mul_conj(ak, a1) + re_mul_conj(bk, b1));
}
g += w / (kf * (kf + T::one())) * re_mul_conj(ak, bk);
}
Sums { sca, back, g }
}
fn derivative_sums<T: Real>(
a: &[Complex<T>],
b: &[Complex<T>],
da: impl Fn(usize) -> Complex<T>,
db: impl Fn(usize) -> Complex<T>,
) -> Sums<T> {
let n = a.len();
let two = T::from_f64(2.0);
let mut sca = T::zero();
let mut back = Complex::new(T::zero(), T::zero());
let mut g = T::zero();
for k in 1..=n {
let (ak, bk, dak, dbk) = (a[k - 1], b[k - 1], da(k - 1), db(k - 1));
let w: T = int(2 * k + 1);
sca += w * two * (re_mul_conj(dak, ak) + re_mul_conj(dbk, bk));
let t = (dak - dbk).scale(w);
back = if k % 2 == 0 { back + t } else { back - t };
let kf: T = int(k);
if k < n {
let (a1, b1, da1, db1) = (a[k], b[k], da(k), db(k));
g += kf * (kf + two) / (kf + T::one())
* (re_mul_conj(dak, a1)
+ re_mul_conj(ak, da1)
+ re_mul_conj(dbk, b1)
+ re_mul_conj(bk, db1));
}
g += w / (kf * (kf + T::one())) * (re_mul_conj(dak, bk) + re_mul_conj(ak, dbk));
}
Sums { sca, back, g }
}
impl<T: Real> MieSolution<'_, T> {
pub fn efficiencies(&self) -> Efficiencies<T> {
self.efficiencies_from(&value_sums(&self.state.a, &self.state.b))
}
fn efficiencies_from(&self, sums: &Sums<T>) -> Efficiencies<T> {
let s = self.state;
let inv_x = s.x.recip();
let inv_x2 = inv_x * inv_x;
let two_x2 = T::from_f64(2.0) * inv_x2;
let q_sca = two_x2 * sums.sca;
let q_abs = two_x2 * s.abs_sum;
let g = if sums.sca == T::zero() {
T::zero()
} else {
T::from_f64(2.0) * sums.g / sums.sca
};
Efficiencies {
q_ext: q_sca + q_abs,
q_sca,
q_abs,
q_back: sums.back.norm_sqr() * inv_x2,
g,
}
}
pub fn efficiencies_jacobian(&self) -> Option<EfficiencyJacobian<T>> {
let s = self.state;
if !s.derivatives {
return None;
}
let i1 = Complex::new(T::zero(), T::one());
let two = T::from_f64(2.0);
let inv_x = s.x.recip();
let inv_x2 = inv_x * inv_x;
let two_x2 = two * inv_x2;
let values = value_sums(&s.a, &s.b);
let q = self.efficiencies_from(&values);
let mut out = [q; 3];
for (p, o) in out.iter_mut().enumerate() {
let sums = match p {
0 => derivative_sums(&s.a, &s.b, |k| s.da_dx[k], |k| s.db_dx[k]),
1 => derivative_sums(&s.a, &s.b, |k| s.da_dm[k], |k| s.db_dm[k]),
_ => derivative_sums(&s.a, &s.b, |k| i1 * s.da_dm[k], |k| i1 * s.db_dm[k]),
};
let x_term = |v: T| if p == 0 { two * inv_x * v } else { T::zero() };
let q_sca = two_x2 * sums.sca - x_term(q.q_sca);
let q_abs = two_x2 * s.abs_sum_jacobian[p] - x_term(q.q_abs);
let q_back = two * re_mul_conj(sums.back, values.back) * inv_x2 - x_term(q.q_back);
let g = if values.sca == T::zero() {
T::zero()
} else {
(two * sums.g - q.g * sums.sca) / values.sca
};
*o = Efficiencies {
q_ext: q_sca + q_abs,
q_sca,
q_abs,
q_back,
g,
};
}
let [x, m_re, m_im] = out;
Some(EfficiencyJacobian { x, m_re, m_im })
}
}