Replaced macros with functions and Box with Rc
This commit is contained in:
parent
db4dc2db54
commit
4b22fc4a7e
@ -10,7 +10,7 @@ nom = "7.1.3"
|
||||
# Async
|
||||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
|
||||
# Serialization / Deserialization
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||
# API
|
||||
axum = "0.7.5"
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Creates a new cargo project, copies the Cargo.toml and Cargo.lock files to the new project,
|
||||
# builds the project, and then copies the built binary to a new image.
|
||||
|
||||
FROM rust:1.78 as build
|
||||
FROM rust:1.79 as build
|
||||
LABEL authors="Martin Berg Alstad"
|
||||
|
||||
RUN USER=root cargo new --bin simplify_truths
|
||||
|
@ -1,8 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::expressions::iterator::ExpressionIterator;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::expressions::iterator::ExpressionIterator;
|
||||
use crate::expressions::operator::BinaryOperator;
|
||||
use crate::parsing::expression_parser::parse_expression;
|
||||
|
||||
@ -13,8 +15,8 @@ pub trait OppositeEq {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Expression {
|
||||
Not(Box<Expression>),
|
||||
Binary { left: Box<Expression>, operator: BinaryOperator, right: Box<Expression> },
|
||||
Not(Rc<Expression>),
|
||||
Binary { left: Rc<Expression>, operator: BinaryOperator, right: Rc<Expression> },
|
||||
Atomic(String),
|
||||
}
|
||||
|
||||
@ -27,14 +29,6 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exists(&self, atomic_value: &str) -> bool {
|
||||
match self {
|
||||
Expression::Not(expr) => expr.exists(atomic_value),
|
||||
Expression::Binary { left, right, .. } => left.exists(atomic_value) || right.exists(atomic_value),
|
||||
Expression::Atomic(value) => value == atomic_value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_atomic_values(&self) -> HashSet<String> {
|
||||
match self {
|
||||
Expression::Not(expr) => expr.get_atomic_values(),
|
||||
@ -47,33 +41,13 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count_distinct(&self) -> usize {
|
||||
self.count_distinct_with_visited(vec![].as_mut())
|
||||
}
|
||||
|
||||
fn count_distinct_with_visited(&self, visited: &mut Vec<String>) -> usize {
|
||||
match self {
|
||||
Expression::Not(expr) => expr.count_distinct_with_visited(visited),
|
||||
Expression::Binary { left, right, .. } =>
|
||||
left.count_distinct_with_visited(visited) + right.count_distinct_with_visited(visited),
|
||||
Expression::Atomic(value) => {
|
||||
if visited.contains(value) {
|
||||
0
|
||||
} else {
|
||||
visited.push(value.clone());
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ExpressionIterator {
|
||||
ExpressionIterator::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Expression {
|
||||
type Item = Expression;
|
||||
type Item = Rc<Expression>;
|
||||
type IntoIter = ExpressionIterator;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
@ -129,38 +103,14 @@ impl Display for Expression {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{and, atomic, implies, not, or};
|
||||
use crate::expressions::expression::Expression;
|
||||
|
||||
#[test]
|
||||
fn test_count_distinct() {
|
||||
let expression = and!(
|
||||
atomic!("a"),
|
||||
or!(
|
||||
atomic!("b"),
|
||||
atomic!("c")
|
||||
)
|
||||
);
|
||||
assert_eq!(expression.count_distinct(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_distinct_duplicates() {
|
||||
let expression = and!(
|
||||
atomic!("a"),
|
||||
or!(
|
||||
atomic!("b"),
|
||||
atomic!("a")
|
||||
)
|
||||
);
|
||||
assert_eq!(expression.count_distinct(), 2);
|
||||
}
|
||||
use crate::expressions::helpers::{and, atomic, implies, not, or};
|
||||
|
||||
#[test]
|
||||
fn test_expression_a_and_not_b_display() {
|
||||
let expression = and!(
|
||||
atomic!("a"),
|
||||
not!(atomic!("b"))
|
||||
let expression = and(
|
||||
atomic("a"),
|
||||
not(atomic("b")),
|
||||
);
|
||||
assert_eq!(expression.to_string(), "a ⋀ ¬b");
|
||||
}
|
||||
@ -169,41 +119,41 @@ mod tests {
|
||||
#[ignore]
|
||||
fn test_expression_a_or_b_and_c_display() {
|
||||
// TODO
|
||||
let expression = or!(
|
||||
atomic!("a"),
|
||||
and!(
|
||||
atomic!("b"),
|
||||
atomic!("c")
|
||||
let expression = or(
|
||||
atomic("a"),
|
||||
and(
|
||||
atomic("b"),
|
||||
atomic("c"),
|
||||
));
|
||||
assert_eq!(expression.to_string(), "a ⋁ b ⋀ c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_c_and_a_or_b_display() {
|
||||
let expression = and!(
|
||||
or!(
|
||||
atomic!("a"),
|
||||
atomic!("b")
|
||||
let expression = and(
|
||||
or(
|
||||
atomic("a"),
|
||||
atomic("b"),
|
||||
),
|
||||
atomic!("c")
|
||||
atomic("c"),
|
||||
);
|
||||
assert_eq!(expression.to_string(), "(a ⋁ b) ⋀ c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_a_implies_b_display() {
|
||||
let expression = implies!(
|
||||
atomic!("a"),
|
||||
atomic!("b")
|
||||
let expression = implies(
|
||||
atomic("a"),
|
||||
atomic("b"),
|
||||
);
|
||||
assert_eq!(expression.to_string(), "a ➔ b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_not_a_and_b_display() {
|
||||
let expression = not!(and!(
|
||||
atomic!("a"),
|
||||
atomic!("b")
|
||||
let expression = not(and(
|
||||
atomic("a"),
|
||||
atomic("b"),
|
||||
));
|
||||
assert_eq!(expression.to_string(), "¬(a ⋀ b)");
|
||||
}
|
||||
@ -211,49 +161,49 @@ mod tests {
|
||||
#[test]
|
||||
fn test_from_str_into_expression_atomic() {
|
||||
let expression: Expression = "a".try_into().unwrap();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_not() {
|
||||
let expression: Expression = "!a".try_into().unwrap();
|
||||
assert_eq!(expression, not!(atomic!("a")));
|
||||
assert_eq!(expression, not(atomic("a")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_and() {
|
||||
let expression: Expression = "a & b".try_into().unwrap();
|
||||
assert_eq!(expression, and!(atomic!("a"), atomic!("b")));
|
||||
assert_eq!(expression, and(atomic("a"), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_or() {
|
||||
let expression: Expression = "a | b".try_into().unwrap();
|
||||
assert_eq!(expression, or!(atomic!("a"), atomic!("b")));
|
||||
assert_eq!(expression, or(atomic("a"), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_implies() {
|
||||
let expression: Expression = "a => b".try_into().unwrap();
|
||||
assert_eq!(expression, implies!(atomic!("a"), atomic!("b")));
|
||||
assert_eq!(expression, implies(atomic("a"), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_complex() {
|
||||
let expression: Expression = "a & b | c".try_into().unwrap();
|
||||
assert_eq!(expression, or!(and!(atomic!("a"), atomic!("b")), atomic!("c")));
|
||||
assert_eq!(expression, or(and(atomic("a"), atomic("b")), atomic("c")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_complex_parentheses() {
|
||||
let expression: Expression = "a & (b | c)".try_into().unwrap();
|
||||
assert_eq!(expression, and!(atomic!("a"), or!(atomic!("b"), atomic!("c"))));
|
||||
assert_eq!(expression, and(atomic("a"), or(atomic("b"), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_into_expression_very_complex_parentheses() {
|
||||
let expression: Expression = "(a & b) | c => (d & e)".try_into().unwrap();
|
||||
assert_eq!(expression, implies!(or!(and!(atomic!("a"), atomic!("b")), atomic!("c")), and!(atomic!("d"), atomic!("e"))));
|
||||
assert_eq!(expression, implies(or(and(atomic("a"), atomic("b")), atomic("c")), and(atomic("d"), atomic("e"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,93 +1,51 @@
|
||||
#[macro_export]
|
||||
macro_rules! and {
|
||||
($left:expr, $right:expr) => {
|
||||
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::And, $right)
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use crate::expressions::expression::Expression;
|
||||
use crate::expressions::operator::BinaryOperator;
|
||||
|
||||
#[inline]
|
||||
pub fn and<L, R>(left: L, right: R) -> Expression
|
||||
where
|
||||
L: Into<Rc<Expression>>,
|
||||
R: Into<Rc<Expression>>,
|
||||
{
|
||||
binary(left, BinaryOperator::And, right)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! or {
|
||||
($left:expr, $right:expr) => {
|
||||
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::Or, $right)
|
||||
};
|
||||
#[inline]
|
||||
pub fn or<L, R>(left: L, right: R) -> Expression
|
||||
where
|
||||
L: Into<Rc<Expression>>,
|
||||
R: Into<Rc<Expression>>,
|
||||
{
|
||||
binary(left, BinaryOperator::Or, right)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! implies {
|
||||
($left:expr, $right:expr) => {
|
||||
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::Implication, $right)
|
||||
};
|
||||
#[inline]
|
||||
pub fn implies<L, R>(left: L, right: R) -> Expression
|
||||
where
|
||||
L: Into<Rc<Expression>>,
|
||||
R: Into<Rc<Expression>>,
|
||||
{
|
||||
binary(left, BinaryOperator::Implication, right)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! binary {
|
||||
($left:expr, $operator:expr, $right:expr) => {
|
||||
$crate::expressions::expression::Expression::Binary { left: Box::new($left), operator: $operator, right: Box::new($right) }
|
||||
};
|
||||
#[inline]
|
||||
pub fn binary<L, R>(left: L, operator: BinaryOperator, right: R) -> Expression
|
||||
where
|
||||
L: Into<Rc<Expression>>,
|
||||
R: Into<Rc<Expression>>,
|
||||
{
|
||||
Expression::Binary { left: left.into(), operator, right: right.into() }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! not {
|
||||
($value:expr) => {
|
||||
$crate::expressions::expression::Expression::Not(Box::new($value))
|
||||
};
|
||||
#[inline]
|
||||
pub fn not<T: Into<Rc<Expression>>>(value: T) -> Expression {
|
||||
Expression::Not(value.into())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! atomic {
|
||||
($value:expr) => {
|
||||
$crate::expressions::expression::Expression::Atomic($value.to_string())
|
||||
};
|
||||
#[inline]
|
||||
pub fn atomic<T: Into<String>>(value: T) -> Expression {
|
||||
Expression::Atomic(value.into())
|
||||
}
|
||||
|
||||
// TODO
|
||||
#[macro_export]
|
||||
macro_rules! eval {
|
||||
($a:literal && $b:literal) => {
|
||||
$crate::and!($crate::eval!($a), $crate::eval!($b))
|
||||
};
|
||||
($a:literal || $b:literal) => {
|
||||
$crate::or!($crate::eval!($a), $crate::eval!($b))
|
||||
};
|
||||
($a:literal => $b:literal) => {
|
||||
$crate::implies!($crate::eval!($a), $crate::eval!($b))
|
||||
};
|
||||
(!$a:expr) => {
|
||||
$crate::not!($crate::eval!($a))
|
||||
};
|
||||
($value:expr) => {
|
||||
$crate::atomic!($value)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::eval;
|
||||
use crate::expressions::expression::Expression::{Atomic, Binary, Not};
|
||||
use crate::expressions::operator::BinaryOperator::{And, Implication, Or};
|
||||
|
||||
#[test]
|
||||
fn eval_atomic() {
|
||||
assert_eq!(eval!("a"), Atomic("a".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_not() {
|
||||
assert_eq!(eval!(!"a"), Not(Box::new(Atomic("a".to_string()))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_and() {
|
||||
assert_eq!(eval!("a" && "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: And, right: Box::new(Atomic("b".to_string())) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_or() {
|
||||
assert_eq!(eval!("a" || "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: Or, right: Box::new(Atomic("b".to_string())) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_implies() {
|
||||
assert_eq!(eval!("a" => "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: Implication, right: Box::new(Atomic("b".to_string())) });
|
||||
}
|
||||
}
|
||||
// TODO eval function using nom parser
|
||||
|
@ -1,49 +1,50 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use crate::expressions::expression::Expression;
|
||||
|
||||
pub struct ExpressionIterator {
|
||||
stack: Vec<Expression>,
|
||||
stack: Vec<Rc<Expression>>,
|
||||
}
|
||||
|
||||
impl ExpressionIterator {
|
||||
pub fn new(expression: Expression) -> Self {
|
||||
let stack = vec![expression];
|
||||
let stack = vec![expression.into()];
|
||||
Self { stack }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ExpressionIterator {
|
||||
type Item = Expression;
|
||||
type Item = Rc<Expression>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(expression) = self.stack.pop() {
|
||||
match &expression {
|
||||
Expression::Atomic(_) => Some(expression),
|
||||
Expression::Not(inner) => {
|
||||
self.stack.push(*inner.clone());
|
||||
Some(expression)
|
||||
}
|
||||
Expression::Binary { left, right, .. } => {
|
||||
self.stack.push(*right.clone());
|
||||
self.stack.push(*left.clone());
|
||||
Some(expression)
|
||||
}
|
||||
let expression = self.stack.pop()?;
|
||||
match expression.deref() {
|
||||
Expression::Atomic(_) => Some(expression),
|
||||
Expression::Not(inner) => {
|
||||
self.stack.push(inner.clone());
|
||||
Some(expression)
|
||||
}
|
||||
Expression::Binary { left, right, .. } => {
|
||||
self.stack.push(right.clone());
|
||||
self.stack.push(left.clone());
|
||||
Some(expression)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::expressions::helpers::{and, atomic, not};
|
||||
|
||||
#[test]
|
||||
fn test_expression_iterator() {
|
||||
let expression = not!(and!(atomic!("A"), atomic!("B")));
|
||||
let expression = not(and(atomic("A"), atomic("B")));
|
||||
let mut iterator = expression.iter();
|
||||
assert_eq!(iterator.next().unwrap(), expression);
|
||||
assert_eq!(iterator.next().unwrap(), and!(atomic!("A"), atomic!("B")));
|
||||
assert_eq!(iterator.next().unwrap(), atomic!("A"));
|
||||
assert_eq!(iterator.next().unwrap(), atomic!("B"));
|
||||
assert_eq!(iterator.next().unwrap(), expression.into());
|
||||
assert_eq!(iterator.next().unwrap(), and(atomic("A"), atomic("B")).into());
|
||||
assert_eq!(iterator.next().unwrap(), atomic("A").into());
|
||||
assert_eq!(iterator.next().unwrap(), atomic("B").into());
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
pub mod expression;
|
||||
pub mod operator;
|
||||
#[macro_use]
|
||||
pub mod helpers;
|
||||
pub mod simplify;
|
||||
pub mod truth_table;
|
||||
|
@ -1,4 +1,6 @@
|
||||
use std::ops::Deref;
|
||||
use crate::expressions::expression::{Expression, OppositeEq};
|
||||
use crate::expressions::helpers::{and, binary, not, or};
|
||||
use crate::expressions::operator::BinaryOperator;
|
||||
|
||||
pub trait Simplify {
|
||||
@ -27,16 +29,16 @@ impl Simplify for Expression {
|
||||
/// This is done by replacing `a ➔ b` with `¬a ⋁ b`.
|
||||
fn elimination_of_implication(&self) -> Self {
|
||||
match self {
|
||||
Expression::Not(expr) => not!(expr.elimination_of_implication()),
|
||||
Expression::Not(expr) => not(expr.elimination_of_implication()),
|
||||
Expression::Binary { left, operator: BinaryOperator::Implication, right } => {
|
||||
let left = left.elimination_of_implication();
|
||||
let right = right.elimination_of_implication();
|
||||
or!(not!(left), right)
|
||||
or(not(left), right)
|
||||
}
|
||||
Expression::Binary { left, operator, right } => {
|
||||
let left = left.elimination_of_implication();
|
||||
let right = right.elimination_of_implication();
|
||||
binary!(left, *operator, right)
|
||||
binary(left, *operator, right)
|
||||
}
|
||||
atomic @ Expression::Atomic(_) => atomic.clone(),
|
||||
}
|
||||
@ -48,16 +50,16 @@ impl Simplify for Expression {
|
||||
fn double_negation_elimination(&self) -> Self {
|
||||
match self {
|
||||
Expression::Not(expr) => {
|
||||
if let Expression::Not(inner) = *expr.clone() {
|
||||
if let Expression::Not(inner) = expr.deref() {
|
||||
inner.double_negation_elimination()
|
||||
} else {
|
||||
not!(expr.double_negation_elimination())
|
||||
not(expr.double_negation_elimination())
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator, right } => {
|
||||
let left = left.double_negation_elimination();
|
||||
let right = right.double_negation_elimination();
|
||||
binary!(left, *operator, right)
|
||||
binary(left, *operator, right)
|
||||
}
|
||||
atomic @ Expression::Atomic(_) => atomic.clone(),
|
||||
}
|
||||
@ -66,25 +68,25 @@ impl Simplify for Expression {
|
||||
fn de_morgans_laws(&self) -> Self {
|
||||
match self {
|
||||
Expression::Not(expr) => {
|
||||
match *expr.clone() {
|
||||
match expr.deref() {
|
||||
Expression::Binary { left, operator: BinaryOperator::And, right } => {
|
||||
// TODO unnecessary cloning calls to de_morgans_laws?
|
||||
let left = not!(left.de_morgans_laws());
|
||||
let right = not!(right.de_morgans_laws());
|
||||
or!(left, right).de_morgans_laws()
|
||||
let left = not(left.de_morgans_laws());
|
||||
let right = not(right.de_morgans_laws());
|
||||
or(left, right).de_morgans_laws()
|
||||
}
|
||||
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
|
||||
let left = not!(left.de_morgans_laws());
|
||||
let right = not!(right.de_morgans_laws());
|
||||
and!(left, right).de_morgans_laws()
|
||||
let left = not(left.de_morgans_laws());
|
||||
let right = not(right.de_morgans_laws());
|
||||
and(left, right).de_morgans_laws()
|
||||
}
|
||||
_ => not!(expr.de_morgans_laws()),
|
||||
_ => not(expr.de_morgans_laws()),
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator, right } => {
|
||||
let left = left.de_morgans_laws();
|
||||
let right = right.de_morgans_laws();
|
||||
binary!(left, *operator, right)
|
||||
binary(left, *operator, right)
|
||||
}
|
||||
atomic @ Expression::Atomic(_) => atomic.clone(),
|
||||
}
|
||||
@ -101,26 +103,26 @@ impl Simplify for Expression {
|
||||
return left.absorption_law();
|
||||
} else if right_left.is_atomic() && right_right.is_atomic() && left.opposite_eq(right_left) {
|
||||
if left.opposite_eq(right_left) {
|
||||
return and!(left.absorption_law(), right_left.absorption_law());
|
||||
return and(left.absorption_law(), right_left.absorption_law());
|
||||
} else if left.opposite_eq(right_right) {
|
||||
return and!(left.absorption_law(), right_right.absorption_law());
|
||||
return and(left.absorption_law(), right_right.absorption_law());
|
||||
}
|
||||
}
|
||||
and!(left.absorption_law(), right.absorption_law())
|
||||
and(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
(Expression::Binary { left: left_left, operator: BinaryOperator::Or, right: left_right }, _) => {
|
||||
if right_ref == left_left.as_ref() || right_ref == left_right.as_ref() {
|
||||
return right.absorption_law();
|
||||
} else if left_left.is_atomic() && left_right.is_atomic() && right.opposite_eq(left_left) {
|
||||
if right.opposite_eq(left_left) {
|
||||
return and!(left_right.absorption_law(), right.absorption_law());
|
||||
return and(left_right.absorption_law(), right.absorption_law());
|
||||
} else if right.opposite_eq(left_right) {
|
||||
return and!(left_left.absorption_law(), right.absorption_law());
|
||||
return and(left_left.absorption_law(), right.absorption_law());
|
||||
}
|
||||
}
|
||||
and!(left.absorption_law(), right.absorption_law())
|
||||
and(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
(left, right) => and!(left.absorption_law(), right.absorption_law())
|
||||
(left, right) => and(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
|
||||
@ -131,34 +133,34 @@ impl Simplify for Expression {
|
||||
return left.absorption_law();
|
||||
} else if right_left.is_atomic() && right_right.is_atomic() && left.opposite_eq(right_left) {
|
||||
if left.opposite_eq(right_left) {
|
||||
return or!(left.absorption_law(), right_left.absorption_law());
|
||||
return or(left.absorption_law(), right_left.absorption_law());
|
||||
} else if left.opposite_eq(right_right) {
|
||||
return or!(left.absorption_law(), right_right.absorption_law());
|
||||
return or(left.absorption_law(), right_right.absorption_law());
|
||||
}
|
||||
}
|
||||
or!(left.absorption_law(), right.absorption_law())
|
||||
or(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
(Expression::Binary { left: left_left, operator: BinaryOperator::And, right: left_right }, _) => {
|
||||
if right_ref == left_left.as_ref() || right_ref == left_right.as_ref() {
|
||||
return right.absorption_law();
|
||||
} else if left_left.is_atomic() && left_right.is_atomic() && right.opposite_eq(left_left) {
|
||||
if right.opposite_eq(left_left) {
|
||||
return or!(left_right.absorption_law(), right.absorption_law());
|
||||
return or(left_right.absorption_law(), right.absorption_law());
|
||||
} else if right.opposite_eq(left_right) {
|
||||
return or!(left_left.absorption_law(), right.absorption_law());
|
||||
return or(left_left.absorption_law(), right.absorption_law());
|
||||
}
|
||||
}
|
||||
or!(left.absorption_law(), right.absorption_law())
|
||||
or(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
(left, right) => or!(left.absorption_law(), right.absorption_law())
|
||||
(left, right) => or(left.absorption_law(), right.absorption_law())
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator, right } => {
|
||||
let left = left.absorption_law();
|
||||
let right = right.absorption_law();
|
||||
binary!(left, *operator, right)
|
||||
binary(left, *operator, right)
|
||||
}
|
||||
Expression::Not(expr) => not!(expr.absorption_law()),
|
||||
Expression::Not(expr) => not(expr.absorption_law()),
|
||||
atomic => atomic.clone(),
|
||||
}
|
||||
}
|
||||
@ -175,14 +177,14 @@ impl Simplify for Expression {
|
||||
(Expression::Atomic(_), Expression::Binary { left: right_left, operator: BinaryOperator::Or, right: right_right }) => {
|
||||
let right_left = right_left.distribution_law();
|
||||
let right_right = right_right.distribution_law();
|
||||
or!(and!(*left.clone(), right_left), and!(*left.clone(), right_right))
|
||||
or(and(left.clone(), right_left), and(left.clone(), right_right))
|
||||
}
|
||||
(Expression::Binary { left: left_left, operator: BinaryOperator::Or, right: left_right }, Expression::Atomic(_)) => {
|
||||
let left_left = left_left.distribution_law();
|
||||
let left_right = left_right.distribution_law();
|
||||
or!(and!(left_left, *right.clone()), and!(left_right, *right.clone()))
|
||||
or(and(left_left, right.clone()), and(left_right, right.clone()))
|
||||
}
|
||||
(left, right) => and!(left.distribution_law(), right.distribution_law())
|
||||
(left, right) => and(left.distribution_law(), right.distribution_law())
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
|
||||
@ -190,22 +192,22 @@ impl Simplify for Expression {
|
||||
(Expression::Atomic(_), Expression::Binary { left: right_left, operator: BinaryOperator::And, right: right_right }) => {
|
||||
let right_left = right_left.distribution_law();
|
||||
let right_right = right_right.distribution_law();
|
||||
and!(or!(*left.clone(), right_left), or!(*left.clone(), right_right))
|
||||
and(or(left.clone(), right_left), or(left.clone(), right_right))
|
||||
}
|
||||
(Expression::Binary { left: left_left, operator: BinaryOperator::And, right: left_right }, Expression::Atomic(_)) => {
|
||||
let left_left = left_left.distribution_law();
|
||||
let left_right = left_right.distribution_law();
|
||||
and!(or!(left_left, *right.clone()), or!(left_right, *right.clone()))
|
||||
and(or(left_left, right.clone()), or(left_right, right.clone()))
|
||||
}
|
||||
(left, right) => or!(left.distribution_law(), right.distribution_law())
|
||||
(left, right) => or(left.distribution_law(), right.distribution_law())
|
||||
}
|
||||
}
|
||||
Expression::Binary { left, operator, right } => {
|
||||
let left = left.distribution_law();
|
||||
let right = right.distribution_law();
|
||||
binary!(left, *operator, right)
|
||||
binary(left, *operator, right)
|
||||
}
|
||||
Expression::Not(expr) => not!(expr.distribution_law()),
|
||||
Expression::Not(expr) => not(expr.distribution_law()),
|
||||
atomic => atomic.clone(),
|
||||
}
|
||||
}
|
||||
@ -217,165 +219,166 @@ impl Simplify for Expression {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::expressions::helpers::{and, atomic, implies, not, or};
|
||||
use crate::expressions::simplify::Simplify;
|
||||
|
||||
#[test]
|
||||
fn test_simplify() {
|
||||
let expression = eval!("a" => "b").simplify();
|
||||
assert_eq!(expression, or!(not!(atomic!("a")), atomic!("b")));
|
||||
let expression = implies(atomic("a"), atomic("b")).simplify();
|
||||
assert_eq!(expression, or(not(atomic("a")), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implication_and_de_morgans() {
|
||||
let expression = implies!(and!(not!(atomic!("a")), atomic!("b")), atomic!("c")).simplify();
|
||||
assert_eq!(expression, or!(or!(atomic!("a"), not!(atomic!("b"))), atomic!("c")));
|
||||
let expression = implies(and(not(atomic("a")), atomic("b")), atomic("c")).simplify();
|
||||
assert_eq!(expression, or(or(atomic("a"), not(atomic("b"))), atomic("c")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elimination_of_implication() {
|
||||
let expression = eval!("a" => "b").elimination_of_implication();
|
||||
assert_eq!(expression, or!(not!(atomic!("a")), atomic!("b")));
|
||||
let expression = implies(atomic("a"), atomic("b")).elimination_of_implication();
|
||||
assert_eq!(expression, or(not(atomic("a")), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elimination_of_implication_nested() {
|
||||
let expression = implies!(atomic!("a"), implies!(atomic!("b"), atomic!("c"))).elimination_of_implication();
|
||||
assert_eq!(expression, or!(not!(atomic!("a")), or!(not!(atomic!("b")), atomic!("c"))));
|
||||
let expression = implies(atomic("a"), implies(atomic("b"), atomic("c"))).elimination_of_implication();
|
||||
assert_eq!(expression, or(not(atomic("a")), or(not(atomic("b")), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elimination_of_implication_none() {
|
||||
let expression = eval!("a" && "b").elimination_of_implication();
|
||||
assert_eq!(expression, eval!("a" && "b"));
|
||||
let expression = and(atomic("a"), atomic("b")).elimination_of_implication();
|
||||
assert_eq!(expression, and(atomic("a"), atomic("b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elimination_of_implication_nested_none() {
|
||||
let expression = or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))).elimination_of_implication();
|
||||
assert_eq!(expression, or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))));
|
||||
let expression = or(atomic("a"), and(atomic("b"), atomic("c"))).elimination_of_implication();
|
||||
assert_eq!(expression, or(atomic("a"), and(atomic("b"), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_negation_elimination() {
|
||||
let expression = not!(not!(atomic!("a"))).double_negation_elimination();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
let expression = not(not(atomic("a"))).double_negation_elimination();
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triple_negation_elimination() {
|
||||
let expression = not!(not!(not!(atomic!("a")))).double_negation_elimination();
|
||||
assert_eq!(expression, not!(atomic!("a")));
|
||||
let expression = not(not(not(atomic("a")))).double_negation_elimination();
|
||||
assert_eq!(expression, not(atomic("a")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_five_negation_elimination() {
|
||||
let expression = not!(not!(not!(not!(not!(atomic!("a")))))).double_negation_elimination();
|
||||
assert_eq!(expression, not!(atomic!("a")));
|
||||
let expression = not(not(not(not(not(atomic("a")))))).double_negation_elimination();
|
||||
assert_eq!(expression, not(atomic("a")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_negation_elimination() {
|
||||
let expression = atomic!("a").double_negation_elimination();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
let expression = atomic("a").double_negation_elimination();
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_negation_nested_elimination() {
|
||||
let expression = and!(or!(not!(eval!(!"a")), eval!("b")), not!(eval!(!"c"))).double_negation_elimination();
|
||||
assert_eq!(expression, and!(or!(atomic!("a"), atomic!("b")), atomic!("c")));
|
||||
let expression = and(or(not(not(atomic("a"))), atomic("b")), not(not(atomic("c")))).double_negation_elimination();
|
||||
assert_eq!(expression, and(or(atomic("a"), atomic("b")), atomic("c")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de_morgans_laws_and() {
|
||||
let expression = not!(eval!("a" && "b")).de_morgans_laws();
|
||||
assert_eq!(expression, or!(not!(atomic!("a")), not!(atomic!("b"))));
|
||||
let expression = not(and(atomic("a"), atomic("b"))).de_morgans_laws();
|
||||
assert_eq!(expression, or(not(atomic("a")), not(atomic("b"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de_morgans_laws_or() {
|
||||
let expression = not!(eval!("a" || "b")).de_morgans_laws();
|
||||
assert_eq!(expression, and!(not!(atomic!("a")), not!(atomic!("b"))));
|
||||
let expression = not(or(atomic("a"), atomic("b"))).de_morgans_laws();
|
||||
assert_eq!(expression, and(not(atomic("a")), not(atomic("b"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de_morgans_laws_nested_or() {
|
||||
let expression = not!(or!(eval!("a" && "b"), atomic!("c"))).de_morgans_laws(); // ¬(a ⋀ b ⋁ c)
|
||||
assert_eq!(expression, and!(or!(eval!(!"a"), eval!(!"b")), eval!(!"c"))); // ¬(a ⋀ b) ⋀ ¬c == (¬a ⋁ ¬b) ⋀ ¬c
|
||||
let expression = not(or(and(atomic("a"), atomic("b")), atomic("c"))).de_morgans_laws(); // ¬(a ⋀ b ⋁ c)
|
||||
assert_eq!(expression, and(or(not(atomic("a")), not(atomic("b"))), not(atomic("c")))); // ¬(a ⋀ b) ⋀ ¬c == (¬a ⋁ ¬b) ⋀ ¬c
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de_morgans_laws_nested_and() {
|
||||
let expression = not!(and!(eval!("a" || "b"), atomic!("c"))).de_morgans_laws(); // ¬(a ⋁ b ⋀ c)
|
||||
assert_eq!(expression, or!(and!(eval!(!"a"), eval!(!"b")), eval!(!"c"))); // ¬(a ⋁ b) ⋀ ¬c == (¬a ⋀ ¬b) ⋁ ¬c
|
||||
let expression = not(and(or(atomic("a"), atomic("b")), atomic("c"))).de_morgans_laws(); // ¬(a ⋁ b ⋀ c)
|
||||
assert_eq!(expression, or(and(not(atomic("a")), not(atomic("b"))), not(atomic("c")))); // ¬(a ⋁ b) ⋀ ¬c == (¬a ⋀ ¬b) ⋁ ¬c
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de_morgans_laws_nested_and_or() {
|
||||
let expression = not!(and!(eval!("a" || "b"), or!(atomic!("c"), atomic!("d")))).de_morgans_laws(); // ¬(a ⋁ b ⋀ c ⋁ d)
|
||||
assert_eq!(expression, or!(and!(eval!(!"a"), eval!(!"b")), and!(eval!(!"c"), eval!(!"d")))); // ¬(a ⋁ b) ⋀ ¬(c ⋁ d) == (¬a ⋀ ¬b) ⋁ (¬c ⋀ ¬d)
|
||||
let expression = not(and(or(atomic("a"), atomic("b")), or(atomic("c"), atomic("d")))).de_morgans_laws(); // ¬(a ⋁ b ⋀ c ⋁ d)
|
||||
assert_eq!(expression, or(and(not(atomic("a")), not(atomic("b"))), and(not(atomic("c")), not(atomic("d"))))); // ¬(a ⋁ b) ⋀ ¬(c ⋁ d) == (¬a ⋀ ¬b) ⋁ (¬c ⋀ ¬d)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_absorption_law_and() {
|
||||
let expression = and!(atomic!("a"), eval!("a" || "b")).absorption_law();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
let expression = and(atomic("a"), or(atomic("a"), atomic("b"))).absorption_law();
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_absorption_law_or() {
|
||||
let expression = or!(atomic!("a"), eval!("a" && "b")).absorption_law();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
let expression = or(atomic("a"), and(atomic("a"), atomic("b"))).absorption_law();
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_absorption_law_nested_and() {
|
||||
let expression = and!(atomic!("a"), or!(atomic!("a"), atomic!("b"))).absorption_law();
|
||||
assert_eq!(expression, atomic!("a"));
|
||||
let expression = and(atomic("a"), or(atomic("a"), atomic("b"))).absorption_law();
|
||||
assert_eq!(expression, atomic("a"));
|
||||
}
|
||||
|
||||
// !A & B | A <=> B | A
|
||||
#[test]
|
||||
fn test_absorption_law_not() {
|
||||
let expression = or!(and!(not!(atomic!("a")), atomic!("b")), atomic!("a")).absorption_law();
|
||||
assert_eq!(expression, or!(atomic!("b"), atomic!("a")));
|
||||
let expression = or(and(not(atomic("a")), atomic("b")), atomic("a")).absorption_law();
|
||||
assert_eq!(expression, or(atomic("b"), atomic("a")));
|
||||
}
|
||||
|
||||
// A & B | !A <=> B | !A
|
||||
#[test]
|
||||
fn test_absorption_law_not_reversed() {
|
||||
let expression = or!(and!(atomic!("a"), atomic!("b")), not!(atomic!("a"))).absorption_law();
|
||||
assert_eq!(expression, or!(atomic!("b"), not!(atomic!("a"))));
|
||||
let expression = or(and(atomic("a"), atomic("b")), not(atomic("a"))).absorption_law();
|
||||
assert_eq!(expression, or(atomic("b"), not(atomic("a"))));
|
||||
}
|
||||
|
||||
// !A & B | !A <=> !A
|
||||
#[test]
|
||||
fn test_absorption_law_double_not() {
|
||||
let expression = or!(and!(not!(atomic!("a")), atomic!("b")), not!(atomic!("a"))).absorption_law();
|
||||
assert_eq!(expression, not!(atomic!("a")));
|
||||
let expression = or(and(not(atomic("a")), atomic("b")), not(atomic("a"))).absorption_law();
|
||||
assert_eq!(expression, not(atomic("a")));
|
||||
}
|
||||
|
||||
// (A | B) & !A <=> B & !A
|
||||
#[test]
|
||||
fn test_in_parenthesis() {
|
||||
let expression = and!(or!(atomic!("a"), atomic!("b")), not!(atomic!("a"))).absorption_law();
|
||||
assert_eq!(expression, and!(atomic!("b"), not!(atomic!("a"))));
|
||||
let expression = and(or(atomic("a"), atomic("b")), not(atomic("a"))).absorption_law();
|
||||
assert_eq!(expression, and(atomic("b"), not(atomic("a"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_distributive_law_and() {
|
||||
let expression = and!(atomic!("a"), or!(atomic!("b"), atomic!("c"))).distribution_law();
|
||||
assert_eq!(expression, or!(and!(atomic!("a"), atomic!("b")), and!(atomic!("a"), atomic!("c"))));
|
||||
let expression = and(atomic("a"), or(atomic("b"), atomic("c"))).distribution_law();
|
||||
assert_eq!(expression, or(and(atomic("a"), atomic("b")), and(atomic("a"), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_distributive_law_or() {
|
||||
let expression = or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))).distribution_law();
|
||||
assert_eq!(expression, and!(or!(atomic!("a"), atomic!("b")), or!(atomic!("a"), atomic!("c"))));
|
||||
let expression = or(atomic("a"), and(atomic("b"), atomic("c"))).distribution_law();
|
||||
assert_eq!(expression, and(or(atomic("a"), atomic("b")), or(atomic("a"), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_distributive_law_nested_not() {
|
||||
let expression = and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c")))).distribution_law();
|
||||
assert_eq!(expression, and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c")))))
|
||||
let expression = and(atomic("a"), not(or(atomic("b"), atomic("c")))).distribution_law();
|
||||
assert_eq!(expression, and(atomic("a"), not(or(atomic("b"), atomic("c")))))
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ impl TruthTable {
|
||||
/// - A vector of strings representing the header
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let expression = TruthTable::extract_header(&atomic!("A"));
|
||||
/// let complex_expression = TruthTable::extract_header(&implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D"))));
|
||||
/// let expression = TruthTable::extract_header(&atomic("A"));
|
||||
/// let complex_expression = TruthTable::extract_header(&implies(and(atomic("A"), atomic("B")), or(atomic("C"), atomic("D"))));
|
||||
/// assert_eq!(expression, vec!["A"]);
|
||||
/// assert_eq!(complex_expression, vec!["A", "B", "A ⋀ B", "C", "D", "(C ⋁ D)", "A ⋀ B ➔ (C ⋁ D)"]);
|
||||
/// ```
|
||||
@ -146,14 +146,14 @@ impl TruthTable {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::expressions::helpers::{and, atomic, implies, not, or};
|
||||
use crate::matrix;
|
||||
|
||||
use super::*;
|
||||
|
||||
// TODO fails sometimes...
|
||||
#[test]
|
||||
fn test_new_truth_table() {
|
||||
let expression = and!(atomic!("A"), atomic!("B"));
|
||||
let expression = and(atomic("A"), atomic("B"));
|
||||
let truth_table = TruthTable::new(&expression, Default::default());
|
||||
assert_eq!(truth_table.header, vec!["A", "B", "A ⋀ B"]);
|
||||
assert_ne!(truth_table.truth_matrix, matrix![
|
||||
@ -172,7 +172,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_new_truth_table_a_and_b_or_c() {
|
||||
let expression = and!(or!(atomic!("A"), atomic!("C")), or!(atomic!("B"), atomic!("C")));
|
||||
let expression = and(or(atomic("A"), atomic("C")), or(atomic("B"), atomic("C")));
|
||||
let truth_table = TruthTable::new(&expression, Default::default());
|
||||
let atomics = 3;
|
||||
|
||||
@ -217,7 +217,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resolve_expression_and_all_true() {
|
||||
let expression = and!(atomic!("A"), atomic!("B"));
|
||||
let expression = and(atomic("A"), atomic("B"));
|
||||
let booleans = map!["A".into() => true, "B".into() => true];
|
||||
let header = vec!["A".into(), "B".into(), "A ⋀ B".into()];
|
||||
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
|
||||
@ -226,7 +226,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resolve_expression_and_1_true_1_false() {
|
||||
let expression = and!(atomic!("A"), atomic!("B"));
|
||||
let expression = and(atomic("A"), atomic("B"));
|
||||
let booleans = map!["A".into() => true, "B".into() => false];
|
||||
let header = vec!["A".into(), "B".into(), "A ⋀ B".into()];
|
||||
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
|
||||
@ -235,7 +235,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resolve_expression_or_1_true_1_false() {
|
||||
let expression = or!(atomic!("A"), atomic!("B"));
|
||||
let expression = or(atomic("A"), atomic("B"));
|
||||
let booleans = map!["A".into() => true, "B".into() => false];
|
||||
let header = vec!["A".into(), "B".into(), "(A ⋁ B)".into()];
|
||||
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
|
||||
@ -244,7 +244,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resolve_expression_duplicate_atomic() {
|
||||
let expression = and!(atomic!("A"), atomic!("A"));
|
||||
let expression = and(atomic("A"), atomic("A"));
|
||||
let booleans = map!["A".into() => true];
|
||||
let header = vec!["A".into(), "A ⋀ A".into()];
|
||||
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
|
||||
@ -253,7 +253,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resolve_expression_even_more_duplicates() {
|
||||
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
|
||||
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
|
||||
let booleans = HashMap::from([("A".into(), true)]);
|
||||
let header = vec!["A".into(), "A ⋀ A".into(), "A ⋀ A ⋀ A".into(), "A ⋀ A ⋀ A ⋀ A".into()];
|
||||
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
|
||||
@ -262,70 +262,70 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn _test_resolve_expression_even_more_duplicates() {
|
||||
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
|
||||
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
|
||||
let booleans = HashMap::from([("A".into(), true)]);
|
||||
let values = TruthTable::_resolve_expression(&expression, &booleans);
|
||||
assert_eq!(values, HashMap::from([
|
||||
(&atomic!("A"), true),
|
||||
(&and!(atomic!("A"), atomic!("A")), true),
|
||||
(&and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))), true),
|
||||
(&and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A")))), true),
|
||||
(&atomic("A"), true),
|
||||
(&and(atomic("A"), atomic("A")), true),
|
||||
(&and(atomic("A"), and(atomic("A"), atomic("A"))), true),
|
||||
(&and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A")))), true),
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_atomic_expression() {
|
||||
let expression = atomic!("A");
|
||||
let expression = atomic("A");
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_expression() {
|
||||
let expression = not!(atomic!("A"));
|
||||
let expression = not(atomic("A"));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "¬A"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_and_expression() {
|
||||
let expression = and!(atomic!("A"), atomic!("B"));
|
||||
let expression = and(atomic("A"), atomic("B"));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "B", "A ⋀ B"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_or_expression() {
|
||||
let expression = or!(atomic!("A"), atomic!("B"));
|
||||
let expression = or(atomic("A"), atomic("B"));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "B", "(A ⋁ B)"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_implies_expression() {
|
||||
let expression = implies!(atomic!("A"), atomic!("B"));
|
||||
let expression = implies(atomic("A"), atomic("B"));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "B", "A ➔ B"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex_expression() {
|
||||
let expression = implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D")));
|
||||
let expression = implies(and(atomic("A"), atomic("B")), or(atomic("C"), atomic("D")));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "B", "A ⋀ B", "C", "D", "(C ⋁ D)", "A ⋀ B ➔ (C ⋁ D)"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equal_expressions_should_not_duplicate() {
|
||||
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
|
||||
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "A ⋀ A", "A ⋀ A ⋀ A", "A ⋀ A ⋀ A ⋀ A"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_somewhat_equal() {
|
||||
let expression = and!(atomic!("A"), and!(or!(not!(atomic!("A")), atomic!("B")), atomic!("A")));
|
||||
let expression = and(atomic("A"), and(or(not(atomic("A")), atomic("B")), atomic("A")));
|
||||
let header = TruthTable::extract_header(&expression);
|
||||
assert_eq!(header, vec!["A", "¬A", "B", "(¬A ⋁ B)", "(¬A ⋁ B) ⋀ A", "A ⋀ (¬A ⋁ B) ⋀ A"]);
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use nom::error::Error;
|
||||
use nom::IResult;
|
||||
use nom::sequence::{pair, preceded};
|
||||
|
||||
use crate::{and, atomic, implies, not, or};
|
||||
use crate::expressions::expression::Expression;
|
||||
use crate::expressions::helpers::{and, atomic, implies, not, or};
|
||||
use crate::parsing::utils::{exhausted, IntoResult, parenthesized, trim};
|
||||
|
||||
pub fn parse_expression(input: &str) -> Result<Expression, nom::Err<Error<&str>>> {
|
||||
@ -26,7 +26,7 @@ fn _parse_expression(input: &str) -> IResult<&str, Expression> {
|
||||
fn left_hand_side(input: &str) -> IResult<&str, Expression> {
|
||||
alt((
|
||||
value,
|
||||
not,
|
||||
not_expression,
|
||||
parenthesized(complete_expression)
|
||||
))(input)
|
||||
}
|
||||
@ -45,10 +45,10 @@ fn expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str,
|
||||
fn operator_combinators(expression: Expression) -> impl Fn(&str) -> IResult<&str, Expression> {
|
||||
move |input: &str| {
|
||||
alt((
|
||||
implies(expression.clone()),
|
||||
or(expression.clone()),
|
||||
and(expression.clone()),
|
||||
not,
|
||||
implication_expression(expression.clone()),
|
||||
or_expression(expression.clone()),
|
||||
and_expression(expression.clone()),
|
||||
not_expression,
|
||||
))(input)
|
||||
}
|
||||
}
|
||||
@ -58,23 +58,23 @@ fn complete_expression(input: &str) -> IResult<&str, Expression> {
|
||||
operator_combinators(atomic.clone())(remaining)
|
||||
}
|
||||
|
||||
fn and<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
fn and_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
move |input: &'a str| {
|
||||
preceded(
|
||||
trim(char('&')),
|
||||
left_hand_side,
|
||||
)(input).map(|(remaining, right)| {
|
||||
(remaining, and!(previous.clone(), right))
|
||||
(remaining, and(previous.clone(), right))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_and(input: &str) -> IResult<&str, Expression> {
|
||||
let (remaining, atomic) = value(input)?;
|
||||
and(atomic.clone())(remaining)
|
||||
and_expression(atomic.clone())(remaining)
|
||||
}
|
||||
|
||||
fn or<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
fn or_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
move |input: &'a str| {
|
||||
preceded(
|
||||
trim(char('|')),
|
||||
@ -83,17 +83,17 @@ fn or<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expressi
|
||||
left_hand_side,
|
||||
)),
|
||||
)(input).map(|(remaining, right)| {
|
||||
(remaining, or!(previous.clone(), right))
|
||||
(remaining, or(previous.clone(), right))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_or(input: &str) -> IResult<&str, Expression> {
|
||||
let (remaining, atomic) = value(input)?;
|
||||
or(atomic.clone())(remaining)
|
||||
or_expression(atomic.clone())(remaining)
|
||||
}
|
||||
|
||||
fn implies<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
fn implication_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
|
||||
move |input: &'a str| {
|
||||
preceded(
|
||||
trim(tag("=>")),
|
||||
@ -103,17 +103,17 @@ fn implies<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Exp
|
||||
left_hand_side,
|
||||
)),
|
||||
)(input).map(|(remaining, right)| {
|
||||
(remaining, implies!(previous.clone(), right))
|
||||
(remaining, implies(previous.clone(), right))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn not(input: &str) -> IResult<&str, Expression> {
|
||||
fn not_expression(input: &str) -> IResult<&str, Expression> {
|
||||
preceded(
|
||||
char('!'),
|
||||
left_hand_side,
|
||||
)(input).map(|(remaining, right)| {
|
||||
(remaining, not!(right))
|
||||
(remaining, not(right))
|
||||
})
|
||||
}
|
||||
|
||||
@ -124,39 +124,39 @@ fn value(input: &str) -> IResult<&str, Expression> {
|
||||
)(input)
|
||||
.map(|(remaining, (first, rest))| {
|
||||
let value = format!("{first}{rest}");
|
||||
(remaining, atomic!(value))
|
||||
(remaining, atomic(value))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{and, atomic, implies, not, or};
|
||||
use crate::expressions::helpers::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let input = "a & b => c";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(implies!(and!(atomic!("a"), atomic!("b")), atomic!("c"))));
|
||||
assert_eq!(result, Ok(implies(and(atomic("a"), atomic("b")), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_complex() {
|
||||
let input = "a => b | !(!c | d & e) => b";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(implies!(
|
||||
implies!(
|
||||
atomic!("a"),
|
||||
or!(
|
||||
atomic!("b"),
|
||||
not!(
|
||||
or!(
|
||||
not!(atomic!("c")),
|
||||
and!(atomic!("d"), atomic!("e"))
|
||||
assert_eq!(result, Ok(implies(
|
||||
implies(
|
||||
atomic("a"),
|
||||
or(
|
||||
atomic("b"),
|
||||
not(
|
||||
or(
|
||||
not(atomic("c")),
|
||||
and(atomic("d"), atomic("e")),
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
atomic!("b")
|
||||
atomic("b"),
|
||||
)));
|
||||
}
|
||||
|
||||
@ -164,98 +164,98 @@ mod tests {
|
||||
fn test_operator_weight() {
|
||||
let input = "A & B | C => D | E & F";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(implies!(or!(and!(atomic!("A"), atomic!("B")), atomic!("C")), or!(atomic!("D"), and!(atomic!("E"), atomic!("F"))))));
|
||||
assert_eq!(result, Ok(implies(or(and(atomic("A"), atomic("B")), atomic("C")), or(atomic("D"), and(atomic("E"), atomic("F"))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implies_chain() {
|
||||
let input = "a => b => c";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(implies!(implies!(atomic!("a"), atomic!("b")), atomic!("c"))));
|
||||
assert_eq!(result, Ok(implies(implies(atomic("a"), atomic("b")), atomic("c"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_parentheses() {
|
||||
let input = "a & (b => c)";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(and!(atomic!("a"), implies!(atomic!("b"), atomic!("c")))));
|
||||
assert_eq!(result, Ok(and(atomic("a"), implies(atomic("b"), atomic("c")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_not() {
|
||||
let input = "!a";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(not!(atomic!("a"))));
|
||||
assert_eq!(result, Ok(not(atomic("a"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_not_parentheses() {
|
||||
let input = "!(a & b)";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(not!(and!(atomic!("a"), atomic!("b")))));
|
||||
assert_eq!(result, Ok(not(and(atomic("a"), atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_with_not_inside_and() {
|
||||
let input = "a & !b";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(and!(atomic!("a"), not!(atomic!("b")))));
|
||||
assert_eq!(result, Ok(and(atomic("a"), not(atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_with_not_inside_or() {
|
||||
let input = "a | !b";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(or!(atomic!("a"), not!(atomic!("b")))));
|
||||
assert_eq!(result, Ok(or(atomic("a"), not(atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_with_not_inside_implies() {
|
||||
let input = "a => !b";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(implies!(atomic!("a"), not!(atomic!("b")))));
|
||||
assert_eq!(result, Ok(implies(atomic("a"), not(atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_with_not_inside_parentheses() {
|
||||
let input = "a & !(b | c)";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c"))))));
|
||||
assert_eq!(result, Ok(and(atomic("a"), not(or(atomic("b"), atomic("c"))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_even_not() {
|
||||
let input = "!!!!a";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(not!(not!(not!(not!(atomic!("a")))))));
|
||||
assert_eq!(result, Ok(not(not(not(not(atomic("a")))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_odd_not() {
|
||||
let input = "!!!!!a";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(not!(not!(not!(not!(not!(atomic!("a"))))))));
|
||||
assert_eq!(result, Ok(not(not(not(not(not(atomic("a"))))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_atomic() {
|
||||
let input = "a";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(atomic!("a")));
|
||||
assert_eq!(result, Ok(atomic("a")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_atomic_with_underscore() {
|
||||
let input = "a_b";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(atomic!("a_b")));
|
||||
assert_eq!(result, Ok(atomic("a_b")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_atomic_with_digits() {
|
||||
let input = "a1";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(atomic!("a1")));
|
||||
assert_eq!(result, Ok(atomic("a1")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -269,83 +269,83 @@ mod tests {
|
||||
fn test_or_chain() {
|
||||
let input = "a | b | c | d | e | f | g";
|
||||
let result = super::parse_expression(input);
|
||||
assert_eq!(result, Ok(or!(or!(or!(or!(or!(or!(atomic!("a"), atomic!("b")), atomic!("c")), atomic!("d")), atomic!("e")), atomic!("f")), atomic!("g"))));
|
||||
assert_eq!(result, Ok(or(or(or(or(or(or(atomic("a"), atomic("b")), atomic("c")), atomic("d")), atomic("e")), atomic("f")), atomic("g"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression() {
|
||||
let input = "a";
|
||||
let result = super::_parse_expression(input);
|
||||
assert_eq!(result, Ok(("", atomic!("a"))));
|
||||
assert_eq!(result, Ok(("", atomic("a"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_and() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & b";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", and!(atomic!("a"), atomic!("b")))));
|
||||
assert_eq!(result, Ok(("", and(atomic("a"), atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_and_or() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & b | c";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), atomic!("c")))));
|
||||
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), atomic("c")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_and_or_implies() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & b | c => d";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", implies!(or!(and!(atomic!("a"), atomic!("b")), atomic!("c")), atomic!("d")))));
|
||||
assert_eq!(result, Ok(("", implies(or(and(atomic("a"), atomic("b")), atomic("c")), atomic("d")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_parentheses_or() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & (b | c) => d";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", implies!(and!(atomic!("a"), or!(atomic!("b"), atomic!("c"))), atomic!("d")))));
|
||||
assert_eq!(result, Ok(("", implies(and(atomic("a"), or(atomic("b"), atomic("c"))), atomic("d")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_parentheses_and() {
|
||||
let input = "(a & b) | (c & d)";
|
||||
let result = super::_parse_expression(input);
|
||||
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), and!(atomic!("c"), atomic!("d"))))));
|
||||
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), and(atomic("c"), atomic("d"))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_parentheses_implies() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & b | (c => d)";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), implies!(atomic!("c"), atomic!("d"))))));
|
||||
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), implies(atomic("c"), atomic("d"))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expression_nested_parentheses() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " & (b | (c => d))";
|
||||
let result = super::expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", and!(atomic!("a"), or!(atomic!("b"), implies!(atomic!("c"), atomic!("d")))))));
|
||||
assert_eq!(result, Ok(("", and(atomic("a"), or(atomic("b"), implies(atomic("c"), atomic("d")))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_or() {
|
||||
let expression = atomic!("a");
|
||||
let expression = atomic("a");
|
||||
let input = " | b";
|
||||
let result = super::or(expression)(input);
|
||||
assert_eq!(result, Ok(("", or!(atomic!("a"), atomic!("b")))));
|
||||
let result = super::or_expression(expression)(input);
|
||||
assert_eq!(result, Ok(("", or(atomic("a"), atomic("b")))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_or_parentheses() {
|
||||
let input = "(a | b)";
|
||||
let result = super::_parse_expression(input);
|
||||
assert_eq!(result, Ok(("", or!(atomic!("a"), atomic!("b")))));
|
||||
assert_eq!(result, Ok(("", or(atomic("a"), atomic("b")))));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user