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