use num_complex::Complex;
use crate::complex::inv;
use crate::mie::kernel::real;
use crate::scalar::{DoubleDouble as Dd, Real};
use crate::{Error, Result};
pub(crate) fn lu<T: Real>(a: &mut [Complex<T>], n: usize) -> Result<Vec<usize>> {
let mut piv: Vec<usize> = (0..n).collect();
for k in 0..n {
let mut best = k;
let mut big = T::zero();
for i in k..n {
let v = a[i * n + k].norm();
if v > big {
big = v;
best = i;
}
}
if big == T::zero() || !big.is_finite() {
return Err(Error::InvalidInput(format!(
"the EBCM matrix is singular or not finite at column {k}"
)));
}
if best != k {
for j in 0..n {
a.swap(k * n + j, best * n + j);
}
piv.swap(k, best);
}
let inv_p = inv(a[k * n + k]);
for i in k + 1..n {
let f = a[i * n + k] * inv_p;
a[i * n + k] = f;
if f.re == T::zero() && f.im == T::zero() {
continue;
}
for j in k + 1..n {
let v = a[k * n + j];
a[i * n + j] = a[i * n + j] - f * v;
}
}
}
Ok(piv)
}
pub(crate) fn lu_solve<T: Real>(a: &[Complex<T>], piv: &[usize], n: usize, b: &mut [Complex<T>]) {
let mut x: Vec<Complex<T>> = piv.iter().map(|&i| b[i]).collect();
for i in 0..n {
let mut v = x[i];
for j in 0..i {
v = v - a[i * n + j] * x[j];
}
x[i] = v;
}
for i in (0..n).rev() {
let mut v = x[i];
for j in i + 1..n {
v = v - a[i * n + j] * x[j];
}
x[i] = v * inv(a[i * n + i]);
}
b.copy_from_slice(&x);
}
pub(crate) fn solve_t<T: Real>(
q: &[Complex<T>],
p: &[Complex<T>],
n: usize,
) -> Result<Vec<Complex<T>>> {
let mut qt: Vec<Complex<T>> = (0..n * n).map(|ij| q[(ij % n) * n + ij / n]).collect();
let piv = lu(&mut qt, n)?;
let mut t = vec![real(T::zero()); n * n];
let mut col = vec![real(T::zero()); n];
for i in 0..n {
for j in 0..n {
col[j] = -p[i * n + j];
}
lu_solve(&qt, &piv, n, &mut col);
t[i * n..(i + 1) * n].copy_from_slice(&col);
}
Ok(t)
}
fn lu_solve_transpose(a: &[Complex<f64>], piv: &[usize], n: usize, b: &mut [Complex<f64>]) {
let mut y = b.to_vec();
for i in 0..n {
let mut v = y[i];
for j in 0..i {
v -= a[j * n + i] * y[j];
}
y[i] = v / a[i * n + i];
}
for i in (0..n).rev() {
let mut v = y[i];
for j in i + 1..n {
v -= a[j * n + i] * y[j];
}
y[i] = v;
}
for (i, &p) in piv.iter().enumerate() {
b[p] = y[i];
}
}
pub(crate) struct F64Solve {
pub(crate) t: Vec<Complex<f64>>,
qt: Vec<Complex<f64>>,
piv: Vec<usize>,
}
pub(crate) fn solve_f64(q: &[Complex<f64>], p: &[Complex<f64>], n: usize) -> Result<F64Solve> {
let mut qt: Vec<Complex<f64>> = (0..n * n).map(|ij| q[(ij % n) * n + ij / n]).collect();
let piv = lu(&mut qt, n)?;
let mut t = vec![Complex::new(0.0, 0.0); n * n];
let mut col = vec![Complex::new(0.0, 0.0); n];
for i in 0..n {
for j in 0..n {
col[j] = -p[i * n + j];
}
lu_solve(&qt, &piv, n, &mut col);
t[i * n..(i + 1) * n].copy_from_slice(&col);
}
Ok(F64Solve { t, qt, piv })
}
struct Signs(u64);
impl Signs {
fn next(&mut self) -> f64 {
self.0 ^= self.0 >> 12;
self.0 ^= self.0 << 25;
self.0 ^= self.0 >> 27;
if self.0.wrapping_mul(0x2545_f491_4f6c_dd1d) >> 63 == 0 {
1.0
} else {
-1.0
}
}
}
pub(crate) fn error_estimate(
solved: &F64Solve,
t: &[Complex<f64>],
eq: &[f64],
ep: &[f64],
lh: &[f64],
n: usize,
) -> f64 {
error_estimate_rows(solved, t, eq, ep, lh, n).0
}
pub(crate) fn error_estimate_rows(
solved: &F64Solve,
t: &[Complex<f64>],
eq: &[f64],
ep: &[f64],
lh: &[f64],
n: usize,
) -> (f64, Vec<f64>) {
let mut signs = Signs(0x9e37_79b9_7f4a_7c15);
let mut worst = 0.0f64;
let mut rows = vec![0.0f64; n];
let mut y = vec![Complex::new(0.0, 0.0); n];
let reach: Vec<f64> = (0..n)
.map(|l| {
(0..n)
.map(|i| t[i * n + l].norm() * lh[i].exp())
.fold(0.0, f64::max)
})
.collect();
for _ in 0..3 {
for (k, v) in y.iter_mut().enumerate() {
*v = Complex::new(signs.next() * (-lh[k]).exp(), 0.0);
}
lu_solve_transpose(&solved.qt, &solved.piv, n, &mut y);
let w: Vec<Complex<f64>> = (0..n)
.map(|l| (0..n).map(|k| y[k] * (signs.next() * eq[l * n + k])).sum())
.collect();
for i in 0..n {
let mut dp = Complex::new(0.0, 0.0);
let mut v = Complex::new(0.0, 0.0);
for k in 0..n {
dp += y[k] * (signs.next() * ep[i * n + k]);
v += t[i * n + k] * w[k];
}
v += dp;
worst = worst.max(v.norm() * lh[i].exp());
rows[i] = rows[i].max(dp.norm() * lh[i].exp());
}
for (l, r) in rows.iter_mut().enumerate() {
*r = r.max(w[l].norm() * reach[l]);
}
}
(4.0 * worst, rows.into_iter().map(|r| 4.0 * r).collect())
}
pub(crate) struct Refined {
pub(crate) t: Vec<Complex<f64>>,
pub(crate) t_dd: Vec<Complex<Dd>>,
pub(crate) q: Vec<Complex<Dd>>,
pub(crate) solved: F64Solve,
pub(crate) sweeps: usize,
pub(crate) last: f64,
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn refine(
q: &[Complex<f64>],
p: &[Complex<f64>],
qd: &[Complex<Dd>],
pd: &[Complex<Dd>],
exact: &[bool],
lh: &[f64],
n: usize,
sweeps: usize,
) -> Result<Refined> {
let dd = |c: Complex<f64>| Complex::new(Dd::from(c.re), Dd::from(c.im));
let hi = |c: Complex<Dd>| Complex::new(c.re.hi(), c.im.hi());
let pick = |ij: usize, v: &[Complex<f64>], vd: &[Complex<Dd>]| {
if exact[ij / n] { vd[ij] } else { dd(v[ij]) }
};
let qm: Vec<Complex<Dd>> = (0..n * n).map(|ij| pick(ij, q, qd)).collect();
let pm: Vec<Complex<Dd>> = (0..n * n).map(|ij| pick(ij, p, pd)).collect();
let q53: Vec<Complex<f64>> = qm.iter().map(|&c| hi(c)).collect();
let p53: Vec<Complex<f64>> = pm.iter().map(|&c| hi(c)).collect();
let solved = solve_f64(&q53, &p53, n)?;
let mut x: Vec<Complex<Dd>> = solved.t.iter().map(|&c| dd(c)).collect();
let big = solved.t.iter().map(|c| c.norm()).fold(0.0, f64::max);
let (mut done, mut last) = (0, 0.0f64);
let mut row = vec![Complex::new(0.0, 0.0); n];
for _ in 0..sweeps {
done += 1;
let (mut worst, mut worst_unscaled) = (0.0f64, 0.0f64);
for i in 0..n {
for j in 0..n {
let mut r = -pm[i * n + j];
for l in 0..n {
r -= x[i * n + l] * qm[l * n + j];
}
row[j] = hi(r);
}
lu_solve(&solved.qt, &solved.piv, n, &mut row);
for j in 0..n {
worst = worst.max(row[j].norm());
worst_unscaled = worst_unscaled.max(row[j].norm() * (lh[i] - lh[j]).exp());
x[i * n + j] += dd(row[j]);
}
}
last = worst_unscaled;
if worst <= big * 2f64.powi(-60) {
break;
}
}
Ok(Refined {
t: x.iter().map(|&c| hi(c)).collect(),
t_dd: x,
q: qm,
solved,
sweeps: done,
last,
})
}
pub(crate) fn derivative_f64(
solved: &F64Solve,
t: &[Complex<f64>],
dq: &[Complex<f64>],
dp: &[Complex<f64>],
n: usize,
) -> Vec<Complex<f64>> {
let mut out = vec![Complex::new(0.0, 0.0); n * n];
let mut row = vec![Complex::new(0.0, 0.0); n];
for i in 0..n {
for j in 0..n {
let mut v = dp[i * n + j];
for l in 0..n {
v += t[i * n + l] * dq[l * n + j];
}
row[j] = -v;
}
lu_solve(&solved.qt, &solved.piv, n, &mut row);
out[i * n..(i + 1) * n].copy_from_slice(&row);
}
out
}
pub(crate) fn refine_derivative(
r: &Refined,
dq: &[Complex<Dd>],
dp: &[Complex<Dd>],
n: usize,
sweeps: usize,
) -> Vec<Complex<f64>> {
let dd = |c: Complex<f64>| Complex::new(Dd::from(c.re), Dd::from(c.im));
let hi = |c: Complex<Dd>| Complex::new(c.re.hi(), c.im.hi());
let mut rhs = vec![Complex::new(Dd::from(0.0), Dd::from(0.0)); n * n];
for i in 0..n {
for j in 0..n {
let mut v = -dp[i * n + j];
for l in 0..n {
v -= r.t_dd[i * n + l] * dq[l * n + j];
}
rhs[i * n + j] = v;
}
}
let first: Vec<Complex<f64>> = rhs.iter().map(|&c| hi(c)).collect();
let mut x: Vec<Complex<Dd>> = Vec::with_capacity(n * n);
let mut row = vec![Complex::new(0.0, 0.0); n];
for i in 0..n {
row.copy_from_slice(&first[i * n..(i + 1) * n]);
lu_solve(&r.solved.qt, &r.solved.piv, n, &mut row);
x.extend(row.iter().map(|&c| dd(c)));
}
let big = x.iter().map(|c| hi(*c).norm()).fold(0.0, f64::max);
for _ in 0..sweeps {
let mut worst = 0.0f64;
for i in 0..n {
for j in 0..n {
let mut v = rhs[i * n + j];
for l in 0..n {
v -= x[i * n + l] * r.q[l * n + j];
}
row[j] = hi(v);
}
lu_solve(&r.solved.qt, &r.solved.piv, n, &mut row);
for j in 0..n {
worst = worst.max(row[j].norm());
x[i * n + j] += dd(row[j]);
}
}
if worst <= big * 2f64.powi(-60) {
break;
}
}
x.iter().map(|&c| hi(c)).collect()
}