1use crate::proof::{Proof, ProofNode, ProofNodeId, ProofStep};
7use rustc_hash::FxHashMap;
8use std::cell::RefCell;
9use std::rc::Rc;
10
11#[derive(Debug, Clone)]
13pub struct LazyNode {
14 pub id: ProofNodeId,
16 cached: Rc<RefCell<Option<ProofNode>>>,
18 proof: Rc<Proof>,
20}
21
22impl LazyNode {
23 fn new(id: ProofNodeId, proof: Rc<Proof>) -> Self {
25 Self {
26 id,
27 cached: Rc::new(RefCell::new(None)),
28 proof,
29 }
30 }
31
32 pub fn force(&self) -> Option<ProofNode> {
34 if let Some(node) = self.cached.borrow().as_ref() {
36 return Some(node.clone());
37 }
38
39 if let Some(node) = self.proof.get_node(self.id) {
41 *self.cached.borrow_mut() = Some(node.clone());
42 Some(node.clone())
43 } else {
44 None
45 }
46 }
47
48 pub fn is_forced(&self) -> bool {
50 self.cached.borrow().is_some()
51 }
52
53 pub fn conclusion_if_cached(&self) -> Option<String> {
55 self.cached
56 .borrow()
57 .as_ref()
58 .map(|node| node.conclusion().to_string())
59 }
60}
61
62pub struct LazyProof {
64 proof: Rc<Proof>,
66 node_cache: RefCell<FxHashMap<ProofNodeId, LazyNode>>,
68 stats: RefCell<LazyStats>,
70}
71
72#[derive(Debug, Clone, Default)]
74pub struct LazyStats {
75 pub cache_hits: usize,
77 pub cache_misses: usize,
79 pub forced_evaluations: usize,
81}
82
83impl LazyStats {
84 pub fn hit_rate(&self) -> f64 {
86 let total = self.cache_hits + self.cache_misses;
87 if total == 0 {
88 0.0
89 } else {
90 self.cache_hits as f64 / total as f64
91 }
92 }
93}
94
95impl LazyProof {
96 pub fn new(proof: Proof) -> Self {
98 Self {
99 proof: Rc::new(proof),
100 node_cache: RefCell::new(FxHashMap::default()),
101 stats: RefCell::new(LazyStats::default()),
102 }
103 }
104
105 pub fn get_lazy_node(&self, id: ProofNodeId) -> Option<LazyNode> {
107 if let Some(lazy_node) = self.node_cache.borrow().get(&id) {
109 self.stats.borrow_mut().cache_hits += 1;
110 return Some(lazy_node.clone());
111 }
112
113 self.stats.borrow_mut().cache_misses += 1;
115 if self.proof.get_node(id).is_some() {
116 let lazy_node = LazyNode::new(id, Rc::clone(&self.proof));
117 self.node_cache.borrow_mut().insert(id, lazy_node.clone());
118 Some(lazy_node)
119 } else {
120 None
121 }
122 }
123
124 pub fn force_node(&self, id: ProofNodeId) -> Option<ProofNode> {
126 if let Some(lazy_node) = self.get_lazy_node(id) {
127 self.stats.borrow_mut().forced_evaluations += 1;
128 lazy_node.force()
129 } else {
130 None
131 }
132 }
133
134 pub fn get_premises_lazy(&self, id: ProofNodeId) -> Vec<LazyNode> {
136 if let Some(node) = self.force_node(id) {
137 if let ProofStep::Inference { premises, .. } = &node.step {
138 premises
139 .iter()
140 .filter_map(|&p| self.get_lazy_node(p))
141 .collect()
142 } else {
143 Vec::new()
144 }
145 } else {
146 Vec::new()
147 }
148 }
149
150 pub fn is_axiom_lazy(&self, id: ProofNodeId) -> bool {
152 if let Some(node) = self.force_node(id) {
153 matches!(node.step, ProofStep::Axiom { .. })
154 } else {
155 false
156 }
157 }
158
159 pub fn len(&self) -> usize {
161 self.proof.len()
162 }
163
164 pub fn is_empty(&self) -> bool {
166 self.proof.is_empty()
167 }
168
169 pub fn get_stats(&self) -> LazyStats {
171 self.stats.borrow().clone()
172 }
173
174 pub fn clear_cache(&self) {
176 self.node_cache.borrow_mut().clear();
177 }
178
179 pub fn proof(&self) -> &Proof {
181 &self.proof
182 }
183}
184
185pub struct LazyNodeIterator {
187 lazy_proof: Rc<LazyProof>,
188 current_index: usize,
189 node_ids: Vec<ProofNodeId>,
190}
191
192impl LazyNodeIterator {
193 pub fn new(lazy_proof: &LazyProof) -> Self {
195 let node_ids = lazy_proof.proof.nodes().iter().map(|n| n.id).collect();
196 Self {
197 lazy_proof: Rc::new(LazyProof::new((*lazy_proof.proof).clone())),
198 current_index: 0,
199 node_ids,
200 }
201 }
202}
203
204impl Iterator for LazyNodeIterator {
205 type Item = LazyNode;
206
207 fn next(&mut self) -> Option<Self::Item> {
208 if self.current_index < self.node_ids.len() {
209 let id = self.node_ids[self.current_index];
210 self.current_index += 1;
211 self.lazy_proof.get_lazy_node(id)
212 } else {
213 None
214 }
215 }
216}
217
218pub struct LazyDependencyResolver {
220 lazy_proof: LazyProof,
221}
222
223impl LazyDependencyResolver {
224 pub fn new(proof: Proof) -> Self {
226 Self {
227 lazy_proof: LazyProof::new(proof),
228 }
229 }
230
231 pub fn get_dependencies(&self, id: ProofNodeId) -> Vec<ProofNodeId> {
233 let mut dependencies = Vec::new();
234 let mut visited = FxHashMap::default();
235 self.collect_dependencies(id, &mut dependencies, &mut visited);
236 dependencies
237 }
238
239 fn collect_dependencies(
241 &self,
242 id: ProofNodeId,
243 deps: &mut Vec<ProofNodeId>,
244 visited: &mut FxHashMap<ProofNodeId, bool>,
245 ) {
246 if visited.contains_key(&id) {
247 return;
248 }
249 visited.insert(id, true);
250
251 let premises = self.lazy_proof.get_premises_lazy(id);
252 for premise in premises {
253 self.collect_dependencies(premise.id, deps, visited);
254 }
255
256 deps.push(id);
257 }
258
259 pub fn depends_on(&self, a: ProofNodeId, b: ProofNodeId) -> bool {
261 self.get_dependencies(a).contains(&b)
262 }
263
264 pub fn get_stats(&self) -> LazyStats {
266 self.lazy_proof.get_stats()
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_lazy_node_new() {
276 let proof = Proof::new();
277 let proof_rc = Rc::new(proof);
278 let lazy_node = LazyNode::new(ProofNodeId(0), proof_rc);
279 assert_eq!(lazy_node.id, ProofNodeId(0));
280 assert!(!lazy_node.is_forced());
281 }
282
283 #[test]
284 fn test_lazy_proof_new() {
285 let proof = Proof::new();
286 let lazy_proof = LazyProof::new(proof);
287 assert!(lazy_proof.is_empty());
288 }
289
290 #[test]
291 fn test_get_lazy_node() {
292 let mut proof = Proof::new();
293 let id = proof.add_axiom("x = x");
294 let lazy_proof = LazyProof::new(proof);
295
296 let lazy_node = lazy_proof.get_lazy_node(id);
297 assert!(lazy_node.is_some());
298 }
299
300 #[test]
301 fn test_force_node() {
302 let mut proof = Proof::new();
303 let id = proof.add_axiom("x = x");
304 let lazy_proof = LazyProof::new(proof);
305
306 let node = lazy_proof.force_node(id);
307 assert!(node.is_some());
308 assert_eq!(
309 node.expect("test operation should succeed").conclusion(),
310 "x = x"
311 );
312 }
313
314 #[test]
315 fn test_is_axiom_lazy() {
316 let mut proof = Proof::new();
317 let id = proof.add_axiom("x = x");
318 let lazy_proof = LazyProof::new(proof);
319
320 assert!(lazy_proof.is_axiom_lazy(id));
321 }
322
323 #[test]
324 fn test_lazy_stats() {
325 let mut proof = Proof::new();
326 let id = proof.add_axiom("x = x");
327 let lazy_proof = LazyProof::new(proof);
328
329 let _ = lazy_proof.get_lazy_node(id);
331 let stats = lazy_proof.get_stats();
332 assert_eq!(stats.cache_misses, 1);
333
334 let _ = lazy_proof.get_lazy_node(id);
336 let stats = lazy_proof.get_stats();
337 assert_eq!(stats.cache_hits, 1);
338 }
339
340 #[test]
341 fn test_lazy_stats_hit_rate() {
342 let stats = LazyStats {
343 cache_hits: 7,
344 cache_misses: 3,
345 forced_evaluations: 10,
346 };
347 assert_eq!(stats.hit_rate(), 0.7);
348 }
349
350 #[test]
351 fn test_clear_cache() {
352 let mut proof = Proof::new();
353 let id = proof.add_axiom("x = x");
354 let lazy_proof = LazyProof::new(proof);
355
356 let _ = lazy_proof.get_lazy_node(id);
357 lazy_proof.clear_cache();
358
359 let _ = lazy_proof.get_lazy_node(id);
361 let stats = lazy_proof.get_stats();
362 assert_eq!(stats.cache_misses, 2); }
364
365 #[test]
366 fn test_lazy_dependency_resolver() {
367 let mut proof = Proof::new();
368 let ax1 = proof.add_axiom("x = x");
369 let ax2 = proof.add_axiom("y = y");
370 let res = proof.add_inference("resolution", vec![ax1, ax2], "x = x or y = y");
371
372 let resolver = LazyDependencyResolver::new(proof);
373 let deps = resolver.get_dependencies(res);
374 assert_eq!(deps.len(), 3); }
376
377 #[test]
378 fn test_depends_on() {
379 let mut proof = Proof::new();
380 let ax1 = proof.add_axiom("x = x");
381 let ax2 = proof.add_axiom("y = y");
382 let res = proof.add_inference("resolution", vec![ax1, ax2], "x = x or y = y");
383
384 let resolver = LazyDependencyResolver::new(proof);
385 assert!(resolver.depends_on(res, ax1));
386 assert!(resolver.depends_on(res, ax2));
387 assert!(!resolver.depends_on(ax1, res));
388 }
389
390 #[test]
391 fn test_get_premises_lazy() {
392 let mut proof = Proof::new();
393 let ax1 = proof.add_axiom("x = x");
394 let ax2 = proof.add_axiom("y = y");
395 let res = proof.add_inference("resolution", vec![ax1, ax2], "x = x or y = y");
396
397 let lazy_proof = LazyProof::new(proof);
398 let premises = lazy_proof.get_premises_lazy(res);
399 assert_eq!(premises.len(), 2);
400 }
401}