Skip to main content

oxilean_kernel/proof/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::{Expr, Level, Name};
6use std::collections::HashSet;
7
8use super::types::{
9    ConfigNode, DecisionNode, Either2, Fixture, FlatSubstitution, FocusStack, LabelSet, MinHeap,
10    NonEmptyVec, PathBuf, PrefixCounter, ProofAnalysis, ProofAnalyzer, ProofCertificate,
11    ProofComplexity, ProofNormalizer, ProofObligation, ProofSkeleton, ProofState, ProofTerm,
12    RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc,
13    StatSummary, Stopwatch, StringPool, TokenBucket, TransformStat, TransitiveClosure,
14    VersionedRecord, WindowIterator, WriteOnce,
15};
16
17/// Known classical axioms that make proofs non-constructive.
18pub(crate) const CLASSICAL_AXIOMS: &[&str] = &[
19    "Classical.choice",
20    "Classical.em",
21    "Classical.byContradiction",
22    "Quotient.sound",
23    "propext",
24];
25/// Collect all axiom-like constant dependencies from an expression.
26pub fn collect_axiom_deps(expr: &Expr) -> HashSet<Name> {
27    let mut result = HashSet::new();
28    collect_constants_impl(expr, &mut result);
29    result
30}
31/// Collect constant names recursively.
32pub(super) fn collect_constants_impl(expr: &Expr, result: &mut HashSet<Name>) {
33    match expr {
34        Expr::Const(name, _) => {
35            result.insert(name.clone());
36        }
37        Expr::App(f, a) => {
38            collect_constants_impl(f, result);
39            collect_constants_impl(a, result);
40        }
41        Expr::Lam(_, _, ty, body) => {
42            collect_constants_impl(ty, result);
43            collect_constants_impl(body, result);
44        }
45        Expr::Pi(_, _, ty, body) => {
46            collect_constants_impl(ty, result);
47            collect_constants_impl(body, result);
48        }
49        Expr::Let(_, ty, val, body) => {
50            collect_constants_impl(ty, result);
51            collect_constants_impl(val, result);
52            collect_constants_impl(body, result);
53        }
54        Expr::Proj(_, _, e) => {
55            collect_constants_impl(e, result);
56        }
57        _ => {}
58    }
59}
60/// Check if proof irrelevance applies between two terms of a given type.
61///
62/// Returns true if the type is a Prop and both terms are proofs,
63/// meaning they should be considered definitionally equal.
64pub fn is_proof_irrelevant(ty: &Expr, _t1: &Expr, _t2: &Expr) -> bool {
65    ProofTerm::could_be_prop(ty)
66}
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::{Literal, Name};
71    #[test]
72    fn test_is_proof() {
73        let term = Expr::Lit(Literal::Nat(42));
74        let prop = Expr::Sort(Level::zero());
75        assert!(ProofTerm::is_proof(&term, &prop));
76    }
77    #[test]
78    fn test_is_proof_non_prop() {
79        let term = Expr::Lit(Literal::Nat(42));
80        let non_prop = Expr::Sort(Level::succ(Level::zero()));
81        assert!(!ProofTerm::is_proof(&term, &non_prop));
82    }
83    #[test]
84    fn test_size_simple() {
85        let term = Expr::Lit(Literal::Nat(42));
86        assert_eq!(ProofTerm::size(&term), 1);
87    }
88    #[test]
89    fn test_size_app() {
90        let f = Expr::Const(Name::str("f"), vec![]);
91        let a = Expr::Lit(Literal::Nat(1));
92        let app = Expr::App(Box::new(f), Box::new(a));
93        assert_eq!(ProofTerm::size(&app), 3);
94    }
95    #[test]
96    fn test_depth() {
97        let f = Expr::Const(Name::str("f"), vec![]);
98        let a = Expr::Lit(Literal::Nat(1));
99        let app = Expr::App(Box::new(f), Box::new(a));
100        assert_eq!(ProofTerm::depth(&app), 1);
101    }
102    #[test]
103    fn test_is_constructive() {
104        let term = Expr::BVar(0);
105        assert!(ProofTerm::is_constructive(&term));
106    }
107    #[test]
108    fn test_is_non_constructive() {
109        let term = Expr::Const(Name::str("Classical.choice"), vec![]);
110        assert!(!ProofTerm::is_constructive(&term));
111    }
112    #[test]
113    fn test_collect_constants() {
114        let f = Expr::Const(Name::str("f"), vec![]);
115        let g = Expr::Const(Name::str("g"), vec![]);
116        let app = Expr::App(Box::new(f), Box::new(g));
117        let consts = ProofTerm::collect_constants(&app);
118        assert!(consts.contains(&Name::str("f")));
119        assert!(consts.contains(&Name::str("g")));
120    }
121    #[test]
122    fn test_could_be_prop() {
123        assert!(ProofTerm::could_be_prop(&Expr::Sort(Level::zero())));
124        assert!(!ProofTerm::could_be_prop(&Expr::Sort(Level::succ(
125            Level::zero()
126        ))));
127    }
128    #[test]
129    fn test_is_sort_zero() {
130        assert!(ProofTerm::is_sort_zero(&Expr::Sort(Level::zero())));
131        assert!(!ProofTerm::is_sort_zero(&Expr::Sort(Level::succ(
132            Level::zero()
133        ))));
134    }
135    #[test]
136    fn test_same_proposition_structure() {
137        let p1 = Expr::Const(Name::str("True"), vec![]);
138        let p2 = Expr::Const(Name::str("True"), vec![]);
139        assert!(ProofTerm::same_proposition_structure(&p1, &p2));
140        let p3 = Expr::Const(Name::str("False"), vec![]);
141        assert!(!ProofTerm::same_proposition_structure(&p1, &p3));
142    }
143    #[test]
144    fn test_proof_irrelevance() {
145        let prop_ty = Expr::Sort(Level::zero());
146        let proof1 = Expr::Const(Name::str("p1"), vec![]);
147        let proof2 = Expr::Const(Name::str("p2"), vec![]);
148        assert!(is_proof_irrelevant(&prop_ty, &proof1, &proof2));
149        let type_ty = Expr::Sort(Level::succ(Level::zero()));
150        assert!(!is_proof_irrelevant(&type_ty, &proof1, &proof2));
151    }
152}
153/// Classifies a proof term into a `ProofComplexity` variant.
154#[allow(dead_code)]
155pub fn classify_proof(term: &Expr) -> ProofComplexity {
156    match term {
157        Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Sort(_) | Expr::Lit(_) => {
158            ProofComplexity::Atomic
159        }
160        Expr::Lam(_, _, _, _) => ProofComplexity::Abstraction,
161        Expr::App(_, _) => ProofComplexity::Application,
162        Expr::Let(_, _, _, _) => ProofComplexity::LetBinding,
163        Expr::Proj(_, _, _) => ProofComplexity::Projection,
164        Expr::Pi(_, _, _, _) => ProofComplexity::Composite,
165    }
166}
167/// Returns the set of all axiom constants depended upon by `term`.
168///
169/// This traverses the AST and collects names matching known axiom patterns.
170#[allow(dead_code)]
171pub fn axiom_dependencies(term: &Expr) -> HashSet<Name> {
172    let mut deps = HashSet::new();
173    axiom_deps_impl(term, &mut deps);
174    deps
175}
176pub(super) fn axiom_deps_impl(term: &Expr, deps: &mut HashSet<Name>) {
177    match term {
178        Expr::Const(name, _) if CLASSICAL_AXIOMS.contains(&name.to_string().as_str()) => {
179            deps.insert(name.clone());
180        }
181        Expr::Const(_, _) => {}
182        Expr::App(f, a) => {
183            axiom_deps_impl(f, deps);
184            axiom_deps_impl(a, deps);
185        }
186        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
187            axiom_deps_impl(ty, deps);
188            axiom_deps_impl(body, deps);
189        }
190        Expr::Let(_, ty, val, body) => {
191            axiom_deps_impl(ty, deps);
192            axiom_deps_impl(val, deps);
193            axiom_deps_impl(body, deps);
194        }
195        Expr::Proj(_, _, e) => axiom_deps_impl(e, deps),
196        _ => {}
197    }
198}
199/// Count the number of times a given constant name appears in `term`.
200#[allow(dead_code)]
201pub fn count_const_occurrences(term: &Expr, target: &Name) -> usize {
202    match term {
203        Expr::Const(name, _) if name == target => 1,
204        Expr::Const(_, _) => 0,
205        Expr::App(f, a) => count_const_occurrences(f, target) + count_const_occurrences(a, target),
206        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
207            count_const_occurrences(ty, target) + count_const_occurrences(body, target)
208        }
209        Expr::Let(_, ty, val, body) => {
210            count_const_occurrences(ty, target)
211                + count_const_occurrences(val, target)
212                + count_const_occurrences(body, target)
213        }
214        Expr::Proj(_, _, e) => count_const_occurrences(e, target),
215        _ => 0,
216    }
217}
218/// Returns `true` if `term` contains no free variables (is a closed term).
219///
220/// A closed term has no `FVar` nodes; BVar nodes are allowed (they are bound).
221#[allow(dead_code)]
222pub fn is_closed_term(term: &Expr) -> bool {
223    match term {
224        Expr::FVar(_) => false,
225        Expr::BVar(_) | Expr::Const(_, _) | Expr::Sort(_) | Expr::Lit(_) => true,
226        Expr::App(f, a) => is_closed_term(f) && is_closed_term(a),
227        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
228            is_closed_term(ty) && is_closed_term(body)
229        }
230        Expr::Let(_, ty, val, body) => {
231            is_closed_term(ty) && is_closed_term(val) && is_closed_term(body)
232        }
233        Expr::Proj(_, _, e) => is_closed_term(e),
234    }
235}
236/// Checks whether two proof terms have structurally identical shapes,
237/// ignoring the actual constants and literals at the leaves.
238#[allow(dead_code)]
239pub fn same_proof_shape(a: &Expr, b: &Expr) -> bool {
240    match (a, b) {
241        (Expr::BVar(i), Expr::BVar(j)) => i == j,
242        (Expr::FVar(_), Expr::FVar(_)) => true,
243        (Expr::Const(_, _), Expr::Const(_, _)) => true,
244        (Expr::Sort(_), Expr::Sort(_)) => true,
245        (Expr::Lit(_), Expr::Lit(_)) => true,
246        (Expr::App(f1, a1), Expr::App(f2, a2)) => {
247            same_proof_shape(f1, f2) && same_proof_shape(a1, a2)
248        }
249        (Expr::Lam(_, _, ty1, b1), Expr::Lam(_, _, ty2, b2)) => {
250            same_proof_shape(ty1, ty2) && same_proof_shape(b1, b2)
251        }
252        (Expr::Pi(_, _, ty1, b1), Expr::Pi(_, _, ty2, b2)) => {
253            same_proof_shape(ty1, ty2) && same_proof_shape(b1, b2)
254        }
255        (Expr::Let(_, ty1, v1, b1), Expr::Let(_, ty2, v2, b2)) => {
256            same_proof_shape(ty1, ty2) && same_proof_shape(v1, v2) && same_proof_shape(b1, b2)
257        }
258        (Expr::Proj(i1, _, e1), Expr::Proj(i2, _, e2)) => i1 == i2 && same_proof_shape(e1, e2),
259        _ => false,
260    }
261}
262/// Estimate the "proof effort" metric: a rough measure of how hard it
263/// was to construct the term, based on its size and classical axiom usage.
264#[allow(dead_code)]
265pub fn proof_effort(term: &Expr) -> u64 {
266    let size = ProofTerm::size(term) as u64;
267    let depth = ProofTerm::depth(term) as u64;
268    let classical_penalty: u64 = if ProofTerm::is_constructive(term) {
269        0
270    } else {
271        100
272    };
273    size + depth * 2 + classical_penalty
274}
275#[cfg(test)]
276mod extended_proof_tests {
277    use super::*;
278    fn mk_const(name: &str) -> Expr {
279        Expr::Const(Name::str(name), vec![])
280    }
281    fn mk_prop() -> Expr {
282        Expr::Sort(Level::zero())
283    }
284    #[allow(dead_code)]
285    fn mk_type1() -> Expr {
286        Expr::Sort(Level::succ(Level::zero()))
287    }
288    #[test]
289    fn test_classify_atomic_const() {
290        let term = mk_const("True");
291        assert_eq!(classify_proof(&term), ProofComplexity::Atomic);
292    }
293    #[test]
294    fn test_classify_bvar() {
295        let term = Expr::BVar(0);
296        assert_eq!(classify_proof(&term), ProofComplexity::Atomic);
297    }
298    #[test]
299    fn test_classify_app() {
300        let f = mk_const("f");
301        let a = mk_const("a");
302        let app = Expr::App(Box::new(f), Box::new(a));
303        assert_eq!(classify_proof(&app), ProofComplexity::Application);
304    }
305    #[test]
306    fn test_proof_analysis_size() {
307        let term = Expr::App(Box::new(mk_const("f")), Box::new(mk_const("a")));
308        let analysis = ProofAnalysis::analyse(&term);
309        assert_eq!(analysis.size, 3);
310        assert_eq!(analysis.app_count, 1);
311        assert_eq!(analysis.lambda_count, 0);
312    }
313    #[test]
314    fn test_proof_obligation_discharge() {
315        let prop = mk_prop();
316        let mut obl = ProofObligation::new("main", prop);
317        assert!(!obl.is_discharged());
318        obl.discharge(mk_const("proof"));
319        assert!(obl.is_discharged());
320    }
321    #[test]
322    fn test_proof_state_remaining() {
323        let mut state = ProofState::new();
324        state.add_obligation("goal1", mk_prop());
325        state.add_obligation("goal2", mk_prop());
326        assert_eq!(state.remaining(), 2);
327        assert!(!state.is_complete());
328        state.discharge_next(mk_const("p1"));
329        assert_eq!(state.remaining(), 1);
330        state.discharge_next(mk_const("p2"));
331        assert!(state.is_complete());
332    }
333    #[test]
334    fn test_beta_reduce_simple() {
335        let body = Expr::BVar(0);
336        let lam = Expr::Lam(
337            crate::BinderInfo::Default,
338            Name::str("x"),
339            Box::new(mk_prop()),
340            Box::new(body),
341        );
342        let arg = mk_const("myarg");
343        let app = Expr::App(Box::new(lam), Box::new(arg.clone()));
344        let reduced = ProofNormalizer::beta_reduce(&app);
345        assert_eq!(reduced, arg);
346    }
347    #[test]
348    fn test_count_redexes_none() {
349        let term = mk_const("pure");
350        assert_eq!(ProofNormalizer::count_redexes(&term), 0);
351        assert!(ProofNormalizer::is_beta_normal(&term));
352    }
353    #[test]
354    fn test_count_redexes_one() {
355        let body = Expr::BVar(0);
356        let lam = Expr::Lam(
357            crate::BinderInfo::Default,
358            Name::str("x"),
359            Box::new(mk_prop()),
360            Box::new(body),
361        );
362        let app = Expr::App(Box::new(lam), Box::new(mk_const("arg")));
363        assert_eq!(ProofNormalizer::count_redexes(&app), 1);
364        assert!(!ProofNormalizer::is_beta_normal(&app));
365    }
366    #[test]
367    fn test_axiom_dependencies_classical() {
368        let term = mk_const("Classical.em");
369        let deps = axiom_dependencies(&term);
370        assert!(deps.contains(&Name::str("Classical.em")));
371    }
372    #[test]
373    fn test_axiom_dependencies_constructive() {
374        let term = mk_const("Nat.succ");
375        let deps = axiom_dependencies(&term);
376        assert!(deps.is_empty());
377    }
378    #[test]
379    fn test_count_const_occurrences() {
380        let target = Name::str("f");
381        let f1 = Expr::Const(target.clone(), vec![]);
382        let f2 = Expr::Const(target.clone(), vec![]);
383        let app = Expr::App(Box::new(f1), Box::new(f2));
384        assert_eq!(count_const_occurrences(&app, &target), 2);
385    }
386    #[test]
387    fn test_is_closed_const() {
388        let term = mk_const("True");
389        assert!(is_closed_term(&term));
390    }
391    #[test]
392    fn test_is_not_closed_fvar() {
393        let term = Expr::FVar(crate::FVarId(0));
394        assert!(!is_closed_term(&term));
395    }
396    #[test]
397    fn test_same_proof_shape_bvar() {
398        let a = Expr::BVar(2);
399        let b = Expr::BVar(2);
400        let c = Expr::BVar(3);
401        assert!(same_proof_shape(&a, &b));
402        assert!(!same_proof_shape(&a, &c));
403    }
404    #[test]
405    fn test_proof_effort_classical() {
406        let classical_term = mk_const("Classical.em");
407        let constructive_term = mk_const("Nat.succ");
408        assert!(proof_effort(&classical_term) > proof_effort(&constructive_term));
409    }
410    #[test]
411    fn test_proof_normalizer_subst_bvar_shift() {
412        let term = Expr::BVar(1);
413        let result = ProofNormalizer::subst_bvar(term, 0, &mk_const("x"));
414        assert_eq!(result, Expr::BVar(0));
415    }
416}
417/// Check whether a term contains a sorry-like pattern.
418#[allow(dead_code)]
419pub(crate) fn contains_classical_sorry(term: &Expr) -> bool {
420    match term {
421        Expr::Const(n, _) => {
422            let s = n.to_string();
423            s.contains("sorry") || s.contains("Lean.Elab.Tactic.sorry")
424        }
425        Expr::App(f, a) => contains_classical_sorry(f) || contains_classical_sorry(a),
426        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
427            contains_classical_sorry(ty) || contains_classical_sorry(body)
428        }
429        Expr::Let(_, ty, val, body) => {
430            contains_classical_sorry(ty)
431                || contains_classical_sorry(val)
432                || contains_classical_sorry(body)
433        }
434        _ => false,
435    }
436}
437#[cfg(test)]
438mod extra_proof_tests {
439    use super::*;
440    fn mk_const(s: &str) -> Expr {
441        Expr::Const(Name::str(s), vec![])
442    }
443    #[test]
444    fn test_proof_skeleton_no_holes() {
445        let proof = mk_const("True.intro");
446        let ty = mk_const("True");
447        let skel = ProofSkeleton::new(proof, ty);
448        assert!(skel.is_complete());
449        assert_eq!(skel.num_holes(), 0);
450    }
451    #[test]
452    fn test_proof_skeleton_with_sorry() {
453        let sorry = mk_const("sorry");
454        let ty = mk_const("Nat");
455        let skel = ProofSkeleton::new(sorry, ty);
456        assert!(!skel.is_complete());
457        assert_eq!(skel.num_holes(), 1);
458    }
459    #[test]
460    fn test_proof_skeleton_display() {
461        let proof = mk_const("True.intro");
462        let ty = mk_const("True");
463        let skel = ProofSkeleton::new(proof, ty);
464        let s = format!("{}", skel);
465        assert!(s.contains("ProofSkeleton"));
466    }
467    #[test]
468    fn test_proof_certificate_no_sorry() {
469        let prop = mk_const("True");
470        let proof = mk_const("True.intro");
471        let cert = ProofCertificate::new(prop, proof);
472        assert!(!cert.has_sorry);
473    }
474    #[test]
475    fn test_proof_certificate_trusted() {
476        let prop = mk_const("True");
477        let proof = mk_const("True.intro");
478        let cert = ProofCertificate::new(prop, proof);
479        assert!(cert.is_trusted());
480    }
481    #[test]
482    fn test_proof_analyzer_constructive() {
483        let proof = mk_const("And.intro");
484        assert!(ProofAnalyzer::is_constructive(&proof));
485    }
486    #[test]
487    fn test_proof_analyzer_classical() {
488        let proof = mk_const("Classical.em");
489        assert!(ProofAnalyzer::uses_classical(&proof));
490        assert!(!ProofAnalyzer::is_constructive(&proof));
491    }
492    #[test]
493    fn test_proof_analyzer_count_apps() {
494        let f = mk_const("f");
495        let a = mk_const("a");
496        let app = Expr::App(Box::new(f), Box::new(a));
497        assert_eq!(ProofAnalyzer::count_applications(&app), 1);
498    }
499    #[test]
500    fn test_proof_analyzer_count_apps_nested() {
501        let f = mk_const("f");
502        let a = mk_const("a");
503        let b = mk_const("b");
504        let app1 = Expr::App(Box::new(f), Box::new(a));
505        let app2 = Expr::App(Box::new(app1), Box::new(b));
506        assert_eq!(ProofAnalyzer::count_applications(&app2), 2);
507    }
508    #[test]
509    fn test_contains_classical_sorry_false() {
510        let proof = mk_const("True.intro");
511        assert!(!contains_classical_sorry(&proof));
512    }
513    #[test]
514    fn test_contains_classical_sorry_true() {
515        let sorry = mk_const("sorry");
516        assert!(contains_classical_sorry(&sorry));
517    }
518}
519#[cfg(test)]
520mod tests_padding_infra {
521    use super::*;
522    #[test]
523    fn test_stat_summary() {
524        let mut ss = StatSummary::new();
525        ss.record(10.0);
526        ss.record(20.0);
527        ss.record(30.0);
528        assert_eq!(ss.count(), 3);
529        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
530        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
531        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
532    }
533    #[test]
534    fn test_transform_stat() {
535        let mut ts = TransformStat::new();
536        ts.record_before(100.0);
537        ts.record_after(80.0);
538        let ratio = ts.mean_ratio().expect("ratio should be present");
539        assert!((ratio - 0.8).abs() < 1e-9);
540    }
541    #[test]
542    fn test_small_map() {
543        let mut m: SmallMap<u32, &str> = SmallMap::new();
544        m.insert(3, "three");
545        m.insert(1, "one");
546        m.insert(2, "two");
547        assert_eq!(m.get(&2), Some(&"two"));
548        assert_eq!(m.len(), 3);
549        let keys = m.keys();
550        assert_eq!(*keys[0], 1);
551        assert_eq!(*keys[2], 3);
552    }
553    #[test]
554    fn test_label_set() {
555        let mut ls = LabelSet::new();
556        ls.add("foo");
557        ls.add("bar");
558        ls.add("foo");
559        assert_eq!(ls.count(), 2);
560        assert!(ls.has("bar"));
561        assert!(!ls.has("baz"));
562    }
563    #[test]
564    fn test_config_node() {
565        let mut root = ConfigNode::section("root");
566        let child = ConfigNode::leaf("key", "value");
567        root.add_child(child);
568        assert_eq!(root.num_children(), 1);
569    }
570    #[test]
571    fn test_versioned_record() {
572        let mut vr = VersionedRecord::new(0u32);
573        vr.update(1);
574        vr.update(2);
575        assert_eq!(*vr.current(), 2);
576        assert_eq!(vr.version(), 2);
577        assert!(vr.has_history());
578        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
579    }
580    #[test]
581    fn test_simple_dag() {
582        let mut dag = SimpleDag::new(4);
583        dag.add_edge(0, 1);
584        dag.add_edge(1, 2);
585        dag.add_edge(2, 3);
586        assert!(dag.can_reach(0, 3));
587        assert!(!dag.can_reach(3, 0));
588        let order = dag.topological_sort().expect("order should be present");
589        assert_eq!(order, vec![0, 1, 2, 3]);
590    }
591    #[test]
592    fn test_focus_stack() {
593        let mut fs: FocusStack<&str> = FocusStack::new();
594        fs.focus("a");
595        fs.focus("b");
596        assert_eq!(fs.current(), Some(&"b"));
597        assert_eq!(fs.depth(), 2);
598        fs.blur();
599        assert_eq!(fs.current(), Some(&"a"));
600    }
601}
602#[cfg(test)]
603mod tests_extra_iterators {
604    use super::*;
605    #[test]
606    fn test_window_iterator() {
607        let data = vec![1u32, 2, 3, 4, 5];
608        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
609        assert_eq!(windows.len(), 3);
610        assert_eq!(windows[0], &[1, 2, 3]);
611        assert_eq!(windows[2], &[3, 4, 5]);
612    }
613    #[test]
614    fn test_non_empty_vec() {
615        let mut nev = NonEmptyVec::singleton(10u32);
616        nev.push(20);
617        nev.push(30);
618        assert_eq!(nev.len(), 3);
619        assert_eq!(*nev.first(), 10);
620        assert_eq!(*nev.last(), 30);
621    }
622}
623#[cfg(test)]
624mod tests_padding2 {
625    use super::*;
626    #[test]
627    fn test_sliding_sum() {
628        let mut ss = SlidingSum::new(3);
629        ss.push(1.0);
630        ss.push(2.0);
631        ss.push(3.0);
632        assert!((ss.sum() - 6.0).abs() < 1e-9);
633        ss.push(4.0);
634        assert!((ss.sum() - 9.0).abs() < 1e-9);
635        assert_eq!(ss.count(), 3);
636    }
637    #[test]
638    fn test_path_buf() {
639        let mut pb = PathBuf::new();
640        pb.push("src");
641        pb.push("main");
642        assert_eq!(pb.as_str(), "src/main");
643        assert_eq!(pb.depth(), 2);
644        pb.pop();
645        assert_eq!(pb.as_str(), "src");
646    }
647    #[test]
648    fn test_string_pool() {
649        let mut pool = StringPool::new();
650        let s = pool.take();
651        assert!(s.is_empty());
652        pool.give("hello".to_string());
653        let s2 = pool.take();
654        assert!(s2.is_empty());
655        assert_eq!(pool.free_count(), 0);
656    }
657    #[test]
658    fn test_transitive_closure() {
659        let mut tc = TransitiveClosure::new(4);
660        tc.add_edge(0, 1);
661        tc.add_edge(1, 2);
662        tc.add_edge(2, 3);
663        assert!(tc.can_reach(0, 3));
664        assert!(!tc.can_reach(3, 0));
665        let r = tc.reachable_from(0);
666        assert_eq!(r.len(), 4);
667    }
668    #[test]
669    fn test_token_bucket() {
670        let mut tb = TokenBucket::new(100, 10);
671        assert_eq!(tb.available(), 100);
672        assert!(tb.try_consume(50));
673        assert_eq!(tb.available(), 50);
674        assert!(!tb.try_consume(60));
675        assert_eq!(tb.capacity(), 100);
676    }
677    #[test]
678    fn test_rewrite_rule_set() {
679        let mut rrs = RewriteRuleSet::new();
680        rrs.add(RewriteRule::unconditional(
681            "beta",
682            "App(Lam(x, b), v)",
683            "b[x:=v]",
684        ));
685        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
686        assert_eq!(rrs.len(), 2);
687        assert_eq!(rrs.unconditional_rules().len(), 1);
688        assert_eq!(rrs.conditional_rules().len(), 1);
689        assert!(rrs.get("beta").is_some());
690        let disp = rrs
691            .get("beta")
692            .expect("element at \'beta\' should exist")
693            .display();
694        assert!(disp.contains("→"));
695    }
696}
697#[cfg(test)]
698mod tests_padding3 {
699    use super::*;
700    #[test]
701    fn test_decision_node() {
702        let tree = DecisionNode::Branch {
703            key: "x".into(),
704            val: "1".into(),
705            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
706            no_branch: Box::new(DecisionNode::Leaf("no".into())),
707        };
708        let mut ctx = std::collections::HashMap::new();
709        ctx.insert("x".into(), "1".into());
710        assert_eq!(tree.evaluate(&ctx), "yes");
711        ctx.insert("x".into(), "2".into());
712        assert_eq!(tree.evaluate(&ctx), "no");
713        assert_eq!(tree.depth(), 1);
714    }
715    #[test]
716    fn test_flat_substitution() {
717        let mut sub = FlatSubstitution::new();
718        sub.add("foo", "bar");
719        sub.add("baz", "qux");
720        assert_eq!(sub.apply("foo and baz"), "bar and qux");
721        assert_eq!(sub.len(), 2);
722    }
723    #[test]
724    fn test_stopwatch() {
725        let mut sw = Stopwatch::start();
726        sw.split();
727        sw.split();
728        assert_eq!(sw.num_splits(), 2);
729        assert!(sw.elapsed_ms() >= 0.0);
730        for &s in sw.splits() {
731            assert!(s >= 0.0);
732        }
733    }
734    #[test]
735    fn test_either2() {
736        let e: Either2<i32, &str> = Either2::First(42);
737        assert!(e.is_first());
738        let mapped = e.map_first(|x| x * 2);
739        assert_eq!(mapped.first(), Some(84));
740        let e2: Either2<i32, &str> = Either2::Second("hello");
741        assert!(e2.is_second());
742        assert_eq!(e2.second(), Some("hello"));
743    }
744    #[test]
745    fn test_write_once() {
746        let wo: WriteOnce<u32> = WriteOnce::new();
747        assert!(!wo.is_written());
748        assert!(wo.write(42));
749        assert!(!wo.write(99));
750        assert_eq!(wo.read(), Some(42));
751    }
752    #[test]
753    fn test_sparse_vec() {
754        let mut sv: SparseVec<i32> = SparseVec::new(100);
755        sv.set(5, 10);
756        sv.set(50, 20);
757        assert_eq!(*sv.get(5), 10);
758        assert_eq!(*sv.get(50), 20);
759        assert_eq!(*sv.get(0), 0);
760        assert_eq!(sv.nnz(), 2);
761        sv.set(5, 0);
762        assert_eq!(sv.nnz(), 1);
763    }
764    #[test]
765    fn test_stack_calc() {
766        let mut calc = StackCalc::new();
767        calc.push(3);
768        calc.push(4);
769        calc.add();
770        assert_eq!(calc.peek(), Some(7));
771        calc.push(2);
772        calc.mul();
773        assert_eq!(calc.peek(), Some(14));
774    }
775}
776#[cfg(test)]
777mod tests_final_padding {
778    use super::*;
779    #[test]
780    fn test_min_heap() {
781        let mut h = MinHeap::new();
782        h.push(5u32);
783        h.push(1u32);
784        h.push(3u32);
785        assert_eq!(h.peek(), Some(&1));
786        assert_eq!(h.pop(), Some(1));
787        assert_eq!(h.pop(), Some(3));
788        assert_eq!(h.pop(), Some(5));
789        assert!(h.is_empty());
790    }
791    #[test]
792    fn test_prefix_counter() {
793        let mut pc = PrefixCounter::new();
794        pc.record("hello");
795        pc.record("help");
796        pc.record("world");
797        assert_eq!(pc.count_with_prefix("hel"), 2);
798        assert_eq!(pc.count_with_prefix("wor"), 1);
799        assert_eq!(pc.count_with_prefix("xyz"), 0);
800    }
801    #[test]
802    fn test_fixture() {
803        let mut f = Fixture::new();
804        f.set("key1", "val1");
805        f.set("key2", "val2");
806        assert_eq!(f.get("key1"), Some("val1"));
807        assert_eq!(f.get("key3"), None);
808        assert_eq!(f.len(), 2);
809    }
810}