#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum TimeSeriesError {
ValueNotFinite {
index: usize,
},
TooFewObservations {
needed: usize,
got: usize,
},
OptionOutOfRange {
option: &'static str,
got: f64,
min: f64,
max: f64,
},
NoVariation,
DegenerateRegression,
}
impl core::fmt::Display for TimeSeriesError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
TimeSeriesError::ValueNotFinite { index } => {
write!(f, "the value at index {index} is not finite")
}
TimeSeriesError::TooFewObservations { needed, got } => {
write!(f, "at least {needed} values are needed, got {got}")
}
TimeSeriesError::OptionOutOfRange {
option,
got,
min,
max,
} => write!(f, "`{option}` must be in [{min}, {max}] (got {got})"),
TimeSeriesError::NoVariation => {
write!(f, "the series does not vary, so the statistic is undefined")
}
TimeSeriesError::DegenerateRegression => write!(
f,
"the test regression is singular, or an exact fit of the differences \
(a deterministic series), so the t-ratio is undefined"
),
}
}
}
impl std::error::Error for TimeSeriesError {}
pub(crate) fn check_series(data: &[f64], needed: usize) -> Result<(), TimeSeriesError> {
if let Some(index) = data.iter().position(|v| !v.is_finite()) {
return Err(TimeSeriesError::ValueNotFinite { index });
}
if data.len() < needed {
return Err(TimeSeriesError::TooFewObservations {
needed,
got: data.len(),
});
}
Ok(())
}
pub(crate) fn check_option(
option: &'static str,
value: usize,
min: usize,
max: usize,
) -> Result<usize, TimeSeriesError> {
if (min..=max).contains(&value) {
Ok(value)
} else {
Err(TimeSeriesError::OptionOutOfRange {
option,
got: value as f64,
min: min as f64,
max: max as f64,
})
}
}