Skip to main content

oxilean_kernel/abstract/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::expr_util::has_any_fvar;
6use crate::subst::instantiate;
7use crate::{Expr, FVarId, Name};
8
9use super::types::{
10    ConfigNode, DecisionNode, Either2, Fixture, FlatSubstitution, FocusStack, LabelSet, MinHeap,
11    NonEmptyVec, PathBuf, PrefixCounter, RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum,
12    SmallMap, SparseVec, StackCalc, StatSummary, Stopwatch, StringPool, TokenBucket, TransformStat,
13    TransitiveClosure, VersionedRecord, WindowIterator, WriteOnce,
14};
15
16/// Abstract multiple free variables simultaneously.
17///
18/// `abstract_fvars(e, [x, y, z])` replaces:
19/// - `x` (at subst\[0\]) -> BVar(2) (outermost)
20/// - `y` (at subst\[1\]) -> BVar(1) (middle)
21/// - `z` (at subst\[2\]) -> BVar(0) (innermost)
22///
23/// This follows LEAN 4's convention where elements at the end of the
24/// array become innermost binders.
25pub fn abstract_fvars(expr: &Expr, fvars: &[FVarId]) -> Expr {
26    if fvars.is_empty() || !has_any_fvar(expr) {
27        return expr.clone();
28    }
29    abstract_fvars_at(expr, fvars, 0)
30}
31fn abstract_fvars_at(expr: &Expr, fvars: &[FVarId], offset: u32) -> Expr {
32    match expr {
33        Expr::FVar(id) => {
34            let n = fvars.len();
35            for (i, fv) in fvars.iter().enumerate().rev() {
36                if id == fv {
37                    return Expr::BVar(offset + (n - i - 1) as u32);
38                }
39            }
40            for (i, fv) in fvars.iter().enumerate() {
41                if id == fv {
42                    return Expr::BVar(offset + (n - i - 1) as u32);
43                }
44            }
45            expr.clone()
46        }
47        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
48        Expr::App(f, a) => {
49            let f_new = abstract_fvars_at(f, fvars, offset);
50            let a_new = abstract_fvars_at(a, fvars, offset);
51            Expr::App(Box::new(f_new), Box::new(a_new))
52        }
53        Expr::Lam(bi, name, ty, body) => {
54            let ty_new = abstract_fvars_at(ty, fvars, offset);
55            let body_new = abstract_fvars_at(body, fvars, offset + 1);
56            Expr::Lam(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
57        }
58        Expr::Pi(bi, name, ty, body) => {
59            let ty_new = abstract_fvars_at(ty, fvars, offset);
60            let body_new = abstract_fvars_at(body, fvars, offset + 1);
61            Expr::Pi(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
62        }
63        Expr::Let(name, ty, val, body) => {
64            let ty_new = abstract_fvars_at(ty, fvars, offset);
65            let val_new = abstract_fvars_at(val, fvars, offset);
66            let body_new = abstract_fvars_at(body, fvars, offset + 1);
67            Expr::Let(
68                name.clone(),
69                Box::new(ty_new),
70                Box::new(val_new),
71                Box::new(body_new),
72            )
73        }
74        Expr::Proj(name, idx, e) => {
75            let e_new = abstract_fvars_at(e, fvars, offset);
76            Expr::Proj(name.clone(), *idx, Box::new(e_new))
77        }
78    }
79}
80/// Abstract a single free variable, converting it to BVar(0).
81///
82/// Equivalent to `abstract_fvars(expr, [fvar])` but more efficient.
83pub fn abstract_single(expr: &Expr, fvar: FVarId) -> Expr {
84    crate::subst::abstract_expr(expr, fvar)
85}
86/// Cheap beta reduction: reduce `(λ x. body) arg` to `body[arg/x]`.
87///
88/// This only reduces the outermost beta-redex. Does not perform
89/// WHNF or any other reduction.
90pub fn cheap_beta_reduce(expr: &Expr) -> Expr {
91    match expr {
92        Expr::App(f, a) => {
93            if let Expr::Lam(_, _, _, body) = f.as_ref() {
94                let result = instantiate(body, a);
95                cheap_beta_reduce(&result)
96            } else {
97                expr.clone()
98            }
99        }
100        _ => expr.clone(),
101    }
102}
103/// Apply multiple arguments to a lambda, performing beta reduction.
104///
105/// `apply_beta(λ x y. body, [a, b])` reduces to `body[a/x, b/y]`.
106pub fn apply_beta(mut expr: Expr, args: &[Expr]) -> Expr {
107    for arg in args {
108        match expr {
109            Expr::Lam(_, _, _, body) => {
110                expr = instantiate(&body, arg);
111            }
112            _ => {
113                expr = Expr::App(Box::new(expr), Box::new(arg.clone()));
114            }
115        }
116    }
117    expr
118}
119/// Build a lambda abstraction from free variables.
120///
121/// Given `body` and free variables with their types `[(x, A), (y, B)]`,
122/// constructs `λ (x : A) (y : B). body[x→#1, y→#0]`.
123pub fn mk_lambda(fvars: &[(FVarId, Name, Expr)], body: &Expr) -> Expr {
124    let fvar_ids: Vec<FVarId> = fvars.iter().map(|(id, _, _)| *id).collect();
125    let mut result = abstract_fvars(body, &fvar_ids);
126    for (_, name, ty) in fvars.iter().rev() {
127        let ty_abs = abstract_fvars(ty, &fvar_ids);
128        result = Expr::Lam(
129            crate::BinderInfo::Default,
130            name.clone(),
131            Box::new(ty_abs),
132            Box::new(result),
133        );
134    }
135    result
136}
137/// Build a Pi type from free variables.
138///
139/// Given `body` and free variables with their types `[(x, A), (y, B)]`,
140/// constructs `Π (x : A) (y : B). body[x→#1, y→#0]`.
141pub fn mk_forall(fvars: &[(FVarId, Name, Expr)], body: &Expr) -> Expr {
142    let fvar_ids: Vec<FVarId> = fvars.iter().map(|(id, _, _)| *id).collect();
143    let mut result = abstract_fvars(body, &fvar_ids);
144    for (_, name, ty) in fvars.iter().rev() {
145        let ty_abs = abstract_fvars(ty, &fvar_ids);
146        result = Expr::Pi(
147            crate::BinderInfo::Default,
148            name.clone(),
149            Box::new(ty_abs),
150            Box::new(result),
151        );
152    }
153    result
154}
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use crate::{BinderInfo, Level};
159    #[test]
160    fn test_abstract_fvars_single() {
161        let fvar = FVarId(42);
162        let e = Expr::FVar(fvar);
163        let result = abstract_fvars(&e, &[fvar]);
164        assert_eq!(result, Expr::BVar(0));
165    }
166    #[test]
167    fn test_abstract_fvars_multiple() {
168        let x = FVarId(1);
169        let y = FVarId(2);
170        let z = FVarId(3);
171        let e = Expr::App(
172            Box::new(Expr::App(
173                Box::new(Expr::App(
174                    Box::new(Expr::Const(Name::str("f"), vec![])),
175                    Box::new(Expr::FVar(x)),
176                )),
177                Box::new(Expr::FVar(y)),
178            )),
179            Box::new(Expr::FVar(z)),
180        );
181        let result = abstract_fvars(&e, &[x, y, z]);
182        let expected = Expr::App(
183            Box::new(Expr::App(
184                Box::new(Expr::App(
185                    Box::new(Expr::Const(Name::str("f"), vec![])),
186                    Box::new(Expr::BVar(2)),
187                )),
188                Box::new(Expr::BVar(1)),
189            )),
190            Box::new(Expr::BVar(0)),
191        );
192        assert_eq!(result, expected);
193    }
194    #[test]
195    fn test_abstract_fvars_under_binder() {
196        let x = FVarId(1);
197        let e = Expr::Lam(
198            BinderInfo::Default,
199            Name::str("a"),
200            Box::new(Expr::Sort(Level::zero())),
201            Box::new(Expr::App(
202                Box::new(Expr::Const(Name::str("f"), vec![])),
203                Box::new(Expr::FVar(x)),
204            )),
205        );
206        let result = abstract_fvars(&e, &[x]);
207        if let Expr::Lam(_, _, _, body) = &result {
208            if let Expr::App(_, arg) = body.as_ref() {
209                assert_eq!(**arg, Expr::BVar(1));
210            } else {
211                panic!("Expected App");
212            }
213        } else {
214            panic!("Expected Lam");
215        }
216    }
217    #[test]
218    fn test_abstract_no_fvars() {
219        let e = Expr::Const(Name::str("Nat"), vec![]);
220        let result = abstract_fvars(&e, &[FVarId(1)]);
221        assert_eq!(result, e);
222    }
223    #[test]
224    fn test_cheap_beta_reduce() {
225        let lam = Expr::Lam(
226            BinderInfo::Default,
227            Name::str("x"),
228            Box::new(Expr::Sort(Level::zero())),
229            Box::new(Expr::BVar(0)),
230        );
231        let a = Expr::FVar(FVarId(99));
232        let app = Expr::App(Box::new(lam), Box::new(a.clone()));
233        let result = cheap_beta_reduce(&app);
234        assert_eq!(result, a);
235    }
236    #[test]
237    fn test_cheap_beta_reduce_non_redex() {
238        let app = Expr::App(
239            Box::new(Expr::Const(Name::str("f"), vec![])),
240            Box::new(Expr::BVar(0)),
241        );
242        let result = cheap_beta_reduce(&app);
243        assert_eq!(result, app);
244    }
245    #[test]
246    fn test_apply_beta() {
247        let inner = Expr::Lam(
248            BinderInfo::Default,
249            Name::str("y"),
250            Box::new(Expr::Sort(Level::zero())),
251            Box::new(Expr::BVar(1)),
252        );
253        let lam = Expr::Lam(
254            BinderInfo::Default,
255            Name::str("x"),
256            Box::new(Expr::Sort(Level::zero())),
257            Box::new(inner),
258        );
259        let a = Expr::FVar(FVarId(1));
260        let b = Expr::FVar(FVarId(2));
261        let result = apply_beta(lam, &[a.clone(), b]);
262        assert_eq!(result, a);
263    }
264    #[test]
265    fn test_abstract_roundtrip() {
266        let x = FVarId(1);
267        let y = FVarId(2);
268        let e = Expr::App(Box::new(Expr::FVar(x)), Box::new(Expr::FVar(y)));
269        let abstracted = abstract_fvars(&e, &[x, y]);
270        let back =
271            crate::instantiate::instantiate_rev(&abstracted, &[Expr::FVar(x), Expr::FVar(y)]);
272        assert_eq!(back, e);
273    }
274}
275/// Abstract a single free variable and simultaneously replace it with a let-binding.
276pub fn let_abstract(expr: &Expr, fvar: FVarId, ty: &Expr, val: &Expr) -> Expr {
277    let body = abstract_fvars(expr, &[fvar]);
278    Expr::Let(
279        Name::str("_let"),
280        Box::new(ty.clone()),
281        Box::new(val.clone()),
282        Box::new(body),
283    )
284}
285/// Count how many times a free variable appears in an expression.
286pub fn count_fvar_occurrences(expr: &Expr, fvar: FVarId) -> usize {
287    match expr {
288        Expr::FVar(id) => {
289            if *id == fvar {
290                1
291            } else {
292                0
293            }
294        }
295        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => 0,
296        Expr::App(f, a) => count_fvar_occurrences(f, fvar) + count_fvar_occurrences(a, fvar),
297        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
298            count_fvar_occurrences(ty, fvar) + count_fvar_occurrences(body, fvar)
299        }
300        Expr::Let(_, ty, val, body) => {
301            count_fvar_occurrences(ty, fvar)
302                + count_fvar_occurrences(val, fvar)
303                + count_fvar_occurrences(body, fvar)
304        }
305        Expr::Proj(_, _, e) => count_fvar_occurrences(e, fvar),
306    }
307}
308/// Collect all free variable IDs occurring in an expression.
309pub fn collect_fvars(expr: &Expr) -> std::collections::HashSet<FVarId> {
310    let mut result = std::collections::HashSet::new();
311    collect_fvars_impl(expr, &mut result);
312    result
313}
314fn collect_fvars_impl(expr: &Expr, result: &mut std::collections::HashSet<FVarId>) {
315    match expr {
316        Expr::FVar(id) => {
317            result.insert(*id);
318        }
319        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => {}
320        Expr::App(f, a) => {
321            collect_fvars_impl(f, result);
322            collect_fvars_impl(a, result);
323        }
324        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
325            collect_fvars_impl(ty, result);
326            collect_fvars_impl(body, result);
327        }
328        Expr::Let(_, ty, val, body) => {
329            collect_fvars_impl(ty, result);
330            collect_fvars_impl(val, result);
331            collect_fvars_impl(body, result);
332        }
333        Expr::Proj(_, _, e) => collect_fvars_impl(e, result),
334    }
335}
336/// Check if an expression is closed under a given set of free variables.
337pub fn is_closed_under(expr: &Expr, allowed_fvars: &[FVarId]) -> bool {
338    let free = collect_fvars(expr);
339    free.iter().all(|fv| allowed_fvars.contains(fv))
340}
341/// Replace a free variable with a given expression.
342pub fn subst_fvar(expr: &Expr, fvar: FVarId, replacement: &Expr) -> Expr {
343    match expr {
344        Expr::FVar(id) => {
345            if *id == fvar {
346                replacement.clone()
347            } else {
348                expr.clone()
349            }
350        }
351        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
352        Expr::App(f, a) => Expr::App(
353            Box::new(subst_fvar(f, fvar, replacement)),
354            Box::new(subst_fvar(a, fvar, replacement)),
355        ),
356        Expr::Lam(bi, name, ty, body) => {
357            let ty_new = subst_fvar(ty, fvar, replacement);
358            let shifted = shift_bvars(replacement, 1, 0);
359            Expr::Lam(
360                *bi,
361                name.clone(),
362                Box::new(ty_new),
363                Box::new(subst_fvar(body, fvar, &shifted)),
364            )
365        }
366        Expr::Pi(bi, name, ty, body) => {
367            let ty_new = subst_fvar(ty, fvar, replacement);
368            let shifted = shift_bvars(replacement, 1, 0);
369            Expr::Pi(
370                *bi,
371                name.clone(),
372                Box::new(ty_new),
373                Box::new(subst_fvar(body, fvar, &shifted)),
374            )
375        }
376        Expr::Let(name, ty, val, body) => {
377            let ty_new = subst_fvar(ty, fvar, replacement);
378            let val_new = subst_fvar(val, fvar, replacement);
379            let shifted = shift_bvars(replacement, 1, 0);
380            Expr::Let(
381                name.clone(),
382                Box::new(ty_new),
383                Box::new(val_new),
384                Box::new(subst_fvar(body, fvar, &shifted)),
385            )
386        }
387        Expr::Proj(name, idx, e) => Expr::Proj(
388            name.clone(),
389            *idx,
390            Box::new(subst_fvar(e, fvar, replacement)),
391        ),
392    }
393}
394/// Shift all bound variable indices >= cutoff by amount.
395pub fn shift_bvars(expr: &Expr, amount: u32, cutoff: u32) -> Expr {
396    match expr {
397        Expr::BVar(i) => {
398            if *i >= cutoff {
399                Expr::BVar(*i + amount)
400            } else {
401                expr.clone()
402            }
403        }
404        Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
405        Expr::App(f, a) => Expr::App(
406            Box::new(shift_bvars(f, amount, cutoff)),
407            Box::new(shift_bvars(a, amount, cutoff)),
408        ),
409        Expr::Lam(bi, name, ty, body) => Expr::Lam(
410            *bi,
411            name.clone(),
412            Box::new(shift_bvars(ty, amount, cutoff)),
413            Box::new(shift_bvars(body, amount, cutoff + 1)),
414        ),
415        Expr::Pi(bi, name, ty, body) => Expr::Pi(
416            *bi,
417            name.clone(),
418            Box::new(shift_bvars(ty, amount, cutoff)),
419            Box::new(shift_bvars(body, amount, cutoff + 1)),
420        ),
421        Expr::Let(name, ty, val, body) => Expr::Let(
422            name.clone(),
423            Box::new(shift_bvars(ty, amount, cutoff)),
424            Box::new(shift_bvars(val, amount, cutoff)),
425            Box::new(shift_bvars(body, amount, cutoff + 1)),
426        ),
427        Expr::Proj(name, idx, e) => {
428            Expr::Proj(name.clone(), *idx, Box::new(shift_bvars(e, amount, cutoff)))
429        }
430    }
431}
432/// Split a pi type: extract the domain and codomain.
433pub fn split_pi(expr: &Expr) -> Option<(&Expr, &Expr)> {
434    if let Expr::Pi(_, _, ty, body) = expr {
435        Some((ty, body))
436    } else {
437        None
438    }
439}
440/// Split a lambda: extract the type and body.
441pub fn split_lam(expr: &Expr) -> Option<(&Expr, &Expr)> {
442    if let Expr::Lam(_, _, ty, body) = expr {
443        Some((ty, body))
444    } else {
445        None
446    }
447}
448/// Build a nested application from a function and a list of arguments.
449pub fn mk_app(f: Expr, args: &[Expr]) -> Expr {
450    let mut result = f;
451    for arg in args {
452        result = Expr::App(Box::new(result), Box::new(arg.clone()));
453    }
454    result
455}
456/// Collect function and all arguments from a nested application.
457pub fn collect_app(expr: &Expr) -> (&Expr, Vec<&Expr>) {
458    let mut args = Vec::new();
459    let mut e = expr;
460    while let Expr::App(f, a) = e {
461        args.push(a.as_ref());
462        e = f;
463    }
464    args.reverse();
465    (e, args)
466}
467/// Count the number of leading pi binders.
468pub fn pi_arity(expr: &Expr) -> usize {
469    let mut n = 0;
470    let mut e = expr;
471    while let Expr::Pi(_, _, _, body) = e {
472        n += 1;
473        e = body;
474    }
475    n
476}
477/// Count the number of leading lambda binders.
478pub fn lam_arity(expr: &Expr) -> usize {
479    let mut n = 0;
480    let mut e = expr;
481    while let Expr::Lam(_, _, _, body) = e {
482        n += 1;
483        e = body;
484    }
485    n
486}
487#[cfg(test)]
488mod extended_tests {
489    use super::*;
490    use crate::{BinderInfo, Level};
491    #[test]
492    fn test_count_fvar_occurrences_zero() {
493        assert_eq!(count_fvar_occurrences(&Expr::BVar(0), FVarId(1)), 0);
494    }
495    #[test]
496    fn test_count_fvar_occurrences_one() {
497        let fv = FVarId(5);
498        assert_eq!(count_fvar_occurrences(&Expr::FVar(fv), fv), 1);
499    }
500    #[test]
501    fn test_count_fvar_occurrences_multiple() {
502        let fv = FVarId(5);
503        let e = Expr::App(Box::new(Expr::FVar(fv)), Box::new(Expr::FVar(fv)));
504        assert_eq!(count_fvar_occurrences(&e, fv), 2);
505    }
506    #[test]
507    fn test_collect_fvars_empty() {
508        assert!(collect_fvars(&Expr::BVar(0)).is_empty());
509    }
510    #[test]
511    fn test_collect_fvars_multi() {
512        let x = FVarId(1);
513        let y = FVarId(2);
514        let e = Expr::App(Box::new(Expr::FVar(x)), Box::new(Expr::FVar(y)));
515        let fvars = collect_fvars(&e);
516        assert!(fvars.contains(&x) && fvars.contains(&y));
517    }
518    #[test]
519    fn test_subst_fvar_replacement() {
520        let x = FVarId(1);
521        let replacement = Expr::Const(Name::str("Nat"), vec![]);
522        let result = subst_fvar(&Expr::FVar(x), x, &replacement);
523        assert_eq!(result, replacement);
524    }
525    #[test]
526    fn test_shift_bvars_above_cutoff() {
527        let shifted = shift_bvars(&Expr::BVar(2), 1, 0);
528        assert_eq!(shifted, Expr::BVar(3));
529    }
530    #[test]
531    fn test_shift_bvars_below_cutoff() {
532        let shifted = shift_bvars(&Expr::BVar(0), 5, 2);
533        assert_eq!(shifted, Expr::BVar(0));
534    }
535    #[test]
536    fn test_is_closed_under() {
537        let x = FVarId(1);
538        assert!(is_closed_under(&Expr::FVar(x), &[x]));
539        assert!(!is_closed_under(&Expr::FVar(x), &[]));
540    }
541    #[test]
542    fn test_split_pi() {
543        let ty = Expr::Sort(Level::zero());
544        let body = Expr::BVar(0);
545        let pi = Expr::Pi(
546            BinderInfo::Default,
547            Name::str("x"),
548            Box::new(ty.clone()),
549            Box::new(body.clone()),
550        );
551        let (d, c) = split_pi(&pi).expect("value should be present");
552        assert_eq!(d, &ty);
553        assert_eq!(c, &body);
554    }
555    #[test]
556    fn test_mk_app_multiple() {
557        let f = Expr::Const(Name::str("f"), vec![]);
558        let a = Expr::BVar(0);
559        let b = Expr::BVar(1);
560        let result = mk_app(f.clone(), &[a.clone(), b.clone()]);
561        let expected = Expr::App(Box::new(Expr::App(Box::new(f), Box::new(a))), Box::new(b));
562        assert_eq!(result, expected);
563    }
564    #[test]
565    fn test_collect_app() {
566        let f = Expr::Const(Name::str("f"), vec![]);
567        let a = Expr::BVar(0);
568        let b = Expr::BVar(1);
569        let app = Expr::App(
570            Box::new(Expr::App(Box::new(f.clone()), Box::new(a.clone()))),
571            Box::new(b.clone()),
572        );
573        let (head, args) = collect_app(&app);
574        assert_eq!(head, &f);
575        assert_eq!(args.len(), 2);
576    }
577    #[test]
578    fn test_pi_arity() {
579        let base = Expr::Sort(Level::zero());
580        let pi1 = Expr::Pi(
581            BinderInfo::Default,
582            Name::str("x"),
583            Box::new(base.clone()),
584            Box::new(base.clone()),
585        );
586        let pi2 = Expr::Pi(
587            BinderInfo::Default,
588            Name::str("y"),
589            Box::new(base.clone()),
590            Box::new(pi1),
591        );
592        assert_eq!(pi_arity(&pi2), 2);
593    }
594    #[test]
595    fn test_let_abstract() {
596        let x = FVarId(10);
597        let ty = Expr::Const(Name::str("Nat"), vec![]);
598        let val = Expr::Lit(crate::Literal::Nat(42));
599        let result = let_abstract(&Expr::FVar(x), x, &ty, &val);
600        assert!(matches!(result, Expr::Let(_, _, _, _)));
601    }
602}
603/// Topological sort of free variables by dependency.
604pub fn topo_sort_fvars(fvars: &[(FVarId, Expr)]) -> Result<Vec<FVarId>, String> {
605    let n = fvars.len();
606    let mut result = Vec::with_capacity(n);
607    let mut visited = vec![false; n];
608    let id_to_idx: std::collections::HashMap<FVarId, usize> = fvars
609        .iter()
610        .enumerate()
611        .map(|(i, (id, _))| (*id, i))
612        .collect();
613    fn visit(
614        i: usize,
615        fvars: &[(FVarId, Expr)],
616        id_to_idx: &std::collections::HashMap<FVarId, usize>,
617        visited: &mut Vec<bool>,
618        in_stack: &mut Vec<bool>,
619        result: &mut Vec<FVarId>,
620    ) -> Result<(), String> {
621        if in_stack[i] {
622            return Err(format!("cycle involving {:?}", fvars[i].0));
623        }
624        if visited[i] {
625            return Ok(());
626        }
627        in_stack[i] = true;
628        let deps = {
629            let mut set = std::collections::HashSet::new();
630            collect_fvars_impl(&fvars[i].1, &mut set);
631            set
632        };
633        for dep in deps {
634            if let Some(&j) = id_to_idx.get(&dep) {
635                visit(j, fvars, id_to_idx, visited, in_stack, result)?;
636            }
637        }
638        in_stack[i] = false;
639        visited[i] = true;
640        result.push(fvars[i].0);
641        Ok(())
642    }
643    let mut in_stack = vec![false; n];
644    for i in 0..n {
645        visit(
646            i,
647            fvars,
648            &id_to_idx,
649            &mut visited,
650            &mut in_stack,
651            &mut result,
652        )?;
653    }
654    Ok(result)
655}
656/// Abstract over free variables in dependency order.
657pub fn abstract_fvars_ordered(
658    expr: &Expr,
659    fvars: &[(FVarId, Expr)],
660) -> Result<(Expr, Vec<FVarId>), String> {
661    let sorted = topo_sort_fvars(fvars)?;
662    let abstracted = abstract_fvars(expr, &sorted);
663    Ok((abstracted, sorted))
664}
665/// Let-abstract multiple bindings in sequence.
666pub fn let_abstract_many(expr: &Expr, bindings: &[(FVarId, Name, Expr, Expr)]) -> Expr {
667    let fvar_ids: Vec<FVarId> = bindings.iter().map(|(id, _, _, _)| *id).collect();
668    let mut result = abstract_fvars(expr, &fvar_ids);
669    for (_, name, ty, val) in bindings.iter().rev() {
670        result = Expr::Let(
671            name.clone(),
672            Box::new(ty.clone()),
673            Box::new(val.clone()),
674            Box::new(result),
675        );
676    }
677    result
678}
679#[cfg(test)]
680mod topo_tests {
681    use super::*;
682    use crate::{BinderInfo, Level};
683    #[test]
684    fn test_topo_sort_no_deps() {
685        let x = FVarId(1);
686        let y = FVarId(2);
687        let fvars = vec![(x, Expr::BVar(0)), (y, Expr::Sort(Level::zero()))];
688        let sorted = topo_sort_fvars(&fvars).expect("sorted should be present");
689        assert_eq!(sorted.len(), 2);
690    }
691    #[test]
692    fn test_abstract_fvars_ordered_simple() {
693        let x = FVarId(1);
694        let fvars = vec![(x, Expr::Sort(Level::zero()))];
695        let (abstracted, order) =
696            abstract_fvars_ordered(&Expr::FVar(x), &fvars).expect("value should be present");
697        assert_eq!(abstracted, Expr::BVar(0));
698        assert_eq!(order, vec![x]);
699    }
700    #[test]
701    fn test_let_abstract_many_empty() {
702        let body = Expr::BVar(0);
703        let result = let_abstract_many(&body, &[]);
704        assert_eq!(result, body);
705    }
706    #[test]
707    fn test_lam_arity() {
708        let base = Expr::Sort(Level::zero());
709        let lam = Expr::Lam(
710            BinderInfo::Default,
711            Name::str("x"),
712            Box::new(base.clone()),
713            Box::new(base.clone()),
714        );
715        assert_eq!(lam_arity(&lam), 1);
716        assert_eq!(lam_arity(&base), 0);
717    }
718    #[test]
719    fn test_split_lam() {
720        let ty = Expr::Sort(Level::zero());
721        let body = Expr::BVar(0);
722        let lam = Expr::Lam(
723            BinderInfo::Default,
724            Name::str("x"),
725            Box::new(ty.clone()),
726            Box::new(body.clone()),
727        );
728        let (d, b) = split_lam(&lam).expect("value should be present");
729        assert_eq!(d, &ty);
730        assert_eq!(b, &body);
731    }
732    #[test]
733    fn test_split_pi_not_pi() {
734        assert!(split_pi(&Expr::BVar(0)).is_none());
735    }
736    #[test]
737    fn test_mk_app_empty() {
738        let f = Expr::Const(Name::str("f"), vec![]);
739        assert_eq!(mk_app(f.clone(), &[]), f);
740    }
741    #[test]
742    fn test_mk_forall_empty() {
743        let body = Expr::Sort(Level::zero());
744        assert_eq!(mk_forall(&[], &body), body);
745    }
746    #[test]
747    fn test_mk_lambda_empty() {
748        let body = Expr::BVar(0);
749        assert_eq!(mk_lambda(&[], &body), body);
750    }
751    #[test]
752    fn test_subst_fvar_identity() {
753        let x = FVarId(1);
754        let y = FVarId(2);
755        let e = Expr::FVar(x);
756        let result = subst_fvar(&e, y, &Expr::BVar(0));
757        assert_eq!(result, e);
758    }
759}
760/// Check if an expression is "ground" — no free or bound variables.
761///
762/// A ground expression is completely closed and self-contained.
763pub fn is_ground(expr: &Expr) -> bool {
764    collect_fvars(expr).is_empty() && !has_any_bvar(expr)
765}
766fn has_any_bvar(expr: &Expr) -> bool {
767    match expr {
768        Expr::BVar(_) => true,
769        Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
770        Expr::App(f, a) => has_any_bvar(f) || has_any_bvar(a),
771        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
772            has_any_bvar(ty) || has_any_bvar(body)
773        }
774        Expr::Let(_, ty, val, body) => has_any_bvar(ty) || has_any_bvar(val) || has_any_bvar(body),
775        Expr::Proj(_, _, e) => has_any_bvar(e),
776    }
777}
778/// Compute the maximum bound variable index in an expression.
779///
780/// Returns None if there are no bound variables.
781pub fn max_bvar(expr: &Expr) -> Option<u32> {
782    match expr {
783        Expr::BVar(i) => Some(*i),
784        Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => None,
785        Expr::App(f, a) => match (max_bvar(f), max_bvar(a)) {
786            (Some(x), Some(y)) => Some(x.max(y)),
787            (Some(x), None) | (None, Some(x)) => Some(x),
788            (None, None) => None,
789        },
790        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
791            match (max_bvar(ty), max_bvar(body)) {
792                (Some(x), Some(y)) => Some(x.max(y)),
793                (Some(x), None) | (None, Some(x)) => Some(x),
794                (None, None) => None,
795            }
796        }
797        Expr::Let(_, ty, val, body) => {
798            let m1 = max_bvar(ty);
799            let m2 = max_bvar(val);
800            let m3 = max_bvar(body);
801            [m1, m2, m3].iter().filter_map(|&x| x).max()
802        }
803        Expr::Proj(_, _, e) => max_bvar(e),
804    }
805}
806#[cfg(test)]
807mod ground_tests {
808    use super::*;
809    use crate::Literal;
810    #[test]
811    fn test_is_ground_lit() {
812        assert!(is_ground(&Expr::Lit(Literal::Nat(42))));
813    }
814    #[test]
815    fn test_is_ground_bvar() {
816        assert!(!is_ground(&Expr::BVar(0)));
817    }
818    #[test]
819    fn test_is_ground_fvar() {
820        assert!(!is_ground(&Expr::FVar(FVarId(1))));
821    }
822    #[test]
823    fn test_max_bvar_none() {
824        assert_eq!(max_bvar(&Expr::Lit(Literal::Nat(0))), None);
825    }
826    #[test]
827    fn test_max_bvar_some() {
828        assert_eq!(max_bvar(&Expr::BVar(3)), Some(3));
829    }
830    #[test]
831    fn test_max_bvar_app() {
832        let e = Expr::App(Box::new(Expr::BVar(2)), Box::new(Expr::BVar(5)));
833        assert_eq!(max_bvar(&e), Some(5));
834    }
835}
836/// Compute the number of distinct names (constants) in an expression.
837#[allow(dead_code)]
838pub fn distinct_const_count(expr: &Expr) -> usize {
839    let mut seen = std::collections::HashSet::new();
840    collect_const_names(expr, &mut seen);
841    seen.len()
842}
843fn collect_const_names(expr: &Expr, seen: &mut std::collections::HashSet<String>) {
844    match expr {
845        Expr::Const(name, _) => {
846            seen.insert(name.to_string());
847        }
848        Expr::App(f, a) => {
849            collect_const_names(f, seen);
850            collect_const_names(a, seen);
851        }
852        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
853            collect_const_names(ty, seen);
854            collect_const_names(body, seen);
855        }
856        Expr::Let(_, ty, val, body) => {
857            collect_const_names(ty, seen);
858            collect_const_names(val, seen);
859            collect_const_names(body, seen);
860        }
861        Expr::Proj(_, _, e) => collect_const_names(e, seen),
862        _ => {}
863    }
864}
865/// Check whether all bound variable indices are within valid range for a
866/// well-formed expression.
867///
868/// An expression with `k` open binders is valid if no `BVar(i)` appears
869/// with `i >= k + depth`.
870#[allow(dead_code)]
871pub fn bvars_in_range(expr: &Expr, open_binders: u32) -> bool {
872    check_bvars(expr, open_binders)
873}
874fn check_bvars(expr: &Expr, depth: u32) -> bool {
875    match expr {
876        Expr::BVar(i) => *i < depth,
877        Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => true,
878        Expr::App(f, a) => check_bvars(f, depth) && check_bvars(a, depth),
879        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
880            check_bvars(ty, depth) && check_bvars(body, depth + 1)
881        }
882        Expr::Let(_, ty, val, body) => {
883            check_bvars(ty, depth) && check_bvars(val, depth) && check_bvars(body, depth + 1)
884        }
885        Expr::Proj(_, _, e) => check_bvars(e, depth),
886    }
887}
888/// Rename all occurrences of `old_name` constant to `new_name`.
889#[allow(dead_code)]
890pub fn rename_const(expr: &Expr, old_name: &Name, new_name: &Name) -> Expr {
891    match expr {
892        Expr::Const(n, ls) => {
893            if n == old_name {
894                Expr::Const(new_name.clone(), ls.clone())
895            } else {
896                expr.clone()
897            }
898        }
899        Expr::App(f, a) => Expr::App(
900            Box::new(rename_const(f, old_name, new_name)),
901            Box::new(rename_const(a, old_name, new_name)),
902        ),
903        Expr::Lam(bi, name, ty, body) => Expr::Lam(
904            *bi,
905            name.clone(),
906            Box::new(rename_const(ty, old_name, new_name)),
907            Box::new(rename_const(body, old_name, new_name)),
908        ),
909        Expr::Pi(bi, name, ty, body) => Expr::Pi(
910            *bi,
911            name.clone(),
912            Box::new(rename_const(ty, old_name, new_name)),
913            Box::new(rename_const(body, old_name, new_name)),
914        ),
915        Expr::Let(name, ty, val, body) => Expr::Let(
916            name.clone(),
917            Box::new(rename_const(ty, old_name, new_name)),
918            Box::new(rename_const(val, old_name, new_name)),
919            Box::new(rename_const(body, old_name, new_name)),
920        ),
921        Expr::Proj(n, i, e) => {
922            Expr::Proj(n.clone(), *i, Box::new(rename_const(e, old_name, new_name)))
923        }
924        _ => expr.clone(),
925    }
926}
927/// Replace all literal naturals with a given value.
928#[allow(dead_code)]
929pub fn replace_nat_lit(expr: &Expr, old: u64, new: u64) -> Expr {
930    match expr {
931        Expr::Lit(crate::Literal::Nat(n)) => {
932            if *n == old {
933                Expr::Lit(crate::Literal::Nat(new))
934            } else {
935                expr.clone()
936            }
937        }
938        Expr::App(f, a) => Expr::App(
939            Box::new(replace_nat_lit(f, old, new)),
940            Box::new(replace_nat_lit(a, old, new)),
941        ),
942        Expr::Lam(bi, name, ty, body) => Expr::Lam(
943            *bi,
944            name.clone(),
945            Box::new(replace_nat_lit(ty, old, new)),
946            Box::new(replace_nat_lit(body, old, new)),
947        ),
948        Expr::Pi(bi, name, ty, body) => Expr::Pi(
949            *bi,
950            name.clone(),
951            Box::new(replace_nat_lit(ty, old, new)),
952            Box::new(replace_nat_lit(body, old, new)),
953        ),
954        Expr::Let(name, ty, val, body) => Expr::Let(
955            name.clone(),
956            Box::new(replace_nat_lit(ty, old, new)),
957            Box::new(replace_nat_lit(val, old, new)),
958            Box::new(replace_nat_lit(body, old, new)),
959        ),
960        Expr::Proj(n, i, e) => Expr::Proj(n.clone(), *i, Box::new(replace_nat_lit(e, old, new))),
961        _ => expr.clone(),
962    }
963}
964#[cfg(test)]
965mod extra_abstract_tests {
966    use super::*;
967    use crate::{BinderInfo, Level, Literal};
968    #[test]
969    fn test_distinct_const_count_zero() {
970        assert_eq!(distinct_const_count(&Expr::BVar(0)), 0);
971    }
972    #[test]
973    fn test_distinct_const_count_one() {
974        let e = Expr::Const(Name::str("Nat"), vec![]);
975        assert_eq!(distinct_const_count(&e), 1);
976    }
977    #[test]
978    fn test_distinct_const_count_dedup() {
979        let nat = Expr::Const(Name::str("Nat"), vec![]);
980        let e = Expr::App(Box::new(nat.clone()), Box::new(nat));
981        assert_eq!(distinct_const_count(&e), 1);
982    }
983    #[test]
984    fn test_bvars_in_range_closed() {
985        let e = Expr::Lit(Literal::Nat(42));
986        assert!(bvars_in_range(&e, 0));
987    }
988    #[test]
989    fn test_bvars_in_range_bvar_ok() {
990        let e = Expr::Lam(
991            BinderInfo::Default,
992            Name::str("x"),
993            Box::new(Expr::Sort(Level::zero())),
994            Box::new(Expr::BVar(0)),
995        );
996        assert!(bvars_in_range(&e, 0));
997    }
998    #[test]
999    fn test_bvars_in_range_bvar_out_of_range() {
1000        assert!(!bvars_in_range(&Expr::BVar(0), 0));
1001    }
1002    #[test]
1003    fn test_rename_const() {
1004        let e = Expr::Const(Name::str("Nat"), vec![]);
1005        let renamed = rename_const(&e, &Name::str("Nat"), &Name::str("Int"));
1006        assert!(matches!(renamed, Expr::Const(n, _) if n == Name::str("Int")));
1007    }
1008    #[test]
1009    fn test_rename_const_no_match() {
1010        let e = Expr::Const(Name::str("Bool"), vec![]);
1011        let renamed = rename_const(&e, &Name::str("Nat"), &Name::str("Int"));
1012        assert_eq!(renamed, e);
1013    }
1014    #[test]
1015    fn test_replace_nat_lit() {
1016        let e = Expr::Lit(Literal::Nat(42));
1017        let replaced = replace_nat_lit(&e, 42, 0);
1018        assert_eq!(replaced, Expr::Lit(Literal::Nat(0)));
1019    }
1020    #[test]
1021    fn test_replace_nat_lit_no_match() {
1022        let e = Expr::Lit(Literal::Nat(1));
1023        let replaced = replace_nat_lit(&e, 42, 0);
1024        assert_eq!(replaced, e);
1025    }
1026    #[test]
1027    fn test_has_any_bvar_const() {
1028        assert!(!has_any_bvar(&Expr::Const(Name::str("Nat"), vec![])));
1029    }
1030    #[test]
1031    fn test_is_ground_sort() {
1032        assert!(is_ground(&Expr::Sort(Level::zero())));
1033    }
1034    #[test]
1035    fn test_max_bvar_app() {
1036        let e = Expr::App(Box::new(Expr::BVar(5)), Box::new(Expr::BVar(3)));
1037        assert_eq!(max_bvar(&e), Some(5));
1038    }
1039}
1040#[cfg(test)]
1041mod tests_padding_infra {
1042    use super::*;
1043    #[test]
1044    fn test_stat_summary() {
1045        let mut ss = StatSummary::new();
1046        ss.record(10.0);
1047        ss.record(20.0);
1048        ss.record(30.0);
1049        assert_eq!(ss.count(), 3);
1050        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1051        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1052        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1053    }
1054    #[test]
1055    fn test_transform_stat() {
1056        let mut ts = TransformStat::new();
1057        ts.record_before(100.0);
1058        ts.record_after(80.0);
1059        let ratio = ts.mean_ratio().expect("ratio should be present");
1060        assert!((ratio - 0.8).abs() < 1e-9);
1061    }
1062    #[test]
1063    fn test_small_map() {
1064        let mut m: SmallMap<u32, &str> = SmallMap::new();
1065        m.insert(3, "three");
1066        m.insert(1, "one");
1067        m.insert(2, "two");
1068        assert_eq!(m.get(&2), Some(&"two"));
1069        assert_eq!(m.len(), 3);
1070        let keys = m.keys();
1071        assert_eq!(*keys[0], 1);
1072        assert_eq!(*keys[2], 3);
1073    }
1074    #[test]
1075    fn test_label_set() {
1076        let mut ls = LabelSet::new();
1077        ls.add("foo");
1078        ls.add("bar");
1079        ls.add("foo");
1080        assert_eq!(ls.count(), 2);
1081        assert!(ls.has("bar"));
1082        assert!(!ls.has("baz"));
1083    }
1084    #[test]
1085    fn test_config_node() {
1086        let mut root = ConfigNode::section("root");
1087        let child = ConfigNode::leaf("key", "value");
1088        root.add_child(child);
1089        assert_eq!(root.num_children(), 1);
1090    }
1091    #[test]
1092    fn test_versioned_record() {
1093        let mut vr = VersionedRecord::new(0u32);
1094        vr.update(1);
1095        vr.update(2);
1096        assert_eq!(*vr.current(), 2);
1097        assert_eq!(vr.version(), 2);
1098        assert!(vr.has_history());
1099        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1100    }
1101    #[test]
1102    fn test_simple_dag() {
1103        let mut dag = SimpleDag::new(4);
1104        dag.add_edge(0, 1);
1105        dag.add_edge(1, 2);
1106        dag.add_edge(2, 3);
1107        assert!(dag.can_reach(0, 3));
1108        assert!(!dag.can_reach(3, 0));
1109        let order = dag.topological_sort().expect("order should be present");
1110        assert_eq!(order, vec![0, 1, 2, 3]);
1111    }
1112    #[test]
1113    fn test_focus_stack() {
1114        let mut fs: FocusStack<&str> = FocusStack::new();
1115        fs.focus("a");
1116        fs.focus("b");
1117        assert_eq!(fs.current(), Some(&"b"));
1118        assert_eq!(fs.depth(), 2);
1119        fs.blur();
1120        assert_eq!(fs.current(), Some(&"a"));
1121    }
1122}
1123#[cfg(test)]
1124mod tests_extra_iterators {
1125    use super::*;
1126    #[test]
1127    fn test_window_iterator() {
1128        let data = vec![1u32, 2, 3, 4, 5];
1129        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1130        assert_eq!(windows.len(), 3);
1131        assert_eq!(windows[0], &[1, 2, 3]);
1132        assert_eq!(windows[2], &[3, 4, 5]);
1133    }
1134    #[test]
1135    fn test_non_empty_vec() {
1136        let mut nev = NonEmptyVec::singleton(10u32);
1137        nev.push(20);
1138        nev.push(30);
1139        assert_eq!(nev.len(), 3);
1140        assert_eq!(*nev.first(), 10);
1141        assert_eq!(*nev.last(), 30);
1142    }
1143}
1144#[cfg(test)]
1145mod tests_padding2 {
1146    use super::*;
1147    #[test]
1148    fn test_sliding_sum() {
1149        let mut ss = SlidingSum::new(3);
1150        ss.push(1.0);
1151        ss.push(2.0);
1152        ss.push(3.0);
1153        assert!((ss.sum() - 6.0).abs() < 1e-9);
1154        ss.push(4.0);
1155        assert!((ss.sum() - 9.0).abs() < 1e-9);
1156        assert_eq!(ss.count(), 3);
1157    }
1158    #[test]
1159    fn test_path_buf() {
1160        let mut pb = PathBuf::new();
1161        pb.push("src");
1162        pb.push("main");
1163        assert_eq!(pb.as_str(), "src/main");
1164        assert_eq!(pb.depth(), 2);
1165        pb.pop();
1166        assert_eq!(pb.as_str(), "src");
1167    }
1168    #[test]
1169    fn test_string_pool() {
1170        let mut pool = StringPool::new();
1171        let s = pool.take();
1172        assert!(s.is_empty());
1173        pool.give("hello".to_string());
1174        let s2 = pool.take();
1175        assert!(s2.is_empty());
1176        assert_eq!(pool.free_count(), 0);
1177    }
1178    #[test]
1179    fn test_transitive_closure() {
1180        let mut tc = TransitiveClosure::new(4);
1181        tc.add_edge(0, 1);
1182        tc.add_edge(1, 2);
1183        tc.add_edge(2, 3);
1184        assert!(tc.can_reach(0, 3));
1185        assert!(!tc.can_reach(3, 0));
1186        let r = tc.reachable_from(0);
1187        assert_eq!(r.len(), 4);
1188    }
1189    #[test]
1190    fn test_token_bucket() {
1191        let mut tb = TokenBucket::new(100, 10);
1192        assert_eq!(tb.available(), 100);
1193        assert!(tb.try_consume(50));
1194        assert_eq!(tb.available(), 50);
1195        assert!(!tb.try_consume(60));
1196        assert_eq!(tb.capacity(), 100);
1197    }
1198    #[test]
1199    fn test_rewrite_rule_set() {
1200        let mut rrs = RewriteRuleSet::new();
1201        rrs.add(RewriteRule::unconditional(
1202            "beta",
1203            "App(Lam(x, b), v)",
1204            "b[x:=v]",
1205        ));
1206        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1207        assert_eq!(rrs.len(), 2);
1208        assert_eq!(rrs.unconditional_rules().len(), 1);
1209        assert_eq!(rrs.conditional_rules().len(), 1);
1210        assert!(rrs.get("beta").is_some());
1211        let disp = rrs
1212            .get("beta")
1213            .expect("element at \'beta\' should exist")
1214            .display();
1215        assert!(disp.contains("→"));
1216    }
1217}
1218#[cfg(test)]
1219mod tests_padding3 {
1220    use super::*;
1221    #[test]
1222    fn test_decision_node() {
1223        let tree = DecisionNode::Branch {
1224            key: "x".into(),
1225            val: "1".into(),
1226            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1227            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1228        };
1229        let mut ctx = std::collections::HashMap::new();
1230        ctx.insert("x".into(), "1".into());
1231        assert_eq!(tree.evaluate(&ctx), "yes");
1232        ctx.insert("x".into(), "2".into());
1233        assert_eq!(tree.evaluate(&ctx), "no");
1234        assert_eq!(tree.depth(), 1);
1235    }
1236    #[test]
1237    fn test_flat_substitution() {
1238        let mut sub = FlatSubstitution::new();
1239        sub.add("foo", "bar");
1240        sub.add("baz", "qux");
1241        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1242        assert_eq!(sub.len(), 2);
1243    }
1244    #[test]
1245    fn test_stopwatch() {
1246        let mut sw = Stopwatch::start();
1247        sw.split();
1248        sw.split();
1249        assert_eq!(sw.num_splits(), 2);
1250        assert!(sw.elapsed_ms() >= 0.0);
1251        for &s in sw.splits() {
1252            assert!(s >= 0.0);
1253        }
1254    }
1255    #[test]
1256    fn test_either2() {
1257        let e: Either2<i32, &str> = Either2::First(42);
1258        assert!(e.is_first());
1259        let mapped = e.map_first(|x| x * 2);
1260        assert_eq!(mapped.first(), Some(84));
1261        let e2: Either2<i32, &str> = Either2::Second("hello");
1262        assert!(e2.is_second());
1263        assert_eq!(e2.second(), Some("hello"));
1264    }
1265    #[test]
1266    fn test_write_once() {
1267        let wo: WriteOnce<u32> = WriteOnce::new();
1268        assert!(!wo.is_written());
1269        assert!(wo.write(42));
1270        assert!(!wo.write(99));
1271        assert_eq!(wo.read(), Some(42));
1272    }
1273    #[test]
1274    fn test_sparse_vec() {
1275        let mut sv: SparseVec<i32> = SparseVec::new(100);
1276        sv.set(5, 10);
1277        sv.set(50, 20);
1278        assert_eq!(*sv.get(5), 10);
1279        assert_eq!(*sv.get(50), 20);
1280        assert_eq!(*sv.get(0), 0);
1281        assert_eq!(sv.nnz(), 2);
1282        sv.set(5, 0);
1283        assert_eq!(sv.nnz(), 1);
1284    }
1285    #[test]
1286    fn test_stack_calc() {
1287        let mut calc = StackCalc::new();
1288        calc.push(3);
1289        calc.push(4);
1290        calc.add();
1291        assert_eq!(calc.peek(), Some(7));
1292        calc.push(2);
1293        calc.mul();
1294        assert_eq!(calc.peek(), Some(14));
1295    }
1296}
1297#[cfg(test)]
1298mod tests_final_padding {
1299    use super::*;
1300    #[test]
1301    fn test_min_heap() {
1302        let mut h = MinHeap::new();
1303        h.push(5u32);
1304        h.push(1u32);
1305        h.push(3u32);
1306        assert_eq!(h.peek(), Some(&1));
1307        assert_eq!(h.pop(), Some(1));
1308        assert_eq!(h.pop(), Some(3));
1309        assert_eq!(h.pop(), Some(5));
1310        assert!(h.is_empty());
1311    }
1312    #[test]
1313    fn test_prefix_counter() {
1314        let mut pc = PrefixCounter::new();
1315        pc.record("hello");
1316        pc.record("help");
1317        pc.record("world");
1318        assert_eq!(pc.count_with_prefix("hel"), 2);
1319        assert_eq!(pc.count_with_prefix("wor"), 1);
1320        assert_eq!(pc.count_with_prefix("xyz"), 0);
1321    }
1322    #[test]
1323    fn test_fixture() {
1324        let mut f = Fixture::new();
1325        f.set("key1", "val1");
1326        f.set("key2", "val2");
1327        assert_eq!(f.get("key1"), Some("val1"));
1328        assert_eq!(f.get("key3"), None);
1329        assert_eq!(f.len(), 2);
1330    }
1331}