use core::cmp::Ordering;
use super::Gate;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum GateLawPoint {
Lower,
Upper,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
pub enum GateLawCheckError {
#[error("lower progress is greater than upper progress")]
ProgressDescends,
#[error("lower and upper progress are incomparable")]
ProgressIncomparable,
#[error("advance did not monotonically increase {at:?} progress")]
AdvanceNotMonotone {
at: GateLawPoint,
},
#[error("deliverable and stale overlap at {at:?} progress")]
DeliverableAndStale {
at: GateLawPoint,
},
#[error("stale is not terminal across the progress pair")]
StaleNotTerminal,
#[error("deliverable or stale is not upward closed across the progress pair")]
StableNotUpwardClosed,
}
pub fn check_gate_laws<G: Gate>(
lower: &G::Progress,
upper: &G::Progress,
sender: u32,
dep: &G::Dep,
) -> Result<(), GateLawCheckError>
where
G::Progress: Clone + PartialOrd,
{
match lower.partial_cmp(upper) {
Some(Ordering::Less | Ordering::Equal) => {}
Some(Ordering::Greater) => return Err(GateLawCheckError::ProgressDescends),
None => return Err(GateLawCheckError::ProgressIncomparable),
}
let mut advanced_lower = lower.clone();
G::advance(&mut advanced_lower, sender, dep);
if !matches!(
lower.partial_cmp(&advanced_lower),
Some(Ordering::Less | Ordering::Equal)
) {
return Err(GateLawCheckError::AdvanceNotMonotone {
at: GateLawPoint::Lower,
});
}
let mut advanced_upper = upper.clone();
G::advance(&mut advanced_upper, sender, dep);
if !matches!(
upper.partial_cmp(&advanced_upper),
Some(Ordering::Less | Ordering::Equal)
) {
return Err(GateLawCheckError::AdvanceNotMonotone {
at: GateLawPoint::Upper,
});
}
let lower_deliverable = G::deliverable(lower, sender, dep);
let lower_stale = G::stale(lower, sender, dep);
let upper_deliverable = G::deliverable(upper, sender, dep);
let upper_stale = G::stale(upper, sender, dep);
if lower_deliverable && lower_stale {
return Err(GateLawCheckError::DeliverableAndStale {
at: GateLawPoint::Lower,
});
}
if upper_deliverable && upper_stale {
return Err(GateLawCheckError::DeliverableAndStale {
at: GateLawPoint::Upper,
});
}
if lower_stale && !upper_stale {
return Err(GateLawCheckError::StaleNotTerminal);
}
if (lower_deliverable || lower_stale) && !(upper_deliverable || upper_stale) {
return Err(GateLawCheckError::StableNotUpwardClosed);
}
Ok(())
}