diff --git a/src/adder.rs b/src/adder.rs index 2dfc4c6..09eadf3 100644 --- a/src/adder.rs +++ b/src/adder.rs @@ -12,4 +12,3 @@ pub fn adder(a: u32, b: u32) -> u32 { } result } - diff --git a/src/ast.rs b/src/ast.rs index e0efaae..bad9321 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,20 +1,24 @@ +mod tests; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Token { Negation, Conjunction, Disjunction, ExclusiveDisjunction, MaterialCondition, - LogicalEquivalence + LogicalEquivalence, } -#[derive(Debug, Clone)] -pub enum Node { +#[derive(Debug, Clone, PartialEq)] +pub enum Node +where + T: Clone + std::fmt::Debug, +{ Leaf(T), Unary { operator: Token, - operand: Box> + operand: Box>, }, Binary { operator: Token, @@ -23,7 +27,285 @@ pub enum Node { }, } -pub fn add_unary_node(stack: &mut Vec>, token: Token) { +impl Node +where + T: Clone + std::fmt::Debug, +{ + fn negation_conjunction_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + } + } + + fn negation_disjunction_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + } + } + + fn negation_material_condition_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(*lhs.clone()), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + } + } + + fn material_condition_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(*rhs.clone()), + } + } + + fn negation_exclusive_disjunction_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + }), + rhs: Box::new(Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(*lhs.clone()), + rhs: Box::new(*rhs.clone()), + }), + } + } + + fn exclusive_disjunction_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(*lhs.clone()), + rhs: Box::new(*rhs.clone()), + }), + rhs: Box::new(Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + }), + } + } + + fn logical_equivalence_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + rhs: Box::new(*rhs.clone()), + }), + rhs: Box::new(Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + rhs: Box::new(*lhs.clone()), + }), + } + } + + fn negation_logical_equivalence_to_nnf(lhs: &Box>, rhs: &Box>) -> Node { + Node::Binary { + operator: Token::Disjunction, + lhs: Box::new(Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(*lhs.clone()), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*rhs.clone()), + }), + }), + rhs: Box::new(Node::Binary { + operator: Token::Conjunction, + lhs: Box::new(*rhs.clone()), + rhs: Box::new(Node::Unary { + operator: Token::Negation, + operand: Box::new(*lhs.clone()), + }), + }), + } + } + + pub fn simplify(&mut self) { + match self { + Node::Unary { operator, operand } if *operator == Token::Negation => match &**operand { + Node::Unary { + operator: inner_op, + operand: inner_operand, + } if *inner_op == Token::Negation => { + *self = *inner_operand.clone(); + self.simplify(); + } + Node::Binary { + operator: inner_op, + lhs: inner_lhs, + rhs: inner_rhs, + } => { + *self = match *inner_op { + Token::Conjunction => { + Self::negation_conjunction_to_nnf(inner_lhs, inner_rhs) + } + Token::Disjunction => { + Self::negation_disjunction_to_nnf(inner_lhs, inner_rhs) + } + Token::ExclusiveDisjunction => { + Self::negation_exclusive_disjunction_to_nnf(inner_lhs, inner_rhs) + } + Token::LogicalEquivalence => { + Self::negation_logical_equivalence_to_nnf(inner_lhs, inner_rhs) + } + Token::MaterialCondition => { + Self::negation_material_condition_to_nnf(inner_lhs, inner_rhs) + } + _ => unreachable!(), + }; + self.simplify(); + } + _ => (), + }, + Node::Binary { + operator, lhs, rhs, .. + } => { + if let Some(node) = match *operator { + Token::ExclusiveDisjunction => { + Some(Self::exclusive_disjunction_to_nnf(lhs, rhs)) + } + Token::LogicalEquivalence => Some(Self::logical_equivalence_to_nnf(lhs, rhs)), + Token::MaterialCondition => Some(Self::material_condition_to_nnf(lhs, rhs)), + _ => None, + } { + *self = node.clone(); + self.simplify(); + } else { + lhs.simplify(); + rhs.simplify(); + } + } + _ => (), + } + } +} + +impl Node { + pub fn parse_formula(formula: &str) -> Node { + let mut stack = vec![]; + for c in formula.chars() { + match c { + '0' => stack.push(Node::Leaf(false)), + '1' => stack.push(Node::Leaf(true)), + '!' => add_unary_node(&mut stack, Token::Negation), + '&' => add_binary_node(&mut stack, Token::Conjunction), + '|' => add_binary_node(&mut stack, Token::Disjunction), + '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), + '>' => add_binary_node(&mut stack, Token::MaterialCondition), + '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), + _ => panic!("Error: {} is not a valid character", c), + } + } + stack.pop().unwrap() + } + + pub fn ast_to_formula(ast: &Node) -> String { + let mut str = String::from(""); + match ast { + Node::Unary { operator, operand } => { + str.push_str(Self::ast_to_formula(operand).as_str()); + str.push(token_to_char(*operator)); + } + Node::Binary { operator, lhs, rhs } => { + str.push_str(Self::ast_to_formula(lhs).as_str()); + str.push_str(Self::ast_to_formula(rhs).as_str()); + str.push(token_to_char(*operator)); + } + Node::Leaf(b) => str.push(bool_to_char(*b)), + }; + str + } +} + +impl Node { + pub fn parse_formula(formula: &str) -> Node { + let mut stack = vec![]; + for c in formula.chars() { + match c { + 'A'..='Z' => stack.push(Node::Leaf(c)), + '!' => add_unary_node(&mut stack, Token::Negation), + '&' => add_binary_node(&mut stack, Token::Conjunction), + '|' => add_binary_node(&mut stack, Token::Disjunction), + '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), + '>' => add_binary_node(&mut stack, Token::MaterialCondition), + '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), + _ => panic!("Error: {} is not a valid character", c), + } + } + stack.pop().unwrap() + } + + pub fn ast_to_formula(ast: &Node) -> String { + let mut str = String::from(""); + match ast { + Node::Unary { operator, operand } => { + str.push_str(Self::ast_to_formula(operand).as_str()); + str.push(token_to_char(*operator)); + } + Node::Binary { operator, lhs, rhs } => { + str.push_str(Self::ast_to_formula(lhs).as_str()); + str.push_str(Self::ast_to_formula(rhs).as_str()); + str.push(token_to_char(*operator)); + } + Node::Leaf(c) => str.push(*c), + }; + str + } +} + +pub fn add_unary_node(stack: &mut Vec>, token: Token) +where + T: Clone + std::fmt::Debug, +{ let operand = Box::new(stack.pop().unwrap()); stack.push(Node::Unary { operator: token, @@ -31,12 +313,33 @@ pub fn add_unary_node(stack: &mut Vec>, token: Token) { }); } -pub fn add_binary_node(stack: &mut Vec>, token: Token) { - let lhs = Box::new(stack.pop().unwrap()); +pub fn add_binary_node(stack: &mut Vec>, token: Token) +where + T: Clone + std::fmt::Debug, +{ let rhs = Box::new(stack.pop().unwrap()); + let lhs = Box::new(stack.pop().unwrap()); stack.push(Node::Binary { operator: token, lhs, - rhs + rhs, }); -} \ No newline at end of file +} + +fn bool_to_char(b: bool) -> char { + match b { + false => '0', + true => '1', + } +} + +fn token_to_char(token: Token) -> char { + match token { + Token::Negation => '!', + Token::Conjunction => '&', + Token::Disjunction => '|', + Token::ExclusiveDisjunction => '^', + Token::MaterialCondition => '>', + Token::LogicalEquivalence => '=', + } +} diff --git a/src/ast/tests.rs b/src/ast/tests.rs index 085d695..23df1a9 100644 --- a/src/ast/tests.rs +++ b/src/ast/tests.rs @@ -1,13 +1,74 @@ #[cfg(test)] mod tests { + use crate::ast::{add_binary_node, Node, Token}; + #[test] fn add_nodes() { - let stack = vec![Node::Leaf(true), Node::Leaf(false)]; + let mut stack = vec![Node::Leaf(true), Node::Leaf(false)]; let rhs = vec![Node::Binary { operator: Token::Conjunction, lhs: Box::new(Node::Leaf(true)), - rhs: Box::new(Node::Leaf(false)) + rhs: Box::new(Node::Leaf(false)), }]; - assert_eq!(add_binary_node(&mut stack, Token::Conjunction), rhs); + add_binary_node(&mut stack, Token::Conjunction); + assert_eq!(stack, rhs); } -} \ No newline at end of file + + #[test] + fn ast_to_formula_bool() { + let formula = "01&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "01&00|&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "01&00|&11^&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "01&00|&11=^"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "01&00|&0!="; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + } + + #[test] + fn ast_to_formula_char() { + let formula = "AB&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "AB&CD|&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "AB&AC|&DE^&"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "AB&CD|&EF=^"; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + let formula = "AB&CD|&E!="; + assert_eq!( + Node::::ast_to_formula(&Node::::parse_formula(formula)), + formula + ); + } +} diff --git a/src/boolean_evaluation.rs b/src/boolean_evaluation.rs index 18f89e7..a61b599 100644 --- a/src/boolean_evaluation.rs +++ b/src/boolean_evaluation.rs @@ -1,24 +1,6 @@ mod tests; -use crate::ast::{ Token, Node, add_unary_node, add_binary_node }; - -fn parse_formula(formula: &str) -> Node { - let mut stack = vec![]; - for c in formula.chars() { - match c { - '0' => stack.push(Node::Leaf(false)), - '1' => stack.push(Node::Leaf(true)), - '!' => add_unary_node(&mut stack, Token::Negation), - '&' => add_binary_node(&mut stack, Token::Conjunction), - '|' => add_binary_node(&mut stack, Token::Disjunction), - '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), - '>' => add_binary_node(&mut stack, Token::MaterialCondition), - '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), - _ => panic!("Error: {} is not a valid character", c) - } - } - stack.pop().unwrap() -} +use crate::ast::{Node, Token}; fn compute(operator: Token, lhs: bool, rhs: bool) -> bool { match operator { @@ -27,7 +9,7 @@ fn compute(operator: Token, lhs: bool, rhs: bool) -> bool { Token::Disjunction => lhs | rhs, Token::ExclusiveDisjunction => lhs ^ rhs, Token::MaterialCondition => !(lhs && !rhs), - Token::LogicalEquivalence => lhs == rhs + Token::LogicalEquivalence => lhs == rhs, } } @@ -39,7 +21,7 @@ fn evaluate(tree: Node) -> bool { } } -fn eval_formula(formula: &str) -> bool { - let tree = parse_formula(formula); +pub fn eval_formula(formula: &str) -> bool { + let tree = Node::::parse_formula(formula); evaluate(tree) -} \ No newline at end of file +} diff --git a/src/boolean_evaluation/tests.rs b/src/boolean_evaluation/tests.rs index daa6150..1bf95a3 100644 --- a/src/boolean_evaluation/tests.rs +++ b/src/boolean_evaluation/tests.rs @@ -15,7 +15,6 @@ mod tests { fn only_unary() { assert_eq!(eval_formula("1!"), false); assert_eq!(eval_formula("0!"), true); - } #[test] @@ -23,8 +22,10 @@ mod tests { assert_eq!(eval_formula("1!!"), true); assert_eq!(eval_formula("0!!"), false); assert_eq!(eval_formula("0!!!"), true); - assert_eq!(eval_formula("1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"), true); - + assert_eq!( + eval_formula("1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"), + true + ); } #[test] diff --git a/src/gray_code.rs b/src/gray_code.rs index 108ea83..b8126ad 100644 --- a/src/gray_code.rs +++ b/src/gray_code.rs @@ -10,4 +10,3 @@ pub fn gray_code(n: u32) -> u32 { } result } - diff --git a/src/gray_code/tests.rs b/src/gray_code/tests.rs index 7903e01..6b4d321 100644 --- a/src/gray_code/tests.rs +++ b/src/gray_code/tests.rs @@ -20,6 +20,6 @@ mod tests { fn msb_u32() { assert_eq!(gray_code(3_000_000_000), 3_954_733_312); assert_eq!(gray_code(2_147_483_648), 3_221_225_472); - assert_eq!(gray_code(4_294_967_295), 2_147_483_648); + assert_eq!(gray_code(4_294_967_295), 2_147_483_648); } } diff --git a/src/main.rs b/src/main.rs index e336b0e..2ca1032 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ mod adder; -mod multiplier; -mod gray_code; -mod boolean_evaluation; -mod truth_table; mod ast; +mod boolean_evaluation; +mod gray_code; +mod multiplier; +mod negation_normal_form; +mod truth_table; use gray_code::gray_code; use truth_table::print_truth_table; @@ -20,4 +21,5 @@ fn main() { println!("{}", gray_code(7)); println!("{}", gray_code(3)); print_truth_table("ZFG|&"); + print_truth_table("ABC|&"); } diff --git a/src/multiplier.rs b/src/multiplier.rs index 2e208af..2201b18 100644 --- a/src/multiplier.rs +++ b/src/multiplier.rs @@ -4,7 +4,7 @@ use crate::adder::adder; pub fn multiplier(a: u32, b: u32) -> u32 { let mut result = 0; - + for i in 0..32 { if a >> i & 1 == 1 { result = adder(result, b << i); diff --git a/src/negation_normal_form.rs b/src/negation_normal_form.rs new file mode 100644 index 0000000..2a63e8d --- /dev/null +++ b/src/negation_normal_form.rs @@ -0,0 +1,9 @@ +mod tests; + +use crate::ast::Node; + +fn negation_normal_form(formula: &str) -> String { + let mut ast = Node::::parse_formula(formula); + ast.simplify(); + Node::::ast_to_formula(&ast) +} diff --git a/src/negation_normal_form/tests.rs b/src/negation_normal_form/tests.rs new file mode 100644 index 0000000..11a1f9c --- /dev/null +++ b/src/negation_normal_form/tests.rs @@ -0,0 +1,68 @@ +#[cfg(test)] +mod tests { + use crate::negation_normal_form::negation_normal_form; + + #[test] + fn double_neg_simple() { + assert_eq!(negation_normal_form("A!!"), "A"); + assert_eq!(negation_normal_form("B!!!!"), "B"); + assert_eq!(negation_normal_form("C!!!"), "C!"); + assert_eq!(negation_normal_form("D!!!!!!!!!!!!!!!!"), "D"); + assert_eq!(negation_normal_form("E!!!!!!!!!!!!!!!"), "E!"); + } + + #[test] + fn double_neg() { + assert_eq!(negation_normal_form("A!!Z&"), "AZ&"); + assert_eq!(negation_normal_form("B!!Z!!&"), "BZ&"); + assert_eq!(negation_normal_form("C!!Z!&"), "CZ!&"); + assert_eq!(negation_normal_form("D!!Z&!!!!"), "DZ&"); + assert_eq!(negation_normal_form("E!!Z!!!!&!!"), "EZ&"); + assert_eq!(negation_normal_form("F!!Z!&!!!!!!!!"), "FZ!&"); + } + + #[test] + fn neg_and() { + assert_eq!(negation_normal_form("AZ&!"), "A!Z!|"); + assert_eq!(negation_normal_form("BZ&!BZ&!&"), "B!Z!|B!Z!|&"); + assert_eq!(negation_normal_form("CZ&!CZ&!&!"), "CZ&CZ&|"); + } + + #[test] + fn neg_or() { + assert_eq!(negation_normal_form("AZ|!"), "A!Z!&"); + assert_eq!(negation_normal_form("BZ|!BZ|!|"), "B!Z!&B!Z!&|"); + assert_eq!(negation_normal_form("CZ|!CZ|!|!"), "CZ|CZ|&"); + } + + #[test] + fn material_condition() { + assert_eq!(negation_normal_form("AZ>"), "A!Z|"); + assert_eq!(negation_normal_form("BZ>!"), "BZ!&"); + } + + #[test] + fn exclusive_disjunction() { + assert_eq!(negation_normal_form("AZ^"), "AZ|A!Z!|&"); + assert_eq!(negation_normal_form("BZ^!"), "B!Z!&BZ&|"); + } + + #[test] + fn logical_equivalence() { + assert_eq!(negation_normal_form("AZ="), "A!Z|Z!A|&"); + assert_eq!(negation_normal_form("BZ=!"), "BZ!&ZB!&|"); + } + + #[test] + fn subject_tests() { + assert_eq!(negation_normal_form("AB&!"), "A!B!|"); + assert_eq!(negation_normal_form("AB|!"), "A!B!&"); + assert_eq!(negation_normal_form("AB>"), "A!B|"); + assert_eq!(negation_normal_form("AB|C&!"), "A!B!&C!|"); + } + + #[test] + fn complex_tests() { + assert_eq!("A", "A"); + } +} diff --git a/src/truth_table.rs b/src/truth_table.rs index c21fe53..462fbb9 100644 --- a/src/truth_table.rs +++ b/src/truth_table.rs @@ -1,7 +1,7 @@ mod tests; +use crate::ast::{add_binary_node, add_unary_node, Node, Token}; use std::collections::HashSet; -use crate::ast::{ Token, Node, add_unary_node, add_binary_node }; fn parse_formula(formula: &str) -> Node { let mut stack = vec![]; @@ -15,7 +15,7 @@ fn parse_formula(formula: &str) -> Node { '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), '>' => add_binary_node(&mut stack, Token::MaterialCondition), '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), - _ => panic!("Error: {} is not a valid character", c) + _ => panic!("Error: {} is not a valid character", c), } } stack.pop().unwrap() @@ -28,7 +28,7 @@ fn compute(operator: Token, lhs: bool, rhs: bool) -> bool { Token::Disjunction => lhs | rhs, Token::ExclusiveDisjunction => lhs ^ rhs, Token::MaterialCondition => !(lhs && !rhs), - Token::LogicalEquivalence => lhs == rhs + Token::LogicalEquivalence => lhs == rhs, } } @@ -45,7 +45,9 @@ fn get_hashset(formula: &str) -> HashSet { for c in formula.chars() { match c { - 'A'..='Z' => { hashset.insert(c); }, + 'A'..='Z' => { + hashset.insert(c); + } '!' | '&' | '|' | '^' | '>' | '=' => (), _ => panic!("Error: {} is not a valid character", c), } @@ -61,8 +63,16 @@ fn eval_formula(formula: &str) -> bool { fn recursive_fn(formula: &str, mut vec: Vec, format: String) { let char = vec.pop(); if let Some(c) = char { - recursive_fn(formula.replace(c, "0").as_str(), vec.clone(), format!("{format} 0 |")); - recursive_fn(formula.replace(c, "1").as_str(), vec.clone(), format!("{format} 1 |")); + recursive_fn( + formula.replace(c, "0").as_str(), + vec.clone(), + format!("{format} 0 |"), + ); + recursive_fn( + formula.replace(c, "1").as_str(), + vec.clone(), + format!("{format} 1 |"), + ); } else { match eval_formula(formula) { false => println!("{format} 0 |"), @@ -88,5 +98,5 @@ pub fn print_truth_table(formula: &str) { println!("{format}"); println!("{separator}"); recursive_fn(formula, vec.into_iter().rev().collect(), String::from("|")); - // call eval formula a lot -} \ No newline at end of file + // call eval formula a lot +} diff --git a/src/truth_table/tests.rs b/src/truth_table/tests.rs index e092e43..79fcc21 100644 --- a/src/truth_table/tests.rs +++ b/src/truth_table/tests.rs @@ -23,6 +23,6 @@ mod tests { #[test] fn big() { - print_truth_table("AB&CD&&EF&GH&&&IJ&KL&&MN&OP&&&QR&ST&&UV&WX&&&YZ&&&") + //print_truth_table("AB&CD&&EF&GH&&&IJ&KL&&MN&OP&&&QR&ST&&UV&WX&&&YZ&&&") } }