use crate::solver_error::SolverErrors;
use crate::solver_parts::*;
use num_traits::Float;
#[derive(Clone, Debug)]
pub struct TridiagonalSystemPrecomputed<T: Float, const D: usize, const S: usize> {
diag: [T; D],
sup1: Option<[T; S]>,
sup2: Option<[T; S]>,
sins_cosins: Option<[(T, T); S]>
}
#[inline]
fn compute_x<T: Float, const D: usize>(rhs: &[T], d: &[T], a: &[T], u: &[T]) -> Result<[T; D], SolverErrors> {
let mut x = [T::zero(); D];
for i in (0..D).rev() {
let mut sum = T::zero();
if i < D - 1 {
sum = a[i] * x[i + 1];
}
if D > 1 && i < D - 2 {
sum = sum + u[i] * x[i + 2];
}
let rhs_sum = rhs[i] - sum;
let di = d[i];
if is_zero_eps_mag(di, rhs_sum) {
return Err(SolverErrors::DivisionByZero);
}
x[i] = (rhs_sum) / di;
}
Ok(x)
}
pub fn solve_givens<T: Float, const D: usize, const S: usize>(sup: &[T; S], diag: &[T; D], sub: &[T; S], rhs: &[T; D]) -> Result<[T; D], SolverErrors>
{
const { assert!(D == S + 1, "Sub and sup diagonals must be exctly 1 element smaller than main diagonal") };
let mut a = sup.clone();
let mut d = diag.clone();
let mut rhs = rhs.clone();
let mut ur = [T::zero(); S];
let u = if S > 1 {&mut ur[..S-1]} else {&mut []};
let mut x = [T::zero(); D];
solve_givens_body(sub, &mut d, &mut a, u, &mut rhs, &mut x)?;
Ok(x)
}
pub fn precompute_givens<T: Float, const D: usize, const S: usize>(sup: &[T; S], diag: &[T; D], sub: &[T; S]) -> Result<TridiagonalSystemPrecomputed<T, D, S>, SolverErrors> {
const { assert!(D == S + 1, "Sub and sup diagonals must be exctly 1 element smaller than main diagonal") };
let mut a = sup.clone();
let mut d = diag.clone();
let mut ur = [T::zero(); S];
let u = if S > 1 {&mut ur[..S-1]} else {&mut []};
let mut sins_cosins = [(T::zero(), T::zero()); S];
precompute_givens_body(sub, &mut d, &mut a, u, &mut sins_cosins)?;
Ok(TridiagonalSystemPrecomputed {
diag: d,
sup1: if a.len() > 0 {Some(a)} else {None},
sup2: if u.len() > 0 {Some(ur)} else {None},
sins_cosins: if sins_cosins.len() > 0 {Some(sins_cosins)} else {None},
})
}
impl<T: Float, const D: usize, const S: usize> TridiagonalSystemPrecomputed<T, D, S> {
pub fn solve_givens_rhs(&self, rhs: &[T; D]) -> Result<[T; D], SolverErrors> {
let mut rhsl = rhs.clone();
if let Some(sins_cosins) = &self.sins_cosins{
solve_givens_sc_rhs_body(sins_cosins, &mut rhsl);
}
compute_x(
&rhsl,
&self.diag,
self.sup1.as_ref().map_or(&[], |v| v.as_slice()),
self.sup2.as_ref().map_or(&[], |v| v.as_slice()),
)
}
}
pub fn refine_tridiag_solution_iter_kaczmarz<T: Float, const D: usize, const S: usize>(sub: &[T; S], diag: &[T; D], sup: &[T; S], rhs: &[T; D], x_init: &[T; D], iter: usize, eps: T) -> Result<[T; D], SolverErrors>{
const { assert!(D == S + 1, "Sub and sup diagonals must be exctly 1 element smaller than main diagonal") };
let mut x = x_init.clone();
let mut ai_ai_dotproducts = [T::zero(); D];
if D == 1 {
ai_ai_dotproducts[0] = diag[0].powi(2);
} else {
ai_ai_dotproducts[0] = diag[0].powi(2) + sup[0].powi(2);
ai_ai_dotproducts
.iter_mut().enumerate()
.skip(1).take(D - 2)
.for_each(|(i, elem)| *elem = diag[i].powi(2) + sup[i].powi(2) + sub[i - 1].powi(2));
ai_ai_dotproducts[D-1] = sub[D - 2].powi(2) + diag[D - 1].powi(2)
}
kaczmarz_body(sub, diag, sup, rhs, &ai_ai_dotproducts, &mut x, D, iter, eps)?;
Ok(x)
}