#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod tests;
pub mod prelude {
pub use crate::compute_solution_residual_norm;
pub use crate::compute_tridiag_determinant;
pub use crate::solver_error::SolverErrors;
#[cfg(feature = "alloc")]
pub use crate::alloc::{
compute_ruiz_scaling, precompute_givens, precompute_givens_ruiz, solve_givens,
solve_givens_ruiz_precond, tridiag_iter_kaczmarz, TridiagSysPrecomp,
};
#[cfg(not(feature = "alloc"))]
pub use crate::no_alloc::{
compute_ruiz_scaling, precompute_givens, precompute_givens_ruiz, solve_givens,
solve_givens_ruiz_precond, tridiag_iter_kaczmarz, TridiagSysPrecomp,
};
}
use num_traits::Float;
use crate::prelude::SolverErrors;
pub mod solver_error;
mod solver_parts;
#[cfg(any(doc, feature = "alloc", feature = "std"))]
pub mod alloc;
#[cfg(any(doc, not(any(feature = "alloc", feature = "std"))))]
pub mod no_alloc;
pub fn compute_tridiag_determinant<T: Float>(
sup: &[T],
diag: &[T],
sub: &[T],
) -> Result<T, SolverErrors> {
if sup.len() != sub.len() || sup.len() + 1 != diag.len() {
return Err(SolverErrors::InvalidDiagonals);
}
let mut dpp;
let mut dp = T::one();
let mut d = diag[0];
for ((&a, &b), &c) in diag.iter().skip(1).zip(sup).zip(sub) {
dpp = dp;
dp = d;
d = a * dp - b * c * dpp;
}
Ok(d)
}
pub fn compute_solution_residual_norm<T: Float>(
sup: &[T],
diag: &[T],
sub: &[T],
rhs: &[T],
x: &[T],
) -> Result<T, solver_error::SolverErrors> {
if sup.len() != sub.len() || sup.len() + 1 != diag.len() || x.len() != diag.len() {
return Err(solver_error::SolverErrors::InvalidDiagonals);
} else if diag.len() != rhs.len() {
return Err(solver_error::SolverErrors::InvalidRhsSizing);
}
let n = diag.len();
if diag.len() == 1 {
return Ok((rhs[0] - diag[0] * x[0]).abs());
}
let mut sum = (rhs[0] - diag[0] * x[0] - sup[0] * x[1]).powi(2)
+ (rhs[n - 1] - diag[n - 1] * x[n - 1] - sub[n - 2] * x[n - 2]).powi(2);
for i in 1..n - 1 {
sum = sum + (rhs[i] - sub[i - 1] * x[i - 1] - diag[i] * x[i] - sup[i] * x[i + 1]).powi(2);
}
Ok(sum.sqrt())
}