use std::collections::HashSet;
use symbolic_mgu::bool_eval::{BooleanSimpleNode, BooleanSimpleOp};
use symbolic_mgu::search::{get_iterator, TermSearchStaticState};
use symbolic_mgu::{
EnumTerm, EnumTermFactory, MetavariableFactory, MguError, Node, SimpleType, Term,
WideMetavariable, WideMetavariableFactory,
};
type TestVar = WideMetavariable;
type TestNode = BooleanSimpleNode<SimpleType>;
type TestTerm = EnumTerm<SimpleType, TestVar, TestNode>;
type TestFactory = EnumTermFactory<SimpleType, TestVar, TestNode>;
fn eval_bool_term_truth_table(
term: &TestTerm,
var_a: &TestVar,
var_b: &TestVar,
) -> Result<u8, MguError> {
if let Some(var) = term.get_metavariable() {
if var == *var_a {
Ok(0b1010u8) } else if var == *var_b {
Ok(0b1100u8) } else {
Err(MguError::BooleanEvaluationFailed {
reason: format!("Unknown variable: {:?}", var),
})
}
} else if let Some(node) = term.get_node() {
let children: Vec<_> = term.get_children().collect();
match node.to_boolean_op() {
Some(BooleanSimpleOp::True0) => Ok(0b1111u8), Some(BooleanSimpleOp::False0) => Ok(0b0000u8), Some(op) => {
let child_tables: Result<Vec<u8>, _> = children
.iter()
.map(|c| eval_bool_term_truth_table(c, var_a, var_b))
.collect();
let child_tables = child_tables?;
match op.get_arity() {
0 => Err(MguError::BooleanEvaluationFailed {
reason: format!("Nullary operator {:?} should have been constant", op),
}),
1 if child_tables.len() == 1 => op
.eval1::<u8, u8, 2>(&child_tables[0])
.ok_or_else(|| MguError::BooleanEvaluationFailed {
reason: format!("eval1 failed for {:?}", op),
}),
2 if child_tables.len() == 2 => op
.eval2::<u8, u8, 2>(&child_tables[0], &child_tables[1])
.ok_or_else(|| MguError::BooleanEvaluationFailed {
reason: format!("eval2 failed for {:?}", op),
}),
_ => Err(MguError::BooleanEvaluationFailed {
reason: format!(
"Arity mismatch: op {:?} has arity {}, got {} children",
op,
op.get_arity(),
child_tables.len()
),
}),
}
}
None => Err(MguError::BooleanEvaluationFailed {
reason: format!("Node is not a Boolean operation: {:?}", node),
}),
}
} else {
Err(MguError::BooleanEvaluationFailed {
reason: "Term is neither variable nor node".to_string(),
})
}
}
fn evaluate_2var_truth_table(
term: &TestTerm,
var_a: &TestVar,
var_b: &TestVar,
_factory: &TestFactory,
) -> Result<u8, MguError> {
let truth_table = eval_bool_term_truth_table(term, var_a, var_b)?;
Ok(truth_table & 0b1111)
}
fn test_functional_completeness(ops: &[BooleanSimpleOp], max_depth: usize) -> usize {
let vf = WideMetavariableFactory();
let var_a = vf
.list_metavariables_by_type(&SimpleType::Boolean)
.next()
.unwrap();
let var_b = vf
.list_metavariables_by_type(&SimpleType::Boolean)
.nth(1)
.unwrap();
let vars = vec![var_a, var_b];
let nodes: Vec<TestNode> = ops
.iter()
.map(|&op| BooleanSimpleNode::from_op(op))
.collect();
let tf = TestFactory::new();
let state =
TermSearchStaticState::new(tf, &nodes, &vars).expect("Failed to create search state");
let mut found_functions: HashSet<u8> = HashSet::new();
for depth in 0..=max_depth {
let iter =
get_iterator(&state, SimpleType::Boolean, depth).expect("Failed to create iterator");
let mut term_count = 0;
for term in iter {
term_count += 1;
match evaluate_2var_truth_table(&term, &var_a, &var_b, state.get_term_factory()) {
Ok(truth_code) => {
found_functions.insert(truth_code);
}
Err(_) => {
continue;
}
}
if found_functions.len() == 16 {
eprintln!(
" Depth {}: Found all 16 functions after {} terms",
depth, term_count
);
return depth;
}
}
eprintln!(
" Depth {}: {} terms generated, {} functions found so far",
depth,
term_count,
found_functions.len()
);
if found_functions.len() == 16 {
return depth;
}
}
assert_eq!(
found_functions.len(),
16,
"Operator set {:?} did not generate all 16 Boolean functions by depth {}. Found {} functions: {:?}",
ops,
max_depth,
found_functions.len(),
{
let mut sorted: Vec<u8> = found_functions.iter().copied().collect();
sorted.sort();
sorted
}
);
max_depth
}
#[test]
fn sheffer_nand_is_functionally_complete() {
eprintln!("\n=== Testing NAND functional completeness ===");
let depth = test_functional_completeness(&[BooleanSimpleOp::NotAndAB2], 3);
eprintln!("NAND: All 16 functions found by depth {}\n", depth);
assert!(depth <= 5, "NAND should find all 16 functions by depth 5");
}
#[test]
fn sheffer_nor_is_functionally_complete() {
eprintln!("\n=== Testing NOR functional completeness ===");
let depth = test_functional_completeness(&[BooleanSimpleOp::NotOrAB2], 3);
eprintln!("NOR: All 16 functions found by depth {}\n", depth);
assert!(depth <= 5, "NOR should find all 16 functions by depth 5");
}
#[test]
fn sheffer_symmetry() {
eprintln!("\n=== Testing NAND/NOR symmetry ===");
let nand_depth = test_functional_completeness(&[BooleanSimpleOp::NotAndAB2], 3);
let nor_depth = test_functional_completeness(&[BooleanSimpleOp::NotOrAB2], 3);
eprintln!("NAND depth: {}, NOR depth: {}\n", nand_depth, nor_depth);
assert_eq!(
nand_depth, nor_depth,
"NAND and NOR should require the same depth due to De Morgan symmetry"
);
}
#[test]
fn two_op_not_and() {
eprintln!("\n=== Testing {{NOT, AND}} ===");
let depth = test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::AndAB2], 4);
eprintln!("{{NOT, AND}}: All 16 functions found by depth {}\n", depth);
assert_eq!(depth, 4);
}
#[test]
fn two_op_not_or() {
eprintln!("\n=== Testing {{NOT, OR}} ===");
let depth = test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::OrAB2], 4);
eprintln!("{{NOT, OR}}: All 16 functions found by depth {}\n", depth);
assert_eq!(depth, 4);
}
#[test]
fn two_op_not_implies() {
eprintln!("\n=== Testing {{NOT, IMPLIES}} (¬, →) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::ImpliesAB2], 4);
eprintln!(
"{{NOT, IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_not_implied_by() {
eprintln!("\n=== Testing {{NOT, IMPLIED-BY}} (¬, ←) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::ImpliesBA2], 4);
eprintln!(
"{{NOT, IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_implies_false() {
eprintln!("\n=== Testing {{IMPLIES, FALSE}} (→, ⊥) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::ImpliesAB2, BooleanSimpleOp::False0], 4);
eprintln!(
"{{IMPLIES, FALSE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_implied_by_false() {
eprintln!("\n=== Testing {{IMPLIED-BY, FALSE}} (←, ⊥) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::ImpliesBA2, BooleanSimpleOp::False0], 4);
eprintln!(
"{{IMPLIED-BY, FALSE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_implies_xor() {
eprintln!("\n=== Testing {{IMPLIES, XOR}} (→, ⊕) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::ImpliesAB2, BooleanSimpleOp::XorAB2], 3);
eprintln!(
"{{IMPLIES, XOR}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 3);
}
#[test]
fn two_op_implied_by_xor() {
eprintln!("\n=== Testing {{IMPLIED-BY, XOR}} (←, ⊕) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::ImpliesBA2, BooleanSimpleOp::XorAB2], 3);
eprintln!(
"{{IMPLIED-BY, XOR}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 3);
}
#[test]
fn two_op_implies_not_implied_by() {
eprintln!("\n=== Testing {{IMPLIES, NOT-IMPLIED-BY}} (→, ↚) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::ImpliesAB2, BooleanSimpleOp::NotImpliesBA2],
2,
);
eprintln!(
"{{IMPLIES, NOT-IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn two_op_implied_by_not_implied_by() {
eprintln!("\n=== Testing {{IMPLIED-BY, NOT-IMPLIED-BY}} (←, ↚) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::ImpliesBA2, BooleanSimpleOp::NotImpliesBA2],
2,
);
eprintln!(
"{{IMPLIED-BY, NOT-IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn two_op_implies_not_implies() {
eprintln!("\n=== Testing {{IMPLIES, NOT-IMPLIES}} (→, ↛) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::ImpliesAB2, BooleanSimpleOp::NotImpliesAB2],
2,
);
eprintln!(
"{{IMPLIES, NOT-IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn two_op_implied_by_not_implies() {
eprintln!("\n=== Testing {{IMPLIED-BY, NOT-IMPLIES}} (←, ↛) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::ImpliesBA2, BooleanSimpleOp::NotImpliesAB2],
2,
);
eprintln!(
"{{IMPLIED-BY, NOT-IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn two_op_not_not_implied_by() {
eprintln!("\n=== Testing {{NOT, NOT-IMPLIED-BY}} (¬, ↚) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::NotImpliesBA2], 4);
eprintln!(
"{{NOT, NOT-IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_not_not_implies() {
eprintln!("\n=== Testing {{NOT, NOT-IMPLIES}} (¬, ↛) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::NotA1, BooleanSimpleOp::NotImpliesAB2], 4);
eprintln!(
"{{NOT, NOT-IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_true_not_implied_by() {
eprintln!("\n=== Testing {{TRUE, NOT-IMPLIED-BY}} (⊤, ↚) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::True0, BooleanSimpleOp::NotImpliesBA2], 4);
eprintln!(
"{{TRUE, NOT-IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_true_not_implies() {
eprintln!("\n=== Testing {{TRUE, NOT-IMPLIES}} (⊤, ↛) ===");
let depth =
test_functional_completeness(&[BooleanSimpleOp::True0, BooleanSimpleOp::NotImpliesAB2], 4);
eprintln!(
"{{TRUE, NOT-IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 4);
}
#[test]
fn two_op_biimp_not_implied_by() {
eprintln!("\n=== Testing {{BIIMP, NOT-IMPLIED-BY}} (↔, ↚) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::BiimpAB2, BooleanSimpleOp::NotImpliesBA2],
3,
);
eprintln!(
"{{BIIMP, NOT-IMPLIED-BY}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 3);
}
#[test]
fn two_op_biimp_not_implies() {
eprintln!("\n=== Testing {{BIIMP, NOT-IMPLIES}} (↔, ↛) ===");
let depth = test_functional_completeness(
&[BooleanSimpleOp::BiimpAB2, BooleanSimpleOp::NotImpliesAB2],
3,
);
eprintln!(
"{{BIIMP, NOT-IMPLIES}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 3);
}
#[test]
fn three_op_or_biimp_false() {
eprintln!("\n=== Testing {{OR, BIIMP, FALSE}} (∨, ↔, ⊥) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::BiimpAB2,
BooleanSimpleOp::False0,
],
2,
);
eprintln!(
"{{OR, BIIMP, FALSE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn three_op_or_biimp_xor() {
eprintln!("\n=== Testing {{OR, BIIMP, XOR}} (∨, ↔, ⊕) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::BiimpAB2,
BooleanSimpleOp::XorAB2,
],
2,
);
eprintln!(
"{{OR, BIIMP, XOR}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn three_op_or_xor_true() {
eprintln!("\n=== Testing {{OR, XOR, TRUE}} (∨, ⊕, ⊤) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::XorAB2,
BooleanSimpleOp::True0,
],
2,
);
eprintln!(
"{{OR, XOR, TRUE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn three_op_and_biimp_false() {
eprintln!("\n=== Testing {{AND, BIIMP, FALSE}} (∧, ↔, ⊥) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::BiimpAB2,
BooleanSimpleOp::False0,
],
2,
);
eprintln!(
"{{AND, BIIMP, FALSE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn three_op_and_biimp_xor() {
eprintln!("\n=== Testing {{AND, BIIMP, XOR}} (∧, ↔, ⊕) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::BiimpAB2,
BooleanSimpleOp::XorAB2,
],
2,
);
eprintln!(
"{{AND, BIIMP, XOR}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn three_op_and_xor_true() {
eprintln!("\n=== Testing {{AND, XOR, TRUE}} (∧, ⊕, ⊤) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::XorAB2,
BooleanSimpleOp::True0,
],
2,
);
eprintln!(
"{{AND, XOR, TRUE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn four_op_and_or_xor_true() {
eprintln!("\n=== Testing {{AND, OR, XOR, TRUE}} (∧, ∨, ⊕, ⊤) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::XorAB2,
BooleanSimpleOp::True0,
],
2,
);
eprintln!(
"{{AND, OR, XOR, TRUE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn four_op_not_and_or_xor() {
eprintln!("\n=== Testing {{NOT, AND, OR, XOR}} (¬, ∧, ∨, ⊕) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::NotA1,
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::XorAB2,
],
2,
);
eprintln!(
"{{NOT, AND, OR, XOR}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}
#[test]
fn five_op_not_and_or_xor_true() {
eprintln!("\n=== Testing {{NOT, AND, OR, XOR, TRUE}} (¬, ∧, ∨, ⊕, ⊤) ===");
let depth = test_functional_completeness(
&[
BooleanSimpleOp::NotA1,
BooleanSimpleOp::AndAB2,
BooleanSimpleOp::OrAB2,
BooleanSimpleOp::XorAB2,
BooleanSimpleOp::True0,
],
2,
);
eprintln!(
"{{NOT, AND, OR, XOR, TRUE}}: All 16 functions found by depth {}\n",
depth
);
assert_eq!(depth, 2);
}