1use crate::proof::{Proof, ProofNode, ProofNodeId, ProofStep};
7use std::collections::{HashSet, VecDeque};
8
9pub trait ProofVisitor {
11 fn visit_node(&mut self, proof: &Proof, node: &ProofNode);
13
14 fn visit_axiom(&mut self, _proof: &Proof, _id: ProofNodeId, _conclusion: &str) {}
16
17 fn visit_inference(
19 &mut self,
20 _proof: &Proof,
21 _id: ProofNodeId,
22 _rule: &str,
23 _premises: &[ProofNodeId],
24 _conclusion: &str,
25 ) {
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum TraversalOrder {
32 PreOrder,
34 PostOrder,
36 BreadthFirst,
38}
39
40pub fn traverse<V: ProofVisitor>(proof: &Proof, visitor: &mut V, order: TraversalOrder) {
42 if let Some(root) = proof.root() {
43 let mut visited = HashSet::new();
44 match order {
45 TraversalOrder::PreOrder => traverse_pre_order(proof, root, visitor, &mut visited),
46 TraversalOrder::PostOrder => traverse_post_order(proof, root, visitor, &mut visited),
47 TraversalOrder::BreadthFirst => traverse_breadth_first(proof, visitor),
48 }
49 }
50}
51
52fn traverse_pre_order<V: ProofVisitor>(
54 proof: &Proof,
55 node_id: ProofNodeId,
56 visitor: &mut V,
57 visited: &mut HashSet<ProofNodeId>,
58) {
59 if visited.contains(&node_id) {
60 return;
61 }
62 visited.insert(node_id);
63
64 if let Some(node) = proof.get_node(node_id) {
65 visitor.visit_node(proof, node);
67
68 match &node.step {
69 ProofStep::Axiom { conclusion } => {
70 visitor.visit_axiom(proof, node_id, conclusion);
71 }
72 ProofStep::Inference {
73 rule,
74 premises,
75 conclusion,
76 ..
77 } => {
78 visitor.visit_inference(proof, node_id, rule, premises, conclusion);
79
80 for &premise in premises {
82 traverse_pre_order(proof, premise, visitor, visited);
83 }
84 }
85 }
86 }
87}
88
89fn traverse_post_order<V: ProofVisitor>(
91 proof: &Proof,
92 node_id: ProofNodeId,
93 visitor: &mut V,
94 visited: &mut HashSet<ProofNodeId>,
95) {
96 if visited.contains(&node_id) {
97 return;
98 }
99 visited.insert(node_id);
100
101 if let Some(node) = proof.get_node(node_id) {
102 if let ProofStep::Inference { premises, .. } = &node.step {
104 for &premise in premises {
105 traverse_post_order(proof, premise, visitor, visited);
106 }
107 }
108
109 visitor.visit_node(proof, node);
111
112 match &node.step {
113 ProofStep::Axiom { conclusion } => {
114 visitor.visit_axiom(proof, node_id, conclusion);
115 }
116 ProofStep::Inference {
117 rule,
118 premises,
119 conclusion,
120 ..
121 } => {
122 visitor.visit_inference(proof, node_id, rule, premises, conclusion);
123 }
124 }
125 }
126}
127
128fn traverse_breadth_first<V: ProofVisitor>(proof: &Proof, visitor: &mut V) {
130 if let Some(root) = proof.root() {
131 let mut queue = VecDeque::new();
132 let mut visited = HashSet::new();
133
134 queue.push_back(root);
135 visited.insert(root);
136
137 while let Some(node_id) = queue.pop_front() {
138 if let Some(node) = proof.get_node(node_id) {
139 visitor.visit_node(proof, node);
140
141 match &node.step {
142 ProofStep::Axiom { conclusion } => {
143 visitor.visit_axiom(proof, node_id, conclusion);
144 }
145 ProofStep::Inference {
146 rule,
147 premises,
148 conclusion,
149 ..
150 } => {
151 visitor.visit_inference(proof, node_id, rule, premises, conclusion);
152
153 for &premise in premises {
154 if !visited.contains(&premise) {
155 visited.insert(premise);
156 queue.push_back(premise);
157 }
158 }
159 }
160 }
161 }
162 }
163 }
164}
165
166#[must_use]
168pub fn topological_order(proof: &Proof) -> Vec<ProofNodeId> {
169 let mut order = Vec::new();
170 if let Some(root) = proof.root() {
171 let mut visited = HashSet::new();
172 collect_topological(proof, root, &mut order, &mut visited);
173 }
174 order
175}
176
177fn collect_topological(
178 proof: &Proof,
179 node_id: ProofNodeId,
180 order: &mut Vec<ProofNodeId>,
181 visited: &mut HashSet<ProofNodeId>,
182) {
183 if visited.contains(&node_id) {
184 return;
185 }
186 visited.insert(node_id);
187
188 if let Some(node) = proof.get_node(node_id)
189 && let ProofStep::Inference { premises, .. } = &node.step
190 {
191 for &premise in premises {
192 collect_topological(proof, premise, order, visited);
193 }
194 }
195
196 order.push(node_id);
197}
198
199#[must_use]
201pub fn find_all_paths(proof: &Proof) -> Vec<Vec<ProofNodeId>> {
202 let mut paths = Vec::new();
203 if let Some(root) = proof.root() {
204 let mut current_path = Vec::new();
205 collect_paths(proof, root, &mut current_path, &mut paths);
206 }
207 paths
208}
209
210fn collect_paths(
211 proof: &Proof,
212 node_id: ProofNodeId,
213 current_path: &mut Vec<ProofNodeId>,
214 all_paths: &mut Vec<Vec<ProofNodeId>>,
215) {
216 current_path.push(node_id);
217
218 if let Some(node) = proof.get_node(node_id) {
219 match &node.step {
220 ProofStep::Axiom { .. } => {
221 all_paths.push(current_path.clone());
223 }
224 ProofStep::Inference { premises, .. } => {
225 for &premise in premises {
227 collect_paths(proof, premise, current_path, all_paths);
228 }
229 }
230 }
231 }
232
233 current_path.pop();
234}
235
236#[derive(Debug, Default)]
239pub struct NodeCounter {
240 pub axioms: usize,
242 pub inferences: usize,
244}
245
246impl ProofVisitor for NodeCounter {
247 fn visit_node(&mut self, _proof: &Proof, node: &ProofNode) {
248 match node.step {
249 ProofStep::Axiom { .. } => self.axioms += 1,
250 ProofStep::Inference { .. } => self.inferences += 1,
251 }
252 }
253}
254
255#[derive(Debug, Default)]
258pub struct ConclusionCollector {
259 pub conclusions: Vec<String>,
261}
262
263impl ProofVisitor for ConclusionCollector {
264 fn visit_node(&mut self, _proof: &Proof, node: &ProofNode) {
265 let conclusion = match &node.step {
266 ProofStep::Axiom { conclusion } => conclusion.clone(),
267 ProofStep::Inference { conclusion, .. } => conclusion.clone(),
268 };
269 self.conclusions.push(conclusion);
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_node_counter() {
279 let mut proof = Proof::new();
280 let p1 = proof.add_axiom("p");
281 let p2 = proof.add_axiom("q");
282 let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
283
284 let mut counter = NodeCounter::default();
285 traverse(&proof, &mut counter, TraversalOrder::PreOrder);
286
287 assert_eq!(counter.axioms, 2);
288 assert_eq!(counter.inferences, 1);
289 }
290
291 #[test]
292 fn test_conclusion_collector() {
293 let mut proof = Proof::new();
294 let p1 = proof.add_axiom("p");
295 let p2 = proof.add_axiom("q");
296 let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
297
298 let mut collector = ConclusionCollector::default();
299 traverse(&proof, &mut collector, TraversalOrder::PreOrder);
300
301 assert_eq!(collector.conclusions.len(), 3);
302 assert!(collector.conclusions.contains(&"p".to_string()));
303 assert!(collector.conclusions.contains(&"q".to_string()));
304 assert!(collector.conclusions.contains(&"(and p q)".to_string()));
305 }
306
307 #[test]
308 fn test_topological_order() {
309 let mut proof = Proof::new();
310 let p1 = proof.add_axiom("p");
311 let p2 = proof.add_axiom("q");
312 let p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
313
314 let order = topological_order(&proof);
315 assert_eq!(order.len(), 3);
316
317 assert_eq!(order[order.len() - 1], p3);
319 }
320
321 #[test]
322 fn test_find_all_paths() {
323 let mut proof = Proof::new();
324 let p1 = proof.add_axiom("p");
325 let p2 = proof.add_axiom("q");
326 let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
327
328 let paths = find_all_paths(&proof);
329 assert_eq!(paths.len(), 2); }
331
332 #[test]
333 fn test_traversal_orders() {
334 let mut proof = Proof::new();
335 let p1 = proof.add_axiom("p");
336 let p2 = proof.add_axiom("q");
337 let _p3 = proof.add_inference("and", vec![p1, p2], "(and p q)");
338
339 let mut counter = NodeCounter::default();
341 traverse(&proof, &mut counter, TraversalOrder::PreOrder);
342 assert_eq!(counter.axioms, 2);
343
344 let mut counter = NodeCounter::default();
345 traverse(&proof, &mut counter, TraversalOrder::PostOrder);
346 assert_eq!(counter.axioms, 2);
347
348 let mut counter = NodeCounter::default();
349 traverse(&proof, &mut counter, TraversalOrder::BreadthFirst);
350 assert_eq!(counter.axioms, 2);
351 }
352}