use crate::Precision;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Sign {
Negative,
Zero,
Positive,
}
impl Sign {
#[must_use]
pub const fn flip(self) -> Self {
match self {
Self::Positive => Self::Negative,
Self::Negative => Self::Positive,
Self::Zero => Self::Zero,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Certified {
Certain {
sign: Sign,
precision: Precision,
},
Uncertain {
attempted: Precision,
},
}
impl Certified {
pub fn from_filter(value: f64, error_bound: f64, precision: Precision) -> Self {
if !value.is_finite() || !error_bound.is_finite() || error_bound < 0.0 {
return Self::Uncertain {
attempted: precision,
};
}
if value > error_bound {
Self::Certain {
sign: Sign::Positive,
precision,
}
} else if value < -error_bound {
Self::Certain {
sign: Sign::Negative,
precision,
}
} else {
Self::Uncertain {
attempted: precision,
}
}
}
pub const fn exact_sign(sign: Sign) -> Self {
Self::Certain {
sign,
precision: Precision::Exact,
}
}
pub const fn exact(value: i64) -> Self {
let sign = if value > 0 {
Sign::Positive
} else if value < 0 {
Sign::Negative
} else {
Sign::Zero
};
Self::Certain {
sign,
precision: Precision::Exact,
}
}
pub const fn sign(self) -> Option<Sign> {
match self {
Self::Certain { sign, .. } => Some(sign),
Self::Uncertain { .. } => None,
}
}
pub const fn is_certain(self) -> bool {
matches!(self, Self::Certain { .. })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EscalationLadder {
ceiling: Precision,
}
impl EscalationLadder {
const RUNGS: [Precision; 3] = [Precision::F32, Precision::F64, Precision::Exact];
pub const fn new(ceiling: Precision) -> Self {
Self { ceiling }
}
pub const fn exact() -> Self {
Self::new(Precision::Exact)
}
pub const fn ceiling(self) -> Precision {
self.ceiling
}
pub const fn is_total(self) -> bool {
matches!(self.ceiling, Precision::Exact)
}
pub fn next_after(self, current: Precision) -> Option<Precision> {
let index = Self::RUNGS.iter().position(|&rung| rung == current)?;
Self::RUNGS
.iter()
.skip(index + 1)
.copied()
.find(|&rung| self.permits(rung))
}
pub fn permits(self, precision: Precision) -> bool {
let Some(rung) = Self::rung_index(precision) else {
return false;
};
match Self::rung_index(self.ceiling) {
Some(ceiling) => rung <= ceiling,
None => false,
}
}
pub fn rungs(self) -> impl Iterator<Item = Precision> {
Self::RUNGS
.into_iter()
.filter(move |&rung| self.permits(rung))
}
fn rung_index(precision: Precision) -> Option<usize> {
Self::RUNGS.iter().position(|&rung| rung == precision)
}
}