Skip to main content

oxilean_kernel/substitution/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::Expr;
6use crate::FVarId;
7
8use super::types::{
9    BitSet64, BucketCounter, ConfigNode, DecisionNode, Either2, Fixture, FlatSubstitution,
10    FocusStack, LabelSet, MinHeap, NonEmptyVec, PathBuf, PrefixCounter, RewriteRule,
11    RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc, StatSummary, Stopwatch,
12    StringPool, Substitution, TokenBucket, TransformStat, TransitiveClosure, VersionedRecord,
13    WindowIterator, WriteOnce,
14};
15
16/// Substitute bound variable with an expression.
17///
18/// subst(e, k, s) replaces BVar(k) with s in e
19pub fn subst(expr: &Expr, level: u32, sub: &Expr) -> Expr {
20    match expr {
21        Expr::BVar(idx) => {
22            if *idx == level {
23                sub.clone()
24            } else {
25                expr.clone()
26            }
27        }
28        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
29        Expr::Lam(info, name, ty, body) => Expr::Lam(
30            *info,
31            name.clone(),
32            Box::new(subst(ty, level, sub)),
33            Box::new(subst(body, level + 1, sub)),
34        ),
35        Expr::Pi(info, name, ty, body) => Expr::Pi(
36            *info,
37            name.clone(),
38            Box::new(subst(ty, level, sub)),
39            Box::new(subst(body, level + 1, sub)),
40        ),
41        Expr::App(f, a) => Expr::App(
42            Box::new(subst(f, level, sub)),
43            Box::new(subst(a, level, sub)),
44        ),
45        Expr::Let(name, ty, val, body) => Expr::Let(
46            name.clone(),
47            Box::new(subst(ty, level, sub)),
48            Box::new(subst(val, level, sub)),
49            Box::new(subst(body, level + 1, sub)),
50        ),
51        Expr::Proj(name, idx, e) => Expr::Proj(name.clone(), *idx, Box::new(subst(e, level, sub))),
52    }
53}
54/// Instantiate bound variable with a free variable.
55pub fn instantiate(expr: &Expr, level: u32, fvar: FVarId) -> Expr {
56    subst(expr, level, &Expr::FVar(fvar))
57}
58/// Abstract a free variable into a bound variable.
59pub fn abstract_fvar(expr: &Expr, fvar: FVarId, level: u32) -> Expr {
60    match expr {
61        Expr::FVar(v) if *v == fvar => Expr::BVar(level),
62        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
63        Expr::Lam(info, name, ty, body) => Expr::Lam(
64            *info,
65            name.clone(),
66            Box::new(abstract_fvar(ty, fvar, level)),
67            Box::new(abstract_fvar(body, fvar, level + 1)),
68        ),
69        Expr::Pi(info, name, ty, body) => Expr::Pi(
70            *info,
71            name.clone(),
72            Box::new(abstract_fvar(ty, fvar, level)),
73            Box::new(abstract_fvar(body, fvar, level + 1)),
74        ),
75        Expr::App(f, a) => Expr::App(
76            Box::new(abstract_fvar(f, fvar, level)),
77            Box::new(abstract_fvar(a, fvar, level)),
78        ),
79        Expr::Let(name, ty, val, body) => Expr::Let(
80            name.clone(),
81            Box::new(abstract_fvar(ty, fvar, level)),
82            Box::new(abstract_fvar(val, fvar, level)),
83            Box::new(abstract_fvar(body, fvar, level + 1)),
84        ),
85        Expr::Proj(name, idx, e) => {
86            Expr::Proj(name.clone(), *idx, Box::new(abstract_fvar(e, fvar, level)))
87        }
88        Expr::FVar(_) => expr.clone(),
89    }
90}
91/// Check if an expression contains a specific bound variable.
92pub fn has_bvar(expr: &Expr, level: u32) -> bool {
93    match expr {
94        Expr::BVar(idx) => *idx == level,
95        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
96        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
97            has_bvar(ty, level) || has_bvar(body, level + 1)
98        }
99        Expr::App(f, a) => has_bvar(f, level) || has_bvar(a, level),
100        Expr::Let(_, ty, val, body) => {
101            has_bvar(ty, level) || has_bvar(val, level) || has_bvar(body, level + 1)
102        }
103        Expr::Proj(_, _, e) => has_bvar(e, level),
104    }
105}
106/// Check if an expression contains a specific free variable.
107pub fn has_fvar(expr: &Expr, fvar: FVarId) -> bool {
108    match expr {
109        Expr::FVar(v) => *v == fvar,
110        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
111        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
112            has_fvar(ty, fvar) || has_fvar(body, fvar)
113        }
114        Expr::App(f, a) => has_fvar(f, fvar) || has_fvar(a, fvar),
115        Expr::Let(_, ty, val, body) => {
116            has_fvar(ty, fvar) || has_fvar(val, fvar) || has_fvar(body, fvar)
117        }
118        Expr::Proj(_, _, e) => has_fvar(e, fvar),
119    }
120}
121/// Lift (shift) all bound variable indices in `expr` by `amount` starting
122/// from the given `cutoff`.
123///
124/// This is required when inserting an expression under binders: any free
125/// reference (index ≥ cutoff) must be incremented to account for the
126/// additional binders above.
127pub fn lift(expr: &Expr, cutoff: u32, amount: u32) -> Expr {
128    match expr {
129        Expr::BVar(idx) => {
130            if *idx >= cutoff {
131                Expr::BVar(idx + amount)
132            } else {
133                expr.clone()
134            }
135        }
136        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
137        Expr::Lam(info, name, ty, body) => Expr::Lam(
138            *info,
139            name.clone(),
140            Box::new(lift(ty, cutoff, amount)),
141            Box::new(lift(body, cutoff + 1, amount)),
142        ),
143        Expr::Pi(info, name, ty, body) => Expr::Pi(
144            *info,
145            name.clone(),
146            Box::new(lift(ty, cutoff, amount)),
147            Box::new(lift(body, cutoff + 1, amount)),
148        ),
149        Expr::App(f, a) => Expr::App(
150            Box::new(lift(f, cutoff, amount)),
151            Box::new(lift(a, cutoff, amount)),
152        ),
153        Expr::Let(name, ty, val, body) => Expr::Let(
154            name.clone(),
155            Box::new(lift(ty, cutoff, amount)),
156            Box::new(lift(val, cutoff, amount)),
157            Box::new(lift(body, cutoff + 1, amount)),
158        ),
159        Expr::Proj(name, idx, e) => {
160            Expr::Proj(name.clone(), *idx, Box::new(lift(e, cutoff, amount)))
161        }
162    }
163}
164/// Lower (un-shift) all bound variable indices ≥ cutoff by `amount`.
165///
166/// This is the inverse of `lift`. Panics if any index would go below 0.
167/// Returns `None` if lowering is not safe (index would underflow).
168pub fn lower(expr: &Expr, cutoff: u32, amount: u32) -> Option<Expr> {
169    match expr {
170        Expr::BVar(idx) => {
171            if *idx >= cutoff {
172                if *idx < cutoff + amount {
173                    None
174                } else {
175                    Some(Expr::BVar(idx - amount))
176                }
177            } else {
178                Some(expr.clone())
179            }
180        }
181        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => Some(expr.clone()),
182        Expr::Lam(info, name, ty, body) => {
183            let ty2 = lower(ty, cutoff, amount)?;
184            let body2 = lower(body, cutoff + 1, amount)?;
185            Some(Expr::Lam(
186                *info,
187                name.clone(),
188                Box::new(ty2),
189                Box::new(body2),
190            ))
191        }
192        Expr::Pi(info, name, ty, body) => {
193            let ty2 = lower(ty, cutoff, amount)?;
194            let body2 = lower(body, cutoff + 1, amount)?;
195            Some(Expr::Pi(
196                *info,
197                name.clone(),
198                Box::new(ty2),
199                Box::new(body2),
200            ))
201        }
202        Expr::App(f, a) => {
203            let f2 = lower(f, cutoff, amount)?;
204            let a2 = lower(a, cutoff, amount)?;
205            Some(Expr::App(Box::new(f2), Box::new(a2)))
206        }
207        Expr::Let(name, ty, val, body) => {
208            let ty2 = lower(ty, cutoff, amount)?;
209            let val2 = lower(val, cutoff, amount)?;
210            let body2 = lower(body, cutoff + 1, amount)?;
211            Some(Expr::Let(
212                name.clone(),
213                Box::new(ty2),
214                Box::new(val2),
215                Box::new(body2),
216            ))
217        }
218        Expr::Proj(name, idx, e) => {
219            let e2 = lower(e, cutoff, amount)?;
220            Some(Expr::Proj(name.clone(), *idx, Box::new(e2)))
221        }
222    }
223}
224/// Apply a bulk substitution to an expression.
225pub(super) fn apply_subst(expr: &Expr, subst: &Substitution, offset: u32) -> Expr {
226    match expr {
227        Expr::BVar(idx) => {
228            if *idx >= offset {
229                let rel = idx - offset;
230                if let Some(replacement) = subst.get(rel) {
231                    lift(replacement, 0, offset)
232                } else {
233                    expr.clone()
234                }
235            } else {
236                expr.clone()
237            }
238        }
239        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
240        Expr::Lam(info, name, ty, body) => Expr::Lam(
241            *info,
242            name.clone(),
243            Box::new(apply_subst(ty, subst, offset)),
244            Box::new(apply_subst(body, subst, offset + 1)),
245        ),
246        Expr::Pi(info, name, ty, body) => Expr::Pi(
247            *info,
248            name.clone(),
249            Box::new(apply_subst(ty, subst, offset)),
250            Box::new(apply_subst(body, subst, offset + 1)),
251        ),
252        Expr::App(f, a) => Expr::App(
253            Box::new(apply_subst(f, subst, offset)),
254            Box::new(apply_subst(a, subst, offset)),
255        ),
256        Expr::Let(name, ty, val, body) => Expr::Let(
257            name.clone(),
258            Box::new(apply_subst(ty, subst, offset)),
259            Box::new(apply_subst(val, subst, offset)),
260            Box::new(apply_subst(body, subst, offset + 1)),
261        ),
262        Expr::Proj(name, idx, e) => {
263            Expr::Proj(name.clone(), *idx, Box::new(apply_subst(e, subst, offset)))
264        }
265    }
266}
267/// Collect all free variables (FVarId) occurring in `expr`.
268pub fn collect_fvars(expr: &Expr) -> Vec<FVarId> {
269    let mut result = Vec::new();
270    collect_fvars_impl(expr, &mut result);
271    result
272}
273pub(super) fn collect_fvars_impl(expr: &Expr, out: &mut Vec<FVarId>) {
274    match expr {
275        Expr::FVar(fv) => {
276            if !out.contains(fv) {
277                out.push(*fv);
278            }
279        }
280        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => {}
281        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
282            collect_fvars_impl(ty, out);
283            collect_fvars_impl(body, out);
284        }
285        Expr::App(f, a) => {
286            collect_fvars_impl(f, out);
287            collect_fvars_impl(a, out);
288        }
289        Expr::Let(_, ty, val, body) => {
290            collect_fvars_impl(ty, out);
291            collect_fvars_impl(val, out);
292            collect_fvars_impl(body, out);
293        }
294        Expr::Proj(_, _, e) => collect_fvars_impl(e, out),
295    }
296}
297/// Count the number of occurrences of BVar(level) in expr.
298pub fn count_bvar(expr: &Expr, level: u32) -> usize {
299    match expr {
300        Expr::BVar(idx) => {
301            if *idx == level {
302                1
303            } else {
304                0
305            }
306        }
307        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 0,
308        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
309            count_bvar(ty, level) + count_bvar(body, level + 1)
310        }
311        Expr::App(f, a) => count_bvar(f, level) + count_bvar(a, level),
312        Expr::Let(_, ty, val, body) => {
313            count_bvar(ty, level) + count_bvar(val, level) + count_bvar(body, level + 1)
314        }
315        Expr::Proj(_, _, e) => count_bvar(e, level),
316    }
317}
318/// Simultaneously substitute multiple free variables with expressions.
319///
320/// More efficient than calling `abstract_fvar` + `subst` for each variable
321/// individually.
322pub fn subst_fvars(expr: &Expr, mapping: &[(FVarId, Expr)]) -> Expr {
323    match expr {
324        Expr::FVar(fv) => {
325            if let Some((_, replacement)) = mapping.iter().find(|(id, _)| id == fv) {
326                replacement.clone()
327            } else {
328                expr.clone()
329            }
330        }
331        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
332        Expr::Lam(info, name, ty, body) => Expr::Lam(
333            *info,
334            name.clone(),
335            Box::new(subst_fvars(ty, mapping)),
336            Box::new(subst_fvars(body, mapping)),
337        ),
338        Expr::Pi(info, name, ty, body) => Expr::Pi(
339            *info,
340            name.clone(),
341            Box::new(subst_fvars(ty, mapping)),
342            Box::new(subst_fvars(body, mapping)),
343        ),
344        Expr::App(f, a) => Expr::App(
345            Box::new(subst_fvars(f, mapping)),
346            Box::new(subst_fvars(a, mapping)),
347        ),
348        Expr::Let(name, ty, val, body) => Expr::Let(
349            name.clone(),
350            Box::new(subst_fvars(ty, mapping)),
351            Box::new(subst_fvars(val, mapping)),
352            Box::new(subst_fvars(body, mapping)),
353        ),
354        Expr::Proj(name, idx, e) => {
355            Expr::Proj(name.clone(), *idx, Box::new(subst_fvars(e, mapping)))
356        }
357    }
358}
359/// Compute the syntactic depth of an expression (longest path from root to leaf).
360pub fn expr_depth(expr: &Expr) -> usize {
361    match expr {
362        Expr::BVar(_) | Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 0,
363        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
364            1 + expr_depth(ty).max(expr_depth(body))
365        }
366        Expr::App(f, a) => 1 + expr_depth(f).max(expr_depth(a)),
367        Expr::Let(_, ty, val, body) => {
368            1 + expr_depth(ty).max(expr_depth(val)).max(expr_depth(body))
369        }
370        Expr::Proj(_, _, e) => 1 + expr_depth(e),
371    }
372}
373/// Compute the number of nodes in the expression tree.
374pub fn expr_size(expr: &Expr) -> usize {
375    match expr {
376        Expr::BVar(_) | Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 1,
377        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => 1 + expr_size(ty) + expr_size(body),
378        Expr::App(f, a) => 1 + expr_size(f) + expr_size(a),
379        Expr::Let(_, ty, val, body) => 1 + expr_size(ty) + expr_size(val) + expr_size(body),
380        Expr::Proj(_, _, e) => 1 + expr_size(e),
381    }
382}
383/// Abstract a list of free variables, turning them into nested lambda binders.
384///
385/// Given `fvars = [x1, x2, x3]` and their types `tys`, produces:
386/// `fun (x1 : T1) (x2 : T2) (x3 : T3) => body`
387pub fn close_with_lambdas(
388    body: &Expr,
389    fvars: &[(FVarId, crate::Name, Expr, crate::BinderInfo)],
390) -> Expr {
391    let mut result = body.clone();
392    for (fvar, name, ty, binfo) in fvars.iter().rev() {
393        let abstracted = abstract_fvar(&result, *fvar, 0);
394        result = Expr::Lam(
395            *binfo,
396            name.clone(),
397            Box::new(ty.clone()),
398            Box::new(abstracted),
399        );
400    }
401    result
402}
403/// Abstract a list of free variables, turning them into nested pi binders.
404///
405/// Given `fvars = [x1, x2, x3]` and their types, produces:
406/// `(x1 : T1) → (x2 : T2) → (x3 : T3) → body`
407pub fn close_with_pis(
408    body: &Expr,
409    fvars: &[(FVarId, crate::Name, Expr, crate::BinderInfo)],
410) -> Expr {
411    let mut result = body.clone();
412    for (fvar, name, ty, binfo) in fvars.iter().rev() {
413        let abstracted = abstract_fvar(&result, *fvar, 0);
414        result = Expr::Pi(
415            *binfo,
416            name.clone(),
417            Box::new(ty.clone()),
418            Box::new(abstracted),
419        );
420    }
421    result
422}
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use crate::{BinderInfo, Level, Literal, Name};
427    #[test]
428    fn test_subst_bvar() {
429        let expr = Expr::BVar(0);
430        let sub = Expr::Lit(Literal::Nat(42));
431        let result = subst(&expr, 0, &sub);
432        assert_eq!(result, sub);
433    }
434    #[test]
435    fn test_subst_no_match() {
436        let expr = Expr::BVar(1);
437        let sub = Expr::Lit(Literal::Nat(42));
438        let result = subst(&expr, 0, &sub);
439        assert_eq!(result, expr);
440    }
441    #[test]
442    fn test_subst_lambda() {
443        let lam = Expr::Lam(
444            BinderInfo::Default,
445            Name::str("x"),
446            Box::new(Expr::Sort(Level::zero())),
447            Box::new(Expr::BVar(0)),
448        );
449        let sub = Expr::Lit(Literal::Nat(42));
450        let result = subst(&lam, 0, &sub);
451        assert!(matches!(result, Expr::Lam(_, _, _, _)));
452    }
453    #[test]
454    fn test_instantiate() {
455        let expr = Expr::BVar(0);
456        let fvar = FVarId(123);
457        let result = instantiate(&expr, 0, fvar);
458        assert_eq!(result, Expr::FVar(fvar));
459    }
460    #[test]
461    fn test_abstract_fvar() {
462        let expr = Expr::FVar(FVarId(123));
463        let result = abstract_fvar(&expr, FVarId(123), 0);
464        assert_eq!(result, Expr::BVar(0));
465    }
466    #[test]
467    fn test_has_bvar_true() {
468        let expr = Expr::BVar(0);
469        assert!(has_bvar(&expr, 0));
470    }
471    #[test]
472    fn test_has_bvar_false() {
473        let expr = Expr::Lit(Literal::Nat(42));
474        assert!(!has_bvar(&expr, 0));
475    }
476    #[test]
477    fn test_has_fvar_true() {
478        let expr = Expr::FVar(FVarId(123));
479        assert!(has_fvar(&expr, FVarId(123)));
480    }
481    #[test]
482    fn test_has_fvar_false() {
483        let expr = Expr::FVar(FVarId(123));
484        assert!(!has_fvar(&expr, FVarId(456)));
485    }
486    #[test]
487    fn test_lift_bvar_above_cutoff() {
488        let expr = Expr::BVar(2);
489        let lifted = lift(&expr, 1, 3);
490        assert_eq!(lifted, Expr::BVar(5));
491    }
492    #[test]
493    fn test_lift_bvar_below_cutoff() {
494        let expr = Expr::BVar(0);
495        let lifted = lift(&expr, 1, 5);
496        assert_eq!(lifted, Expr::BVar(0));
497    }
498    #[test]
499    fn test_lift_lambda() {
500        let lam = Expr::Lam(
501            BinderInfo::Default,
502            Name::str("x"),
503            Box::new(Expr::Sort(Level::zero())),
504            Box::new(Expr::BVar(1)),
505        );
506        let lifted = lift(&lam, 0, 1);
507        if let Expr::Lam(_, _, _, body) = &lifted {
508            assert_eq!(**body, Expr::BVar(2));
509        } else {
510            panic!("Expected Lam");
511        }
512    }
513    #[test]
514    fn test_lower_safe() {
515        let expr = Expr::BVar(5);
516        let result = lower(&expr, 2, 2).expect("result should be present");
517        assert_eq!(result, Expr::BVar(3));
518    }
519    #[test]
520    fn test_lower_unsafe() {
521        let expr = Expr::BVar(3);
522        let result = lower(&expr, 2, 2);
523        assert!(result.is_none());
524    }
525    #[test]
526    fn test_lower_below_cutoff() {
527        let expr = Expr::BVar(1);
528        let result = lower(&expr, 2, 2).expect("result should be present");
529        assert_eq!(result, Expr::BVar(1));
530    }
531    #[test]
532    fn test_substitution_single() {
533        let replacement = Expr::Lit(Literal::Nat(99));
534        let s = Substitution::single(replacement.clone());
535        assert_eq!(s.get(0), Some(&replacement));
536        assert_eq!(s.get(1), None);
537    }
538    #[test]
539    fn test_substitution_apply() {
540        let mut s = Substitution::new();
541        let v = Expr::Lit(Literal::Nat(7));
542        s.add(0, v.clone());
543        let expr = Expr::BVar(0);
544        let result = s.apply(&expr);
545        assert_eq!(result, v);
546    }
547    #[test]
548    fn test_substitution_no_match() {
549        let s = Substitution::new();
550        let expr = Expr::BVar(0);
551        let result = s.apply(&expr);
552        assert_eq!(result, expr);
553    }
554    #[test]
555    fn test_collect_fvars() {
556        let fv1 = FVarId(1);
557        let fv2 = FVarId(2);
558        let expr = Expr::App(Box::new(Expr::FVar(fv1)), Box::new(Expr::FVar(fv2)));
559        let fvars = collect_fvars(&expr);
560        assert_eq!(fvars.len(), 2);
561        assert!(fvars.contains(&fv1));
562        assert!(fvars.contains(&fv2));
563    }
564    #[test]
565    fn test_collect_fvars_dedup() {
566        let fv = FVarId(42);
567        let expr = Expr::App(Box::new(Expr::FVar(fv)), Box::new(Expr::FVar(fv)));
568        let fvars = collect_fvars(&expr);
569        assert_eq!(fvars.len(), 1);
570    }
571    #[test]
572    fn test_count_bvar() {
573        let expr = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(0)));
574        assert_eq!(count_bvar(&expr, 0), 2);
575        assert_eq!(count_bvar(&expr, 1), 0);
576    }
577    #[test]
578    fn test_subst_fvars() {
579        let fv1 = FVarId(10);
580        let fv2 = FVarId(20);
581        let expr = Expr::App(Box::new(Expr::FVar(fv1)), Box::new(Expr::FVar(fv2)));
582        let r1 = Expr::Lit(Literal::Nat(1));
583        let r2 = Expr::Lit(Literal::Nat(2));
584        let result = subst_fvars(&expr, &[(fv1, r1.clone()), (fv2, r2.clone())]);
585        assert_eq!(result, Expr::App(Box::new(r1), Box::new(r2)));
586    }
587    #[test]
588    fn test_expr_depth() {
589        let leaf = Expr::Lit(Literal::Nat(0));
590        assert_eq!(expr_depth(&leaf), 0);
591        let app = Expr::App(Box::new(leaf.clone()), Box::new(leaf.clone()));
592        assert_eq!(expr_depth(&app), 1);
593        let nested = Expr::App(Box::new(app.clone()), Box::new(app));
594        assert_eq!(expr_depth(&nested), 2);
595    }
596    #[test]
597    fn test_expr_size() {
598        let leaf = Expr::Lit(Literal::Nat(0));
599        assert_eq!(expr_size(&leaf), 1);
600        let app = Expr::App(Box::new(leaf.clone()), Box::new(leaf.clone()));
601        assert_eq!(expr_size(&app), 3);
602    }
603    #[test]
604    fn test_close_with_lambdas() {
605        let fvar = FVarId(1);
606        let ty = Expr::Sort(Level::zero());
607        let body = Expr::FVar(fvar);
608        let closed = close_with_lambdas(
609            &body,
610            &[(fvar, Name::str("x"), ty.clone(), BinderInfo::Default)],
611        );
612        if let Expr::Lam(_, name, _, b) = &closed {
613            assert_eq!(*name, Name::str("x"));
614            assert_eq!(**b, Expr::BVar(0));
615        } else {
616            panic!("Expected Lam");
617        }
618    }
619    #[test]
620    fn test_close_with_pis() {
621        let fvar = FVarId(5);
622        let ty = Expr::Sort(Level::zero());
623        let body = Expr::FVar(fvar);
624        let closed = close_with_pis(
625            &body,
626            &[(fvar, Name::str("x"), ty.clone(), BinderInfo::Default)],
627        );
628        if let Expr::Pi(_, name, _, b) = &closed {
629            assert_eq!(*name, Name::str("x"));
630            assert_eq!(**b, Expr::BVar(0));
631        } else {
632            panic!("Expected Pi");
633        }
634    }
635}
636/// Check if an expression is closed (no free BVars at depth 0).
637pub fn is_closed(expr: &Expr) -> bool {
638    !has_bvar(expr, 0)
639}
640/// Replace all occurrences of a constant name with another expression.
641pub fn subst_const(expr: &Expr, from: &crate::Name, to: &Expr) -> Expr {
642    match expr {
643        Expr::Const(n, _) if n == from => to.clone(),
644        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => {
645            expr.clone()
646        }
647        Expr::App(f, a) => Expr::App(
648            Box::new(subst_const(f, from, to)),
649            Box::new(subst_const(a, from, to)),
650        ),
651        Expr::Lam(info, name, ty, body) => Expr::Lam(
652            *info,
653            name.clone(),
654            Box::new(subst_const(ty, from, to)),
655            Box::new(subst_const(body, from, to)),
656        ),
657        Expr::Pi(info, name, ty, body) => Expr::Pi(
658            *info,
659            name.clone(),
660            Box::new(subst_const(ty, from, to)),
661            Box::new(subst_const(body, from, to)),
662        ),
663        Expr::Let(name, ty, val, body) => Expr::Let(
664            name.clone(),
665            Box::new(subst_const(ty, from, to)),
666            Box::new(subst_const(val, from, to)),
667            Box::new(subst_const(body, from, to)),
668        ),
669        Expr::Proj(name, idx, e) => {
670            Expr::Proj(name.clone(), *idx, Box::new(subst_const(e, from, to)))
671        }
672    }
673}
674/// Rename a bound variable (change binder name without affecting semantics).
675///
676/// This is a no-op on the expression structure but useful for pretty-printing.
677pub fn rename_binder(expr: &Expr, old_name: &crate::Name, new_name: crate::Name) -> Expr {
678    match expr {
679        Expr::Lam(info, name, ty, body) if name == old_name => Expr::Lam(
680            *info,
681            new_name.clone(),
682            Box::new(rename_binder(ty, old_name, new_name.clone())),
683            Box::new(rename_binder(body, old_name, new_name)),
684        ),
685        Expr::Pi(info, name, ty, body) if name == old_name => Expr::Pi(
686            *info,
687            new_name.clone(),
688            Box::new(rename_binder(ty, old_name, new_name.clone())),
689            Box::new(rename_binder(body, old_name, new_name)),
690        ),
691        Expr::Lam(info, name, ty, body) => Expr::Lam(
692            *info,
693            name.clone(),
694            Box::new(rename_binder(ty, old_name, new_name.clone())),
695            Box::new(rename_binder(body, old_name, new_name)),
696        ),
697        Expr::Pi(info, name, ty, body) => Expr::Pi(
698            *info,
699            name.clone(),
700            Box::new(rename_binder(ty, old_name, new_name.clone())),
701            Box::new(rename_binder(body, old_name, new_name)),
702        ),
703        Expr::App(f, a) => Expr::App(
704            Box::new(rename_binder(f, old_name, new_name.clone())),
705            Box::new(rename_binder(a, old_name, new_name)),
706        ),
707        Expr::Let(name, ty, val, body) => Expr::Let(
708            name.clone(),
709            Box::new(rename_binder(ty, old_name, new_name.clone())),
710            Box::new(rename_binder(val, old_name, new_name.clone())),
711            Box::new(rename_binder(body, old_name, new_name)),
712        ),
713        Expr::Proj(name, idx, e) => Expr::Proj(
714            name.clone(),
715            *idx,
716            Box::new(rename_binder(e, old_name, new_name)),
717        ),
718        _ => expr.clone(),
719    }
720}
721/// Collect all constant names that appear in an expression (deduplicated).
722pub fn collect_consts(expr: &Expr) -> Vec<crate::Name> {
723    let mut result = Vec::new();
724    collect_consts_impl(expr, &mut result);
725    result
726}
727pub(super) fn collect_consts_impl(expr: &Expr, out: &mut Vec<crate::Name>) {
728    match expr {
729        Expr::Const(n, _) => {
730            if !out.contains(n) {
731                out.push(n.clone());
732            }
733        }
734        Expr::BVar(_) | Expr::Sort(_) | Expr::FVar(_) | Expr::Lit(_) => {}
735        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
736            collect_consts_impl(ty, out);
737            collect_consts_impl(body, out);
738        }
739        Expr::App(f, a) => {
740            collect_consts_impl(f, out);
741            collect_consts_impl(a, out);
742        }
743        Expr::Let(_, ty, val, body) => {
744            collect_consts_impl(ty, out);
745            collect_consts_impl(val, out);
746            collect_consts_impl(body, out);
747        }
748        Expr::Proj(_, _, e) => collect_consts_impl(e, out),
749    }
750}
751/// Count occurrences of a free variable `fv` in an expression.
752pub fn count_fvar(expr: &Expr, fv: FVarId) -> usize {
753    match expr {
754        Expr::FVar(v) => {
755            if *v == fv {
756                1
757            } else {
758                0
759            }
760        }
761        Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => 0,
762        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
763            count_fvar(ty, fv) + count_fvar(body, fv)
764        }
765        Expr::App(f, a) => count_fvar(f, fv) + count_fvar(a, fv),
766        Expr::Let(_, ty, val, body) => {
767            count_fvar(ty, fv) + count_fvar(val, fv) + count_fvar(body, fv)
768        }
769        Expr::Proj(_, _, e) => count_fvar(e, fv),
770    }
771}
772/// Check whether `expr` is a closed term (no BVars and no FVars).
773pub fn is_ground(expr: &Expr) -> bool {
774    match expr {
775        Expr::FVar(_) | Expr::BVar(_) => false,
776        Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => true,
777        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => is_ground(ty) && is_ground(body),
778        Expr::App(f, a) => is_ground(f) && is_ground(a),
779        Expr::Let(_, ty, val, body) => is_ground(ty) && is_ground(val) && is_ground(body),
780        Expr::Proj(_, _, e) => is_ground(e),
781    }
782}
783/// Simultaneously lift all FVars from `mapping` by replacing them with BVars
784/// starting at `start_level`.  The first entry in `mapping` maps to BVar(start_level),
785/// the second to BVar(start_level+1), etc.
786pub fn abstract_fvars_ordered(expr: &Expr, fvars: &[FVarId], start_level: u32) -> Expr {
787    let mut result = expr.clone();
788    for (i, &fv) in fvars.iter().enumerate() {
789        let level = start_level + i as u32;
790        result = abstract_fvar(&result, fv, level);
791    }
792    result
793}
794/// Check if two expressions are alpha-equivalent (identical up to bound variable
795/// renaming).  This is a simple structural check — it does NOT reduce.
796pub fn alpha_eq(e1: &Expr, e2: &Expr) -> bool {
797    match (e1, e2) {
798        (Expr::BVar(i), Expr::BVar(j)) => i == j,
799        (Expr::FVar(a), Expr::FVar(b)) => a == b,
800        (Expr::Sort(l1), Expr::Sort(l2)) => l1 == l2,
801        (Expr::Const(n1, ls1), Expr::Const(n2, ls2)) => n1 == n2 && ls1 == ls2,
802        (Expr::Lit(l1), Expr::Lit(l2)) => l1 == l2,
803        (Expr::App(f1, a1), Expr::App(f2, a2)) => alpha_eq(f1, f2) && alpha_eq(a1, a2),
804        (Expr::Lam(i1, _, ty1, b1), Expr::Lam(i2, _, ty2, b2)) => {
805            i1 == i2 && alpha_eq(ty1, ty2) && alpha_eq(b1, b2)
806        }
807        (Expr::Pi(i1, _, ty1, b1), Expr::Pi(i2, _, ty2, b2)) => {
808            i1 == i2 && alpha_eq(ty1, ty2) && alpha_eq(b1, b2)
809        }
810        (Expr::Let(_, ty1, v1, b1), Expr::Let(_, ty2, v2, b2)) => {
811            alpha_eq(ty1, ty2) && alpha_eq(v1, v2) && alpha_eq(b1, b2)
812        }
813        (Expr::Proj(n1, i1, e1), Expr::Proj(n2, i2, e2)) => {
814            n1 == n2 && i1 == i2 && alpha_eq(e1, e2)
815        }
816        _ => false,
817    }
818}
819#[cfg(test)]
820mod extra_subst_tests {
821    use super::*;
822    use crate::{BinderInfo, Level, Literal, Name};
823    #[test]
824    fn test_collect_consts_app() {
825        let e = Expr::App(
826            Box::new(Expr::Const(Name::str("f"), vec![])),
827            Box::new(Expr::Const(Name::str("g"), vec![])),
828        );
829        let consts = collect_consts(&e);
830        assert!(consts.contains(&Name::str("f")));
831        assert!(consts.contains(&Name::str("g")));
832        assert_eq!(consts.len(), 2);
833    }
834    #[test]
835    fn test_collect_consts_deduplicated() {
836        let e = Expr::App(
837            Box::new(Expr::Const(Name::str("f"), vec![])),
838            Box::new(Expr::Const(Name::str("f"), vec![])),
839        );
840        assert_eq!(collect_consts(&e).len(), 1);
841    }
842    #[test]
843    fn test_count_fvar_present() {
844        let fv = FVarId(7);
845        let e = Expr::App(Box::new(Expr::FVar(fv)), Box::new(Expr::FVar(fv)));
846        assert_eq!(count_fvar(&e, fv), 2);
847    }
848    #[test]
849    fn test_count_fvar_absent() {
850        let fv = FVarId(7);
851        let e = Expr::Lit(Literal::Nat(0));
852        assert_eq!(count_fvar(&e, fv), 0);
853    }
854    #[test]
855    fn test_is_ground_literal() {
856        assert!(is_ground(&Expr::Lit(Literal::Nat(0))));
857    }
858    #[test]
859    fn test_is_ground_bvar() {
860        assert!(!is_ground(&Expr::BVar(0)));
861    }
862    #[test]
863    fn test_is_ground_fvar() {
864        assert!(!is_ground(&Expr::FVar(FVarId(1))));
865    }
866    #[test]
867    fn test_is_ground_sort() {
868        assert!(is_ground(&Expr::Sort(Level::zero())));
869    }
870    #[test]
871    fn test_alpha_eq_bvar() {
872        assert!(alpha_eq(&Expr::BVar(0), &Expr::BVar(0)));
873        assert!(!alpha_eq(&Expr::BVar(0), &Expr::BVar(1)));
874    }
875    #[test]
876    fn test_alpha_eq_literal() {
877        assert!(alpha_eq(
878            &Expr::Lit(Literal::Nat(5)),
879            &Expr::Lit(Literal::Nat(5))
880        ));
881        assert!(!alpha_eq(
882            &Expr::Lit(Literal::Nat(5)),
883            &Expr::Lit(Literal::Nat(6))
884        ));
885    }
886    #[test]
887    fn test_alpha_eq_lambda_ignores_name() {
888        let l1 = Expr::Lam(
889            BinderInfo::Default,
890            Name::str("x"),
891            Box::new(Expr::Sort(Level::zero())),
892            Box::new(Expr::BVar(0)),
893        );
894        let l2 = Expr::Lam(
895            BinderInfo::Default,
896            Name::str("y"),
897            Box::new(Expr::Sort(Level::zero())),
898            Box::new(Expr::BVar(0)),
899        );
900        assert!(alpha_eq(&l1, &l2));
901    }
902    #[test]
903    fn test_abstract_fvars_ordered() {
904        let fv0 = FVarId(100);
905        let fv1 = FVarId(101);
906        let e = Expr::App(Box::new(Expr::FVar(fv0)), Box::new(Expr::FVar(fv1)));
907        let result = abstract_fvars_ordered(&e, &[fv0, fv1], 0);
908        assert_eq!(
909            result,
910            Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(1)))
911        );
912    }
913    #[test]
914    fn test_rename_binder() {
915        let lam = Expr::Lam(
916            BinderInfo::Default,
917            Name::str("x"),
918            Box::new(Expr::Sort(Level::zero())),
919            Box::new(Expr::BVar(0)),
920        );
921        let renamed = rename_binder(&lam, &Name::str("x"), Name::str("y"));
922        if let Expr::Lam(_, name, _, _) = renamed {
923            assert_eq!(name, Name::str("y"));
924        } else {
925            panic!("Expected Lam");
926        }
927    }
928}
929#[cfg(test)]
930mod tests_padding_infra {
931    use super::*;
932    #[test]
933    fn test_stat_summary() {
934        let mut ss = StatSummary::new();
935        ss.record(10.0);
936        ss.record(20.0);
937        ss.record(30.0);
938        assert_eq!(ss.count(), 3);
939        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
940        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
941        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
942    }
943    #[test]
944    fn test_transform_stat() {
945        let mut ts = TransformStat::new();
946        ts.record_before(100.0);
947        ts.record_after(80.0);
948        let ratio = ts.mean_ratio().expect("ratio should be present");
949        assert!((ratio - 0.8).abs() < 1e-9);
950    }
951    #[test]
952    fn test_small_map() {
953        let mut m: SmallMap<u32, &str> = SmallMap::new();
954        m.insert(3, "three");
955        m.insert(1, "one");
956        m.insert(2, "two");
957        assert_eq!(m.get(&2), Some(&"two"));
958        assert_eq!(m.len(), 3);
959        let keys = m.keys();
960        assert_eq!(*keys[0], 1);
961        assert_eq!(*keys[2], 3);
962    }
963    #[test]
964    fn test_label_set() {
965        let mut ls = LabelSet::new();
966        ls.add("foo");
967        ls.add("bar");
968        ls.add("foo");
969        assert_eq!(ls.count(), 2);
970        assert!(ls.has("bar"));
971        assert!(!ls.has("baz"));
972    }
973    #[test]
974    fn test_config_node() {
975        let mut root = ConfigNode::section("root");
976        let child = ConfigNode::leaf("key", "value");
977        root.add_child(child);
978        assert_eq!(root.num_children(), 1);
979    }
980    #[test]
981    fn test_versioned_record() {
982        let mut vr = VersionedRecord::new(0u32);
983        vr.update(1);
984        vr.update(2);
985        assert_eq!(*vr.current(), 2);
986        assert_eq!(vr.version(), 2);
987        assert!(vr.has_history());
988        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
989    }
990    #[test]
991    fn test_simple_dag() {
992        let mut dag = SimpleDag::new(4);
993        dag.add_edge(0, 1);
994        dag.add_edge(1, 2);
995        dag.add_edge(2, 3);
996        assert!(dag.can_reach(0, 3));
997        assert!(!dag.can_reach(3, 0));
998        let order = dag.topological_sort().expect("order should be present");
999        assert_eq!(order, vec![0, 1, 2, 3]);
1000    }
1001    #[test]
1002    fn test_focus_stack() {
1003        let mut fs: FocusStack<&str> = FocusStack::new();
1004        fs.focus("a");
1005        fs.focus("b");
1006        assert_eq!(fs.current(), Some(&"b"));
1007        assert_eq!(fs.depth(), 2);
1008        fs.blur();
1009        assert_eq!(fs.current(), Some(&"a"));
1010    }
1011}
1012#[cfg(test)]
1013mod tests_extra_iterators {
1014    use super::*;
1015    #[test]
1016    fn test_window_iterator() {
1017        let data = vec![1u32, 2, 3, 4, 5];
1018        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1019        assert_eq!(windows.len(), 3);
1020        assert_eq!(windows[0], &[1, 2, 3]);
1021        assert_eq!(windows[2], &[3, 4, 5]);
1022    }
1023    #[test]
1024    fn test_non_empty_vec() {
1025        let mut nev = NonEmptyVec::singleton(10u32);
1026        nev.push(20);
1027        nev.push(30);
1028        assert_eq!(nev.len(), 3);
1029        assert_eq!(*nev.first(), 10);
1030        assert_eq!(*nev.last(), 30);
1031    }
1032}
1033#[cfg(test)]
1034mod tests_padding2 {
1035    use super::*;
1036    #[test]
1037    fn test_sliding_sum() {
1038        let mut ss = SlidingSum::new(3);
1039        ss.push(1.0);
1040        ss.push(2.0);
1041        ss.push(3.0);
1042        assert!((ss.sum() - 6.0).abs() < 1e-9);
1043        ss.push(4.0);
1044        assert!((ss.sum() - 9.0).abs() < 1e-9);
1045        assert_eq!(ss.count(), 3);
1046    }
1047    #[test]
1048    fn test_path_buf() {
1049        let mut pb = PathBuf::new();
1050        pb.push("src");
1051        pb.push("main");
1052        assert_eq!(pb.as_str(), "src/main");
1053        assert_eq!(pb.depth(), 2);
1054        pb.pop();
1055        assert_eq!(pb.as_str(), "src");
1056    }
1057    #[test]
1058    fn test_string_pool() {
1059        let mut pool = StringPool::new();
1060        let s = pool.take();
1061        assert!(s.is_empty());
1062        pool.give("hello".to_string());
1063        let s2 = pool.take();
1064        assert!(s2.is_empty());
1065        assert_eq!(pool.free_count(), 0);
1066    }
1067    #[test]
1068    fn test_transitive_closure() {
1069        let mut tc = TransitiveClosure::new(4);
1070        tc.add_edge(0, 1);
1071        tc.add_edge(1, 2);
1072        tc.add_edge(2, 3);
1073        assert!(tc.can_reach(0, 3));
1074        assert!(!tc.can_reach(3, 0));
1075        let r = tc.reachable_from(0);
1076        assert_eq!(r.len(), 4);
1077    }
1078    #[test]
1079    fn test_token_bucket() {
1080        let mut tb = TokenBucket::new(100, 10);
1081        assert_eq!(tb.available(), 100);
1082        assert!(tb.try_consume(50));
1083        assert_eq!(tb.available(), 50);
1084        assert!(!tb.try_consume(60));
1085        assert_eq!(tb.capacity(), 100);
1086    }
1087    #[test]
1088    fn test_rewrite_rule_set() {
1089        let mut rrs = RewriteRuleSet::new();
1090        rrs.add(RewriteRule::unconditional(
1091            "beta",
1092            "App(Lam(x, b), v)",
1093            "b[x:=v]",
1094        ));
1095        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1096        assert_eq!(rrs.len(), 2);
1097        assert_eq!(rrs.unconditional_rules().len(), 1);
1098        assert_eq!(rrs.conditional_rules().len(), 1);
1099        assert!(rrs.get("beta").is_some());
1100        let disp = rrs
1101            .get("beta")
1102            .expect("element at \'beta\' should exist")
1103            .display();
1104        assert!(disp.contains("→"));
1105    }
1106}
1107#[cfg(test)]
1108mod tests_padding3 {
1109    use super::*;
1110    #[test]
1111    fn test_decision_node() {
1112        let tree = DecisionNode::Branch {
1113            key: "x".into(),
1114            val: "1".into(),
1115            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1116            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1117        };
1118        let mut ctx = std::collections::HashMap::new();
1119        ctx.insert("x".into(), "1".into());
1120        assert_eq!(tree.evaluate(&ctx), "yes");
1121        ctx.insert("x".into(), "2".into());
1122        assert_eq!(tree.evaluate(&ctx), "no");
1123        assert_eq!(tree.depth(), 1);
1124    }
1125    #[test]
1126    fn test_flat_substitution() {
1127        let mut sub = FlatSubstitution::new();
1128        sub.add("foo", "bar");
1129        sub.add("baz", "qux");
1130        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1131        assert_eq!(sub.len(), 2);
1132    }
1133    #[test]
1134    fn test_stopwatch() {
1135        let mut sw = Stopwatch::start();
1136        sw.split();
1137        sw.split();
1138        assert_eq!(sw.num_splits(), 2);
1139        assert!(sw.elapsed_ms() >= 0.0);
1140        for &s in sw.splits() {
1141            assert!(s >= 0.0);
1142        }
1143    }
1144    #[test]
1145    fn test_either2() {
1146        let e: Either2<i32, &str> = Either2::First(42);
1147        assert!(e.is_first());
1148        let mapped = e.map_first(|x| x * 2);
1149        assert_eq!(mapped.first(), Some(84));
1150        let e2: Either2<i32, &str> = Either2::Second("hello");
1151        assert!(e2.is_second());
1152        assert_eq!(e2.second(), Some("hello"));
1153    }
1154    #[test]
1155    fn test_write_once() {
1156        let wo: WriteOnce<u32> = WriteOnce::new();
1157        assert!(!wo.is_written());
1158        assert!(wo.write(42));
1159        assert!(!wo.write(99));
1160        assert_eq!(wo.read(), Some(42));
1161    }
1162    #[test]
1163    fn test_sparse_vec() {
1164        let mut sv: SparseVec<i32> = SparseVec::new(100);
1165        sv.set(5, 10);
1166        sv.set(50, 20);
1167        assert_eq!(*sv.get(5), 10);
1168        assert_eq!(*sv.get(50), 20);
1169        assert_eq!(*sv.get(0), 0);
1170        assert_eq!(sv.nnz(), 2);
1171        sv.set(5, 0);
1172        assert_eq!(sv.nnz(), 1);
1173    }
1174    #[test]
1175    fn test_stack_calc() {
1176        let mut calc = StackCalc::new();
1177        calc.push(3);
1178        calc.push(4);
1179        calc.add();
1180        assert_eq!(calc.peek(), Some(7));
1181        calc.push(2);
1182        calc.mul();
1183        assert_eq!(calc.peek(), Some(14));
1184    }
1185}
1186#[cfg(test)]
1187mod tests_final_padding {
1188    use super::*;
1189    #[test]
1190    fn test_min_heap() {
1191        let mut h = MinHeap::new();
1192        h.push(5u32);
1193        h.push(1u32);
1194        h.push(3u32);
1195        assert_eq!(h.peek(), Some(&1));
1196        assert_eq!(h.pop(), Some(1));
1197        assert_eq!(h.pop(), Some(3));
1198        assert_eq!(h.pop(), Some(5));
1199        assert!(h.is_empty());
1200    }
1201    #[test]
1202    fn test_prefix_counter() {
1203        let mut pc = PrefixCounter::new();
1204        pc.record("hello");
1205        pc.record("help");
1206        pc.record("world");
1207        assert_eq!(pc.count_with_prefix("hel"), 2);
1208        assert_eq!(pc.count_with_prefix("wor"), 1);
1209        assert_eq!(pc.count_with_prefix("xyz"), 0);
1210    }
1211    #[test]
1212    fn test_fixture() {
1213        let mut f = Fixture::new();
1214        f.set("key1", "val1");
1215        f.set("key2", "val2");
1216        assert_eq!(f.get("key1"), Some("val1"));
1217        assert_eq!(f.get("key3"), None);
1218        assert_eq!(f.len(), 2);
1219    }
1220}
1221#[cfg(test)]
1222mod tests_tiny_padding {
1223    use super::*;
1224    #[test]
1225    fn test_bitset64() {
1226        let mut bs = BitSet64::new();
1227        bs.insert(0);
1228        bs.insert(63);
1229        assert!(bs.contains(0));
1230        assert!(bs.contains(63));
1231        assert!(!bs.contains(1));
1232        assert_eq!(bs.len(), 2);
1233        bs.remove(0);
1234        assert!(!bs.contains(0));
1235    }
1236    #[test]
1237    fn test_bucket_counter() {
1238        let mut bc: BucketCounter<4> = BucketCounter::new();
1239        bc.inc(0);
1240        bc.inc(0);
1241        bc.inc(1);
1242        assert_eq!(bc.get(0), 2);
1243        assert_eq!(bc.total(), 3);
1244        assert_eq!(bc.argmax(), 0);
1245    }
1246}