use core::fmt;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum NumericError {
NonFiniteInput {
name: &'static str,
},
NonFiniteEvaluation {
name: &'static str,
at: Option<f64>,
},
InvalidArgument {
name: &'static str,
reason: &'static str,
},
DimensionMismatch {
name: &'static str,
expected: usize,
found: usize,
},
NoSignChange {
f_lower: f64,
f_upper: f64,
},
ZeroPolynomial,
Singular {
name: &'static str,
},
RankDeficient {
name: &'static str,
rank: usize,
required: usize,
},
NotSymmetric {
row: usize,
column: usize,
},
NotPositiveDefinite {
pivot: usize,
},
NotConverged {
name: &'static str,
error_estimate: f64,
},
}
impl fmt::Display for NumericError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NonFiniteInput { name } => write!(f, "{name} is not finite"),
Self::NonFiniteEvaluation { name, at: Some(x) } => {
write!(f, "{name} returned a non-finite value at {x}")
}
Self::NonFiniteEvaluation { name, at: None } => {
write!(f, "{name} returned a non-finite value")
}
Self::InvalidArgument { name, reason } => write!(f, "{name} {reason}"),
Self::DimensionMismatch {
name,
expected,
found,
} => write!(f, "{name} has size {found}, expected {expected}"),
Self::NoSignChange { f_lower, f_upper } => write!(
f,
"function values {f_lower} and {f_upper} at the bracket ends do not change sign"
),
Self::ZeroPolynomial => f.write_str("polynomial is identically zero"),
Self::Singular { name } => write!(f, "{name} is singular to working precision"),
Self::RankDeficient {
name,
rank,
required,
} => write!(f, "{name} has numerical rank {rank}, {required} required"),
Self::NotSymmetric { row, column } => {
write!(f, "matrix is not symmetric at ({row}, {column})")
}
Self::NotPositiveDefinite { pivot } => {
write!(f, "matrix is not positive definite (pivot {pivot})")
}
Self::NotConverged {
name,
error_estimate,
} => write!(
f,
"{name} did not converge (error estimate {error_estimate})"
),
}
}
}
impl std::error::Error for NumericError {}
pub type NumericResult<T> = Result<T, NumericError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Converged,
BudgetExhausted,
RoundoffLimited,
}
pub(crate) fn finite(value: f64, name: &'static str) -> NumericResult<f64> {
if value.is_finite() {
Ok(value)
} else {
Err(NumericError::NonFiniteInput { name })
}
}
pub(crate) fn all_finite(values: &[f64], name: &'static str) -> NumericResult<()> {
if values.iter().all(|v| v.is_finite()) {
Ok(())
} else {
Err(NumericError::NonFiniteInput { name })
}
}
pub(crate) fn tolerance(value: f64, name: &'static str) -> NumericResult<f64> {
finite(value, name)?;
if value < 0.0 {
return Err(NumericError::InvalidArgument {
name,
reason: "must be non-negative",
});
}
Ok(value)
}