use crate::proof::{Proof, ProofNode, ProofNodeId, ProofStep};
use std::collections::{HashSet, VecDeque};
pub trait ProofVisitor {
fn visit_node(&mut self, proof: &Proof, node: &ProofNode);
fn visit_axiom(&mut self, _proof: &Proof, _id: ProofNodeId, _conclusion: &str) {}
fn visit_inference(
&mut self,
_proof: &Proof,
_id: ProofNodeId,
_rule: &str,
_premises: &[ProofNodeId],
_conclusion: &str,
) {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalOrder {
PreOrder,
PostOrder,
BreadthFirst,
}
pub fn traverse<V: ProofVisitor>(proof: &Proof, visitor: &mut V, order: TraversalOrder) {
if let Some(root) = proof.root() {
let mut visited = HashSet::new();
match order {
TraversalOrder::PreOrder => traverse_pre_order(proof, root, visitor, &mut visited),
TraversalOrder::PostOrder => traverse_post_order(proof, root, visitor, &mut visited),
TraversalOrder::BreadthFirst => traverse_breadth_first(proof, visitor),
}
}
}
fn traverse_pre_order<V: ProofVisitor>(
proof: &Proof,
node_id: ProofNodeId,
visitor: &mut V,
visited: &mut HashSet<ProofNodeId>,
) {
if visited.contains(&node_id) {
return;
}
visited.insert(node_id);
if let Some(node) = proof.get_node(node_id) {
visitor.visit_node(proof, node);
match &node.step {
ProofStep::Axiom { conclusion } => {
visitor.visit_axiom(proof, node_id, conclusion);
}
ProofStep::Inference {
rule,
premises,
conclusion,
..
} => {
visitor.visit_inference(proof, node_id, rule, premises, conclusion);
for &premise in premises {
traverse_pre_order(proof, premise, visitor, visited);
}
}
}
}
}
fn traverse_post_order<V: ProofVisitor>(
proof: &Proof,
node_id: ProofNodeId,
visitor: &mut V,
visited: &mut HashSet<ProofNodeId>,
) {
if visited.contains(&node_id) {
return;
}
visited.insert(node_id);
if let Some(node) = proof.get_node(node_id) {
if let ProofStep::Inference { premises, .. } = &node.step {
for &premise in premises {
traverse_post_order(proof, premise, visitor, visited);
}
}
visitor.visit_node(proof, node);
match &node.step {
ProofStep::Axiom { conclusion } => {
visitor.visit_axiom(proof, node_id, conclusion);
}
ProofStep::Inference {
rule,
premises,
conclusion,
..
} => {
visitor.visit_inference(proof, node_id, rule, premises, conclusion);
}
}
}
}
fn traverse_breadth_first<V: ProofVisitor>(proof: &Proof, visitor: &mut V) {
if let Some(root) = proof.root() {
let mut queue = VecDeque::new();
let mut visited = HashSet::new();
queue.push_back(root);
visited.insert(root);
while let Some(node_id) = queue.pop_front() {
if let Some(node) = proof.get_node(node_id) {
visitor.visit_node(proof, node);
match &node.step {
ProofStep::Axiom { conclusion } => {
visitor.visit_axiom(proof, node_id, conclusion);
}
ProofStep::Inference {
rule,
premises,
conclusion,
..
} => {
visitor.visit_inference(proof, node_id, rule, premises, conclusion);
for &premise in premises {
if !visited.contains(&premise) {
visited.insert(premise);
queue.push_back(premise);
}
}
}
}
}
}
}
}
#[must_use]
pub fn topological_order(proof: &Proof) -> Vec<ProofNodeId> {
let mut order = Vec::new();
if let Some(root) = proof.root() {
let mut visited = HashSet::new();
collect_topological(proof, root, &mut order, &mut visited);
}
order
}
fn collect_topological(
proof: &Proof,
node_id: ProofNodeId,
order: &mut Vec<ProofNodeId>,
visited: &mut HashSet<ProofNodeId>,
) {
if visited.contains(&node_id) {
return;
}
visited.insert(node_id);
if let Some(node) = proof.get_node(node_id)
&& let ProofStep::Inference { premises, .. } = &node.step
{
for &premise in premises {
collect_topological(proof, premise, order, visited);
}
}
order.push(node_id);
}
#[must_use]
pub fn find_all_paths(proof: &Proof) -> Vec<Vec<ProofNodeId>> {
let mut paths = Vec::new();
if let Some(root) = proof.root() {
let mut current_path = Vec::new();
collect_paths(proof, root, &mut current_path, &mut paths);
}
paths
}
fn collect_paths(
proof: &Proof,
node_id: ProofNodeId,
current_path: &mut Vec<ProofNodeId>,
all_paths: &mut Vec<Vec<ProofNodeId>>,
) {
current_path.push(node_id);
if let Some(node) = proof.get_node(node_id) {
match &node.step {
ProofStep::Axiom { .. } => {
all_paths.push(current_path.clone());
}
ProofStep::Inference { premises, .. } => {
for &premise in premises {
collect_paths(proof, premise, current_path, all_paths);
}
}
}
}
current_path.pop();
}
#[derive(Debug, Default)]
pub struct NodeCounter {
pub axioms: usize,
pub inferences: usize,
}
impl ProofVisitor for NodeCounter {
fn visit_node(&mut self, _proof: &Proof, node: &ProofNode) {
match node.step {
ProofStep::Axiom { .. } => self.axioms += 1,
ProofStep::Inference { .. } => self.inferences += 1,
}
}
}
#[derive(Debug, Default)]
pub struct ConclusionCollector {
pub conclusions: Vec<String>,
}
impl ProofVisitor for ConclusionCollector {
fn visit_node(&mut self, _proof: &Proof, node: &ProofNode) {
let conclusion = match &node.step {
ProofStep::Axiom { conclusion } => conclusion.clone(),
ProofStep::Inference { conclusion, .. } => conclusion.clone(),
};
self.conclusions.push(conclusion);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_counter() {
let mut proof = Proof::new();
let p1 = proof.add_axiom("p");
let p2 = proof.add_axiom("q");
let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
let mut counter = NodeCounter::default();
traverse(&proof, &mut counter, TraversalOrder::PreOrder);
assert_eq!(counter.axioms, 2);
assert_eq!(counter.inferences, 1);
}
#[test]
fn test_conclusion_collector() {
let mut proof = Proof::new();
let p1 = proof.add_axiom("p");
let p2 = proof.add_axiom("q");
let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
let mut collector = ConclusionCollector::default();
traverse(&proof, &mut collector, TraversalOrder::PreOrder);
assert_eq!(collector.conclusions.len(), 3);
assert!(collector.conclusions.contains(&"p".to_string()));
assert!(collector.conclusions.contains(&"q".to_string()));
assert!(collector.conclusions.contains(&"(and p q)".to_string()));
}
#[test]
fn test_topological_order() {
let mut proof = Proof::new();
let p1 = proof.add_axiom("p");
let p2 = proof.add_axiom("q");
let p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
let order = topological_order(&proof);
assert_eq!(order.len(), 3);
assert_eq!(order[order.len() - 1], p3);
}
#[test]
fn test_find_all_paths() {
let mut proof = Proof::new();
let p1 = proof.add_axiom("p");
let p2 = proof.add_axiom("q");
let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
let paths = find_all_paths(&proof);
assert_eq!(paths.len(), 2); }
#[test]
fn test_traversal_orders() {
let mut proof = Proof::new();
let p1 = proof.add_axiom("p");
let p2 = proof.add_axiom("q");
let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
let mut counter = NodeCounter::default();
traverse(&proof, &mut counter, TraversalOrder::PreOrder);
assert_eq!(counter.axioms, 2);
let mut counter = NodeCounter::default();
traverse(&proof, &mut counter, TraversalOrder::PostOrder);
assert_eq!(counter.axioms, 2);
let mut counter = NodeCounter::default();
traverse(&proof, &mut counter, TraversalOrder::BreadthFirst);
assert_eq!(counter.axioms, 2);
}
}