use core::fmt;
use crate::errors::CurveError;
pub mod brent;
pub mod linear_solve;
pub mod tridiag;
pub use brent::{BrentConfig, brent_root};
pub use linear_solve::{solve, solve_spd};
pub use tridiag::thomas;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MathError {
Singular,
NotSpd,
DimensionMismatch,
NoConvergence,
BracketNotStraddling,
}
impl fmt::Display for MathError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Singular => write!(f, "matrix is singular"),
Self::NotSpd => write!(f, "matrix is not symmetric positive-definite"),
Self::DimensionMismatch => write!(f, "inputs have inconsistent dimensions"),
Self::NoConvergence => write!(f, "iterative algorithm did not converge"),
Self::BracketNotStraddling => {
write!(f, "f(a) and f(b) do not straddle zero")
}
}
}
}
impl std::error::Error for MathError {}
impl From<MathError> for CurveError {
fn from(e: MathError) -> Self {
match e {
MathError::DimensionMismatch => Self::TooFewNodes { found: 0 },
_ => Self::InvalidTime { t: f64::NAN },
}
}
}
#[inline]
#[must_use]
pub fn index_to_f64(i: usize) -> f64 {
let value = i as u64;
let high = u32::try_from(value >> 32).unwrap_or(u32::MAX);
let low = u32::try_from(value & 0xFFFF_FFFF).unwrap_or(u32::MAX);
f64::from(high) * 4_294_967_296.0 + f64::from(low)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn math_error_display_all_variants() {
assert_eq!(format!("{}", MathError::Singular), "matrix is singular");
assert!(format!("{}", MathError::NotSpd).contains("positive-definite"));
assert!(format!("{}", MathError::DimensionMismatch).contains("dimensions"));
assert!(format!("{}", MathError::NoConvergence).contains("converge"));
assert!(format!("{}", MathError::BracketNotStraddling).contains("straddle"));
}
#[test]
fn math_error_is_error_trait() {
let err: &dyn std::error::Error = &MathError::Singular;
assert!(err.source().is_none());
}
#[test]
fn math_error_copy_eq_hash() {
let err = MathError::NotSpd;
let copy = err;
assert_eq!(err, copy);
let mut set = std::collections::HashSet::new();
set.insert(err);
assert!(set.contains(©));
}
#[test]
fn math_error_debug() {
assert!(format!("{:?}", MathError::Singular).contains("Singular"));
}
#[test]
fn curve_error_from_math_error_singular() {
let ce: CurveError = MathError::Singular.into();
assert!(matches!(ce, CurveError::InvalidTime { .. }));
}
#[test]
fn curve_error_from_math_error_dim_mismatch() {
let ce: CurveError = MathError::DimensionMismatch.into();
assert!(matches!(ce, CurveError::TooFewNodes { .. }));
}
#[test]
fn index_to_f64_basic() {
assert!((index_to_f64(0) - 0.0).abs() < f64::EPSILON);
assert!((index_to_f64(1) - 1.0).abs() < f64::EPSILON);
assert!((index_to_f64(1000) - 1000.0).abs() < f64::EPSILON);
}
#[test]
fn index_to_f64_large() {
let large = 1usize << 40;
let expected = f64::from(1u32 << 30) * 1024.0;
assert!((index_to_f64(large) - expected).abs() < f64::EPSILON);
}
}