#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Formula {
Atom(String),
NegAtom(String),
Tensor(Box<Formula>, Box<Formula>),
Par(Box<Formula>, Box<Formula>),
One,
Bottom,
With(Box<Formula>, Box<Formula>),
Plus(Box<Formula>, Box<Formula>),
Top,
Zero,
OfCourse(Box<Formula>),
WhyNot(Box<Formula>),
Lolli(Box<Formula>, Box<Formula>),
}
impl Formula {
pub fn negate(&self) -> Formula {
match self {
Formula::Atom(a) => Formula::NegAtom(a.clone()),
Formula::NegAtom(a) => Formula::Atom(a.clone()),
Formula::Tensor(a, b) => {
Formula::Par(Box::new(a.negate()), Box::new(b.negate()))
}
Formula::Par(a, b) => {
Formula::Tensor(Box::new(a.negate()), Box::new(b.negate()))
}
Formula::One => Formula::Bottom,
Formula::Bottom => Formula::One,
Formula::With(a, b) => {
Formula::Plus(Box::new(a.negate()), Box::new(b.negate()))
}
Formula::Plus(a, b) => {
Formula::With(Box::new(a.negate()), Box::new(b.negate()))
}
Formula::Top => Formula::Zero,
Formula::Zero => Formula::Top,
Formula::OfCourse(a) => Formula::WhyNot(Box::new(a.negate())),
Formula::WhyNot(a) => Formula::OfCourse(Box::new(a.negate())),
Formula::Lolli(a, b) => {
Formula::Tensor(a.clone(), Box::new(b.negate()))
}
}
}
pub fn desugar(&self) -> Formula {
match self {
Formula::Lolli(a, b) => {
Formula::Par(Box::new(a.negate().desugar()), Box::new(b.desugar()))
}
Formula::Tensor(a, b) => {
Formula::Tensor(Box::new(a.desugar()), Box::new(b.desugar()))
}
Formula::Par(a, b) => {
Formula::Par(Box::new(a.desugar()), Box::new(b.desugar()))
}
Formula::With(a, b) => {
Formula::With(Box::new(a.desugar()), Box::new(b.desugar()))
}
Formula::Plus(a, b) => {
Formula::Plus(Box::new(a.desugar()), Box::new(b.desugar()))
}
Formula::OfCourse(a) => Formula::OfCourse(Box::new(a.desugar())),
Formula::WhyNot(a) => Formula::WhyNot(Box::new(a.desugar())),
_ => self.clone(),
}
}
pub fn is_positive(&self) -> bool {
matches!(
self,
Formula::Atom(_)
| Formula::Tensor(_, _)
| Formula::One
| Formula::Plus(_, _)
| Formula::Zero
| Formula::OfCourse(_)
)
}
pub fn is_negative(&self) -> bool {
!self.is_positive()
}
pub fn pretty(&self) -> String {
match self {
Formula::Atom(a) => a.clone(),
Formula::NegAtom(a) => format!("{}⊥", a),
Formula::Tensor(a, b) => format!("({} ⊗ {})", a.pretty(), b.pretty()),
Formula::Par(a, b) => format!("({} ⅋ {})", a.pretty(), b.pretty()),
Formula::Lolli(a, b) => format!("({} ⊸ {})", a.pretty(), b.pretty()),
Formula::With(a, b) => format!("({} & {})", a.pretty(), b.pretty()),
Formula::Plus(a, b) => format!("({} ⊕ {})", a.pretty(), b.pretty()),
Formula::OfCourse(a) => format!("!{}", a.pretty()),
Formula::WhyNot(a) => format!("?{}", a.pretty()),
Formula::One => "1".to_string(),
Formula::Bottom => "⊥".to_string(),
Formula::Top => "⊤".to_string(),
Formula::Zero => "0".to_string(),
}
}
pub fn pretty_ascii(&self) -> String {
match self {
Formula::Atom(a) => a.clone(),
Formula::NegAtom(a) => format!("{}^", a),
Formula::Tensor(a, b) => format!("({} * {})", a.pretty_ascii(), b.pretty_ascii()),
Formula::Par(a, b) => format!("({} | {})", a.pretty_ascii(), b.pretty_ascii()),
Formula::Lolli(a, b) => format!("({} -o {})", a.pretty_ascii(), b.pretty_ascii()),
Formula::With(a, b) => format!("({} & {})", a.pretty_ascii(), b.pretty_ascii()),
Formula::Plus(a, b) => format!("({} + {})", a.pretty_ascii(), b.pretty_ascii()),
Formula::OfCourse(a) => format!("!{}", a.pretty_ascii()),
Formula::WhyNot(a) => format!("?{}", a.pretty_ascii()),
Formula::One => "1".to_string(),
Formula::Bottom => "bot".to_string(),
Formula::Top => "top".to_string(),
Formula::Zero => "0".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_negation_involutive() {
let a = Formula::Atom("A".to_string());
assert_eq!(a.negate().negate(), a);
let complex = Formula::Tensor(
Box::new(Formula::Atom("A".to_string())),
Box::new(Formula::Atom("B".to_string())),
);
assert_eq!(complex.negate().negate(), complex);
}
#[test]
fn test_de_morgan() {
let a = Formula::Atom("A".to_string());
let b = Formula::Atom("B".to_string());
let tensor = Formula::Tensor(Box::new(a.clone()), Box::new(b.clone()));
let expected = Formula::Par(
Box::new(Formula::NegAtom("A".to_string())),
Box::new(Formula::NegAtom("B".to_string())),
);
assert_eq!(tensor.negate(), expected);
}
#[test]
fn test_polarity() {
assert!(Formula::Atom("A".to_string()).is_positive());
assert!(Formula::NegAtom("A".to_string()).is_negative());
assert!(Formula::Tensor(
Box::new(Formula::Atom("A".to_string())),
Box::new(Formula::Atom("B".to_string()))
)
.is_positive());
assert!(Formula::Par(
Box::new(Formula::Atom("A".to_string())),
Box::new(Formula::Atom("B".to_string()))
)
.is_negative());
}
}