use super::{Compare, Comparison, MaybeImprecise};
use std::cmp::max;
use std::cmp::min;
use std::ops::Not;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Ternary {
Void,
False,
True,
}
impl From<Ternary> for bool {
fn from(value: Ternary) -> Self {
matches!(value, Ternary::True)
}
}
impl Ternary {
#[must_use]
pub fn is_true(self) -> bool {
self == Self::True
}
#[must_use]
pub fn is_false(self) -> bool {
self == Self::False
}
#[must_use]
pub fn is_void(self) -> bool {
self == Self::Void
}
#[must_use]
pub fn or(self, b: Self) -> Self {
max(self, b)
}
#[must_use]
pub const fn xor(self, b: Self) -> Self {
match (self, b) {
(Self::Void, Self::Void) => Self::Void,
(Self::True, Self::False | Self::Void) | (Self::False | Self::Void, Self::True) => {
Self::True
}
(Self::False | Self::Void, Self::False)
| (Self::False, Self::Void)
| (Self::True, Self::True) => Self::False,
}
}
#[must_use]
pub fn and(self, b: Self) -> Self {
min(self, b)
}
}
impl Not for Ternary {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Self::True => Self::False,
Self::False => Self::True,
Self::Void => Self::Void,
}
}
}
impl From<bool> for Ternary {
fn from(value: bool) -> Self {
if value { Self::True } else { Self::False }
}
}
impl<T: Compare> Compare for Option<T> {
fn gt(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.gt(comparison))
}
fn gt_eq(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.gt_eq(comparison))
}
fn lt(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.lt(comparison))
}
fn lt_eq(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.lt_eq(comparison))
}
fn eq(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.eq(comparison))
}
fn ne(&self, comparison: usize) -> Ternary {
self.as_ref().map_or(Ternary::Void, |x| x.ne(comparison))
}
}
impl Compare for MaybeImprecise {
fn gt(&self, comparison: usize) -> Ternary {
match self {
Self::Precise(x) => match x {
super::MaybeVar::Const(x) => (*x > comparison).into(),
super::MaybeVar::Var(_) => Ternary::True,
},
Self::Imprecise(x) => match x {
Comparison::GreaterThan(_)
| Comparison::GreaterThanOrEqual(_)
| Comparison::NotEqual(_) => Ternary::True,
Comparison::Equal(x) | Comparison::LessThanOrEqual(x) => (*x > comparison).into(),
Comparison::LessThan(x) => (*x > comparison + 1).into(),
},
}
}
fn gt_eq(&self, comparison: usize) -> Ternary {
self.gt(comparison).or(Compare::eq(self, comparison))
}
fn lt(&self, comparison: usize) -> Ternary {
match self {
Self::Precise(x) => match x {
super::MaybeVar::Const(x) => (*x < comparison).into(),
super::MaybeVar::Var(_) => Ternary::True,
},
Self::Imprecise(x) => match x {
Comparison::NotEqual(_)
| Comparison::LessThanOrEqual(_)
| Comparison::LessThan(_) => Ternary::True,
Comparison::GreaterThan(x) => (*x < comparison - 1).into(),
Comparison::GreaterThanOrEqual(x) | Comparison::Equal(x) => {
(*x < comparison).into()
}
},
}
}
fn lt_eq(&self, comparison: usize) -> Ternary {
self.lt(comparison).or(Compare::eq(self, comparison))
}
fn eq(&self, comparison: usize) -> Ternary {
match self {
Self::Precise(x) => match x {
super::MaybeVar::Const(x) => (*x == comparison).into(),
super::MaybeVar::Var(_) => Ternary::True,
},
Self::Imprecise(x) => match x {
Comparison::Equal(x) => (*x == comparison).into(),
Comparison::GreaterThan(x) => (*x < comparison).into(),
Comparison::GreaterThanOrEqual(x) => (*x <= comparison).into(),
Comparison::LessThan(x) => (*x > comparison).into(),
Comparison::LessThanOrEqual(x) => (*x >= comparison).into(),
Comparison::NotEqual(x) => (comparison != *x).into(),
},
}
}
fn ne(&self, comparison: usize) -> Ternary {
match self {
Self::Precise(x) => match x {
super::MaybeVar::Const(x) => (*x != comparison).into(),
super::MaybeVar::Var(_) => Ternary::True,
},
Self::Imprecise(x) => match x {
Comparison::NotEqual(_)
| Comparison::LessThanOrEqual(_)
| Comparison::LessThan(_) => Ternary::True,
Comparison::GreaterThanOrEqual(x) => (*x < comparison).into(),
Comparison::GreaterThan(x) => (*x < comparison - 1).into(),
Comparison::Equal(x) => (*x != comparison).into(),
},
}
}
}
impl Compare for usize {
fn gt(&self, comparison: usize) -> Ternary {
(*self > comparison).into()
}
fn gt_eq(&self, comparison: usize) -> Ternary {
(*self >= comparison).into()
}
fn lt(&self, comparison: usize) -> Ternary {
(*self < comparison).into()
}
fn lt_eq(&self, comparison: usize) -> Ternary {
(*self <= comparison).into()
}
fn eq(&self, comparison: usize) -> Ternary {
(*self == comparison).into()
}
fn ne(&self, comparison: usize) -> Ternary {
(*self != comparison).into()
}
}