use crate::tree::EmlTree;
use std::fmt;
#[derive(Clone, Debug)]
pub enum EmlConstraint {
EqZero(EmlTree),
GtZero(EmlTree),
GeZero(EmlTree),
LtZero(EmlTree),
LeZero(EmlTree),
NeZero(EmlTree),
Not(Box<EmlConstraint>),
And(Vec<EmlConstraint>),
Or(Vec<EmlConstraint>),
ForAll {
var: usize,
lo: f64,
hi: f64,
body: Box<EmlConstraint>,
},
Exists {
var: usize,
lo: f64,
hi: f64,
body: Box<EmlConstraint>,
},
}
impl fmt::Display for EmlConstraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EmlConstraint::EqZero(t) => write!(f, "({t} == 0)"),
EmlConstraint::GtZero(t) => write!(f, "({t} > 0)"),
EmlConstraint::GeZero(t) => write!(f, "({t} >= 0)"),
EmlConstraint::LtZero(t) => write!(f, "({t} < 0)"),
EmlConstraint::LeZero(t) => write!(f, "({t} <= 0)"),
EmlConstraint::NeZero(t) => write!(f, "({t} != 0)"),
EmlConstraint::Not(inner) => write!(f, "¬{inner}"),
EmlConstraint::And(cs) => {
write!(f, "(and")?;
for c in cs {
write!(f, " {c}")?;
}
write!(f, ")")
}
EmlConstraint::Or(cs) => {
write!(f, "(or")?;
for c in cs {
write!(f, " {c}")?;
}
write!(f, ")")
}
EmlConstraint::ForAll { var, lo, hi, body } => {
write!(f, "∀x{var}∈[{lo},{hi}].{body}")
}
EmlConstraint::Exists { var, lo, hi, body } => {
write!(f, "∃x{var}∈[{lo},{hi}].{body}")
}
}
}
}
#[derive(Clone, Debug)]
pub struct EmlSolution {
pub assignments: Vec<f64>,
pub is_exact: bool,
}
impl EmlConstraint {
pub fn lt(a: EmlTree, b: EmlTree) -> Self {
Self::LtZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn le(a: EmlTree, b: EmlTree) -> Self {
Self::LeZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn gt(a: EmlTree, b: EmlTree) -> Self {
Self::GtZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn ge(a: EmlTree, b: EmlTree) -> Self {
Self::GeZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn eq(a: EmlTree, b: EmlTree) -> Self {
Self::EqZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn ne(a: EmlTree, b: EmlTree) -> Self {
Self::NeZero(crate::canonical::Canonical::sub(&a, &b))
}
pub fn to_nnf(self) -> Self {
match self {
Self::Not(inner) => negate(*inner),
Self::And(cs) => Self::And(cs.into_iter().map(Self::to_nnf).collect()),
Self::Or(cs) => Self::Or(cs.into_iter().map(Self::to_nnf).collect()),
Self::ForAll { var, lo, hi, body } => Self::ForAll {
var,
lo,
hi,
body: Box::new(body.to_nnf()),
},
Self::Exists { var, lo, hi, body } => Self::Exists {
var,
lo,
hi,
body: Box::new(body.to_nnf()),
},
other => other,
}
}
}
fn negate(c: EmlConstraint) -> EmlConstraint {
match c {
EmlConstraint::EqZero(t) => EmlConstraint::NeZero(t),
EmlConstraint::GtZero(t) => EmlConstraint::LeZero(t),
EmlConstraint::GeZero(t) => EmlConstraint::LtZero(t),
EmlConstraint::LtZero(t) => EmlConstraint::GeZero(t),
EmlConstraint::LeZero(t) => EmlConstraint::GtZero(t),
EmlConstraint::NeZero(t) => EmlConstraint::EqZero(t),
EmlConstraint::Not(inner) => inner.to_nnf(), EmlConstraint::And(cs) => EmlConstraint::Or(cs.into_iter().map(negate).collect()),
EmlConstraint::Or(cs) => EmlConstraint::And(cs.into_iter().map(negate).collect()),
EmlConstraint::ForAll { var, lo, hi, body } => EmlConstraint::Exists {
var,
lo,
hi,
body: Box::new(negate(*body)),
},
EmlConstraint::Exists { var, lo, hi, body } => EmlConstraint::ForAll {
var,
lo,
hi,
body: Box::new(negate(*body)),
},
}
}