#![forbid(unsafe_code)]
use crate::LaError;
#[must_use]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Tolerance {
value: f64,
}
impl Tolerance {
pub(crate) const fn new_unchecked(value: f64) -> Self {
Self { value }
}
#[inline]
pub const fn new(value: f64) -> Result<Self, LaError> {
if value >= 0.0 && value.is_finite() {
Ok(Self::new_unchecked(value))
} else {
Err(LaError::invalid_tolerance(value))
}
}
#[inline]
#[must_use]
pub const fn get(self) -> f64 {
self.value
}
}
pub const DEFAULT_SINGULAR_TOL: Tolerance = Tolerance::new_unchecked(1e-12);
pub const LDLT_SYMMETRY_REL_TOL: Tolerance = Tolerance::new_unchecked(1e-12);
#[cfg(test)]
mod tests {
use core::assert_matches;
use approx::assert_abs_diff_eq;
use super::*;
#[test]
fn default_singular_tol_is_expected() {
assert_abs_diff_eq!(DEFAULT_SINGULAR_TOL.get(), 1e-12, epsilon = 0.0);
}
#[test]
fn tolerance_new_accepts_finite_non_negative_values() {
assert_eq!(
Tolerance::new(0.0).unwrap().get().to_bits(),
0.0f64.to_bits()
);
assert_eq!(
Tolerance::new(1e-12).unwrap().get().to_bits(),
1e-12f64.to_bits()
);
assert_eq!(
Tolerance::new(f64::MAX).unwrap().get().to_bits(),
f64::MAX.to_bits()
);
}
#[test]
fn tolerance_new_rejects_negative_nan_and_infinity() {
assert_eq!(
Tolerance::new(-1.0),
Err(LaError::InvalidTolerance { value: -1.0 })
);
assert_matches!(
Tolerance::new(f64::NAN),
Err(LaError::InvalidTolerance { value }) if value.is_nan()
);
assert_eq!(
Tolerance::new(f64::INFINITY),
Err(LaError::InvalidTolerance {
value: f64::INFINITY,
})
);
assert_eq!(
Tolerance::new(f64::NEG_INFINITY),
Err(LaError::InvalidTolerance {
value: f64::NEG_INFINITY,
})
);
}
}