Skip to main content

oxilean_kernel/normalize/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::Node;
6use crate::{Environment, Expr, Reducer};
7use std::collections::HashMap;
8use std::rc::Rc;
9
10use super::types::{
11    ConfigNode, DecisionNode, Either2, Fixture, FlatSubstitution, FocusStack, LabelSet, LazyNormal,
12    MemoizedNormalizer, MinHeap, NonEmptyVec, NormStats, NormStrategy, PathBuf, PrefixCounter,
13    RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc,
14    StatSummary, Stopwatch, StringPool, TokenBucket, TransformStat, TransitiveClosure,
15    VersionedRecord, WindowIterator, WriteOnce,
16};
17
18/// Normalize an expression to full normal form (no environment).
19///
20/// Reduces all beta-redexes, let-bindings, and under all binders.
21pub fn normalize(expr: &Expr) -> Expr {
22    let mut reducer = Reducer::new();
23    normalize_impl(&mut reducer, expr, None)
24}
25/// Normalize an expression with an environment.
26///
27/// Also unfolds definitions according to the environment.
28pub fn normalize_env(expr: &Expr, env: &Environment) -> Expr {
29    let mut reducer = Reducer::new();
30    normalize_impl(&mut reducer, expr, Some(env))
31}
32/// Internal normalization with optional environment.
33pub(super) fn normalize_impl(
34    reducer: &mut Reducer,
35    expr: &Expr,
36    env: Option<&Environment>,
37) -> Expr {
38    match expr {
39        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Lit(_) => expr.clone(),
40        Expr::Const(_, _) => {
41            let whnf = match env {
42                Some(e) => reducer.whnf_env(expr, e),
43                None => reducer.whnf(expr),
44            };
45            if whnf == *expr {
46                expr.clone()
47            } else {
48                normalize_impl(reducer, &whnf, env)
49            }
50        }
51        Expr::Lam(info, name, ty, body) => {
52            let ty_norm = normalize_impl(reducer, ty, env);
53            let body_norm = normalize_impl(reducer, body, env);
54            Expr::Lam(
55                *info,
56                name.clone(),
57                Node::new(ty_norm),
58                Node::new(body_norm),
59            )
60        }
61        Expr::Pi(info, name, ty, body) => {
62            let ty_norm = normalize_impl(reducer, ty, env);
63            let body_norm = normalize_impl(reducer, body, env);
64            Expr::Pi(
65                *info,
66                name.clone(),
67                Node::new(ty_norm),
68                Node::new(body_norm),
69            )
70        }
71        Expr::App(_, _) => {
72            let whnf = match env {
73                Some(e) => reducer.whnf_env(expr, e),
74                None => reducer.whnf(expr),
75            };
76            if let Expr::App(f_whnf, a_whnf) = whnf {
77                let f_norm = normalize_impl(reducer, &f_whnf, env);
78                let a_norm = normalize_impl(reducer, &a_whnf, env);
79                Expr::App(Node::new(f_norm), Node::new(a_norm))
80            } else {
81                normalize_impl(reducer, &whnf, env)
82            }
83        }
84        Expr::Let(_, _, _, _) => {
85            let whnf = match env {
86                Some(e) => reducer.whnf_env(expr, e),
87                None => reducer.whnf(expr),
88            };
89            normalize_impl(reducer, &whnf, env)
90        }
91        Expr::Proj(name, idx, e) => {
92            let whnf = match env {
93                Some(env) => reducer.whnf_env(expr, env),
94                None => reducer.whnf(expr),
95            };
96            if let Expr::Proj(_, _, _) = &whnf {
97                let e_norm = normalize_impl(reducer, e, env);
98                Expr::Proj(name.clone(), *idx, Node::new(e_norm))
99            } else {
100                normalize_impl(reducer, &whnf, env)
101            }
102        }
103    }
104}
105/// Check if two expressions are equal after full normalization.
106pub fn alpha_eq(e1: &Expr, e2: &Expr) -> bool {
107    let n1 = normalize(e1);
108    let n2 = normalize(e2);
109    n1 == n2
110}
111/// Check if two expressions are equal after full normalization with environment.
112pub fn alpha_eq_env(e1: &Expr, e2: &Expr, env: &Environment) -> bool {
113    let n1 = normalize_env(e1, env);
114    let n2 = normalize_env(e2, env);
115    n1 == n2
116}
117/// Compute weak head normal form and then fully normalize.
118pub fn normalize_whnf(expr: &Expr) -> Expr {
119    let mut reducer = Reducer::new();
120    let whnf = reducer.whnf(expr);
121    normalize_impl(&mut reducer, &whnf, None)
122}
123/// Check if an expression is already in normal form.
124///
125/// An expression is in normal form if normalizing it yields
126/// the same expression.
127pub fn is_normal_form(expr: &Expr) -> bool {
128    let normalized = normalize(expr);
129    normalized == *expr
130}
131/// Evaluate an expression with an environment to a value.
132///
133/// This is like normalize_env but stops at constructors and
134/// lambda abstractions.
135pub fn evaluate(expr: &Expr, env: &Environment) -> Expr {
136    let mut reducer = Reducer::new();
137    let whnf = reducer.whnf_env(expr, env);
138    match &whnf {
139        Expr::Lit(_) | Expr::Lam(_, _, _, _) | Expr::Sort(_) => whnf,
140        Expr::App(_, _) => {
141            let (head, args) = collect_app_parts(&whnf);
142            if is_constructor_head(head, env) {
143                let head_norm = head.clone();
144                let mut result = head_norm;
145                for arg in args {
146                    let arg_eval = evaluate(arg, env);
147                    result = Expr::App(Node::new(result), Node::new(arg_eval));
148                }
149                result
150            } else {
151                normalize_impl(&mut reducer, &whnf, Some(env))
152            }
153        }
154        _ => normalize_impl(&mut reducer, &whnf, Some(env)),
155    }
156}
157/// Collect head and args from nested application.
158pub(super) fn collect_app_parts(expr: &Expr) -> (&Expr, Vec<&Expr>) {
159    let mut args = Vec::new();
160    let mut e = expr;
161    while let Expr::App(f, a) = e {
162        args.push(a.as_ref());
163        e = f;
164    }
165    args.reverse();
166    (e, args)
167}
168/// Check if an expression head is a constructor.
169pub(super) fn is_constructor_head(head: &Expr, env: &Environment) -> bool {
170    if let Expr::Const(name, _) = head {
171        env.is_constructor(name)
172    } else {
173        false
174    }
175}
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use crate::{BinderInfo, Level, Literal, Name};
180    #[test]
181    fn test_normalize_sort() {
182        let expr = Expr::Sort(Level::zero());
183        let norm = normalize(&expr);
184        assert_eq!(norm, expr);
185    }
186    #[test]
187    fn test_normalize_lit() {
188        let expr = Expr::Lit(Literal::nat(42));
189        let norm = normalize(&expr);
190        assert_eq!(norm, expr);
191    }
192    #[test]
193    fn test_normalize_bvar() {
194        let expr = Expr::BVar(0);
195        let norm = normalize(&expr);
196        assert_eq!(norm, expr);
197    }
198    #[test]
199    fn test_normalize_lambda() {
200        let lam = Expr::Lam(
201            BinderInfo::Default,
202            Name::str("x"),
203            Node::new(Expr::Sort(Level::zero())),
204            Node::new(Expr::BVar(0)),
205        );
206        let norm = normalize(&lam);
207        assert!(matches!(norm, Expr::Lam(_, _, _, _)));
208    }
209    #[test]
210    fn test_normalize_beta() {
211        let lam = Expr::Lam(
212            BinderInfo::Default,
213            Name::str("x"),
214            Node::new(Expr::Sort(Level::zero())),
215            Node::new(Expr::BVar(0)),
216        );
217        let arg = Expr::Lit(Literal::nat(42));
218        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
219        let norm = normalize(&app);
220        assert_eq!(norm, arg);
221    }
222    #[test]
223    fn test_alpha_eq_same() {
224        let e1 = Expr::Lit(Literal::nat(42));
225        let e2 = Expr::Lit(Literal::nat(42));
226        assert!(alpha_eq(&e1, &e2));
227    }
228    #[test]
229    fn test_alpha_eq_different() {
230        let e1 = Expr::Lit(Literal::nat(42));
231        let e2 = Expr::Lit(Literal::nat(43));
232        assert!(!alpha_eq(&e1, &e2));
233    }
234    #[test]
235    fn test_normalize_nested() {
236        let inner_lam = Expr::Lam(
237            BinderInfo::Default,
238            Name::str("y"),
239            Node::new(Expr::Sort(Level::zero())),
240            Node::new(Expr::BVar(1)),
241        );
242        let outer_lam = Expr::Lam(
243            BinderInfo::Default,
244            Name::str("x"),
245            Node::new(Expr::Sort(Level::zero())),
246            Node::new(inner_lam),
247        );
248        let arg = Expr::Lit(Literal::nat(42));
249        let app = Expr::App(Node::new(outer_lam), Node::new(arg));
250        let norm = normalize(&app);
251        assert!(matches!(norm, Expr::Lam(_, _, _, _)));
252    }
253    #[test]
254    fn test_is_normal_form() {
255        assert!(is_normal_form(&Expr::Lit(Literal::nat(42))));
256        assert!(is_normal_form(&Expr::BVar(0)));
257        assert!(is_normal_form(&Expr::Sort(Level::zero())));
258    }
259    #[test]
260    fn test_normalize_with_env() {
261        let mut env = Environment::new();
262        env.add(crate::Declaration::Definition {
263            name: Name::str("answer"),
264            univ_params: vec![],
265            ty: Expr::Const(Name::str("Nat"), vec![]),
266            val: Expr::Lit(Literal::nat(42)),
267            hint: crate::ReducibilityHint::Regular(1),
268        })
269        .expect("value should be present");
270        let expr = Expr::Const(Name::str("answer"), vec![]);
271        let norm = normalize_env(&expr, &env);
272        assert_eq!(norm, Expr::Lit(Literal::nat(42)));
273    }
274    #[test]
275    fn test_alpha_eq_env() {
276        let mut env = Environment::new();
277        env.add(crate::Declaration::Definition {
278            name: Name::str("a"),
279            univ_params: vec![],
280            ty: Expr::Const(Name::str("Nat"), vec![]),
281            val: Expr::Lit(Literal::nat(42)),
282            hint: crate::ReducibilityHint::Regular(1),
283        })
284        .expect("value should be present");
285        env.add(crate::Declaration::Definition {
286            name: Name::str("b"),
287            univ_params: vec![],
288            ty: Expr::Const(Name::str("Nat"), vec![]),
289            val: Expr::Lit(Literal::nat(42)),
290            hint: crate::ReducibilityHint::Regular(1),
291        })
292        .expect("value should be present");
293        let e1 = Expr::Const(Name::str("a"), vec![]);
294        let e2 = Expr::Const(Name::str("b"), vec![]);
295        assert!(alpha_eq_env(&e1, &e2, &env));
296    }
297}
298/// Normalize only the type-level parts of an expression (sorts and Pi domains).
299///
300/// This is useful when you want to normalize types without touching proof terms.
301#[allow(dead_code)]
302pub fn normalize_types(expr: &Expr) -> Expr {
303    normalize_types_impl(expr)
304}
305pub(super) fn normalize_types_impl(expr: &Expr) -> Expr {
306    match expr {
307        Expr::Pi(bk, n, ty, body) => {
308            let ty2 = normalize_types_impl(ty);
309            let body2 = normalize_types_impl(body);
310            Expr::Pi(*bk, n.clone(), Node::new(ty2), Node::new(body2))
311        }
312        Expr::Sort(_) => expr.clone(),
313        Expr::App(f, a) => Expr::App(
314            Node::new(normalize_types_impl(f)),
315            Node::new(normalize_types_impl(a)),
316        ),
317        Expr::Lam(bk, n, ty, body) => {
318            let ty2 = normalize_types_impl(ty);
319            Expr::Lam(
320                *bk,
321                n.clone(),
322                Node::new(ty2),
323                Node::new(body.as_ref().clone()),
324            )
325        }
326        other => other.clone(),
327    }
328}
329/// Reduce an expression to head-normal form without environment (no delta-unfolding).
330///
331/// Head-normal form means the outermost expression is not a redex:
332/// - Not a beta-redex (no `(lam ...) arg`)
333/// - Not a let-binding at the top level
334#[allow(dead_code)]
335pub fn head_normal_form(expr: &Expr) -> Expr {
336    match expr {
337        Expr::App(f, arg) => {
338            let f_hnf = head_normal_form(f);
339            match f_hnf {
340                Expr::Lam(_, _, _, body) => {
341                    let substituted = subst_bvar_norm((*body).clone(), 0, arg);
342                    head_normal_form(&substituted)
343                }
344                other_f => Expr::App(Node::new(other_f), arg.clone()),
345            }
346        }
347        Expr::Let(_, _, val, body) => {
348            let substituted = subst_bvar_norm((**body).clone(), 0, val);
349            head_normal_form(&substituted)
350        }
351        other => other.clone(),
352    }
353}
354pub(super) fn subst_bvar_norm(term: Expr, depth: u32, replacement: &Expr) -> Expr {
355    match term {
356        Expr::BVar(i) => {
357            if i == depth {
358                replacement.clone()
359            } else if i > depth {
360                Expr::BVar(i - 1)
361            } else {
362                Expr::BVar(i)
363            }
364        }
365        Expr::App(f, a) => Expr::App(
366            Node::new(subst_bvar_norm((*f).clone(), depth, replacement)),
367            Node::new(subst_bvar_norm((*a).clone(), depth, replacement)),
368        ),
369        Expr::Lam(bk, n, ty, body) => Expr::Lam(
370            bk,
371            n,
372            Node::new(subst_bvar_norm((*ty).clone(), depth, replacement)),
373            Node::new(subst_bvar_norm((*body).clone(), depth + 1, replacement)),
374        ),
375        Expr::Pi(bk, n, ty, body) => Expr::Pi(
376            bk,
377            n,
378            Node::new(subst_bvar_norm((*ty).clone(), depth, replacement)),
379            Node::new(subst_bvar_norm((*body).clone(), depth + 1, replacement)),
380        ),
381        Expr::Let(n, ty, val, body) => Expr::Let(
382            n,
383            Node::new(subst_bvar_norm((*ty).clone(), depth, replacement)),
384            Node::new(subst_bvar_norm((*val).clone(), depth, replacement)),
385            Node::new(subst_bvar_norm((*body).clone(), depth + 1, replacement)),
386        ),
387        Expr::Proj(idx, n, e) => Expr::Proj(
388            idx,
389            n,
390            Node::new(subst_bvar_norm((*e).clone(), depth, replacement)),
391        ),
392        other => other,
393    }
394}
395/// Count the number of reduction steps to reach normal form.
396///
397/// Returns the step count and the normalized expression.
398#[allow(dead_code)]
399pub fn count_reduction_steps(expr: &Expr) -> (usize, Expr) {
400    let mut steps = 0usize;
401    let result = count_steps_impl(expr, &mut steps);
402    (steps, result)
403}
404pub(super) fn count_steps_impl(expr: &Expr, steps: &mut usize) -> Expr {
405    match expr {
406        Expr::App(f, arg) => {
407            let f_norm = count_steps_impl(f, steps);
408            let arg_norm = count_steps_impl(arg, steps);
409            if let Expr::Lam(_, _, _, body) = f_norm {
410                *steps += 1;
411                let substituted = subst_bvar_norm((*body).clone(), 0, &arg_norm);
412                count_steps_impl(&substituted, steps)
413            } else {
414                Expr::App(Node::new(f_norm), Node::new(arg_norm))
415            }
416        }
417        Expr::Let(_, _, val, body) => {
418            *steps += 1;
419            let substituted = subst_bvar_norm((**body).clone(), 0, val);
420            count_steps_impl(&substituted, steps)
421        }
422        Expr::Lam(bk, n, ty, body) => {
423            let ty2 = count_steps_impl(ty, steps);
424            let body2 = count_steps_impl(body, steps);
425            Expr::Lam(*bk, n.clone(), Node::new(ty2), Node::new(body2))
426        }
427        Expr::Pi(bk, n, ty, body) => {
428            let ty2 = count_steps_impl(ty, steps);
429            let body2 = count_steps_impl(body, steps);
430            Expr::Pi(*bk, n.clone(), Node::new(ty2), Node::new(body2))
431        }
432        Expr::Proj(idx, n, e) => Expr::Proj(idx.clone(), *n, Node::new(count_steps_impl(e, steps))),
433        other => other.clone(),
434    }
435}
436/// Normalize a slice of expressions in parallel (sequential implementation).
437///
438/// Returns a `Vec` of normalized expressions in the same order.
439#[allow(dead_code)]
440pub fn normalize_many(exprs: &[Expr]) -> Vec<Expr> {
441    exprs.iter().map(normalize).collect()
442}
443/// Normalize a slice of expressions using an environment.
444#[allow(dead_code)]
445pub fn normalize_many_env(exprs: &[Expr], env: &Environment) -> Vec<Expr> {
446    exprs.iter().map(|e| normalize_env(e, env)).collect()
447}
448/// Check if an expression is already in weak head normal form.
449///
450/// An expression is in WHNF if:
451/// - It is a sort, literal, free variable, or constant
452/// - It is a lambda (already a value)
453/// - It is a Pi type
454/// - It is an application whose head is not a lambda
455#[allow(dead_code)]
456pub fn is_in_whnf(expr: &Expr) -> bool {
457    match expr {
458        Expr::Sort(_) | Expr::Lit(_) | Expr::FVar(_) | Expr::Const(_, _) => true,
459        Expr::Lam(_, _, _, _) | Expr::Pi(_, _, _, _) => true,
460        Expr::App(f, _) => !matches!(f.as_ref(), Expr::Lam(_, _, _, _)),
461        Expr::BVar(_) => true,
462        Expr::Let(_, _, _, _) => false,
463        Expr::Proj(_, _, _) => false,
464    }
465}
466/// Normalize an expression fully, but only up to a given depth limit.
467///
468/// Stops descending past `max_depth` levels of binders/applications.
469/// Returns the partially normalized expression.
470#[allow(dead_code)]
471pub fn normalize_fully(expr: &Expr, max_depth: usize) -> Expr {
472    normalize_fully_impl(expr, max_depth, 0)
473}
474pub(super) fn normalize_fully_impl(expr: &Expr, max_depth: usize, current: usize) -> Expr {
475    if current >= max_depth {
476        return expr.clone();
477    }
478    match expr {
479        Expr::App(f, arg) => {
480            let f2 = normalize_fully_impl(f, max_depth, current + 1);
481            let a2 = normalize_fully_impl(arg, max_depth, current + 1);
482            if let Expr::Lam(_, _, _, body) = f2 {
483                let sub = subst_bvar_norm((*body).clone(), 0, &a2);
484                normalize_fully_impl(&sub, max_depth, current + 1)
485            } else {
486                Expr::App(Node::new(f2), Node::new(a2))
487            }
488        }
489        Expr::Lam(bk, n, ty, body) => {
490            let ty2 = normalize_fully_impl(ty, max_depth, current + 1);
491            let b2 = normalize_fully_impl(body, max_depth, current + 1);
492            Expr::Lam(*bk, n.clone(), Node::new(ty2), Node::new(b2))
493        }
494        Expr::Pi(bk, n, ty, body) => {
495            let ty2 = normalize_fully_impl(ty, max_depth, current + 1);
496            let b2 = normalize_fully_impl(body, max_depth, current + 1);
497            Expr::Pi(*bk, n.clone(), Node::new(ty2), Node::new(b2))
498        }
499        Expr::Let(_, _, val, body) => {
500            let sub = subst_bvar_norm((**body).clone(), 0, val);
501            normalize_fully_impl(&sub, max_depth, current + 1)
502        }
503        other => other.clone(),
504    }
505}
506/// Apply exactly `n` reduction steps to the expression.
507///
508/// Returns the result after at most `n` steps. If the expression
509/// is already in normal form before `n` steps, returns early.
510#[allow(dead_code)]
511pub fn reduce_n_steps(expr: &Expr, n: usize) -> Expr {
512    let mut current = expr.clone();
513    for _ in 0..n {
514        let next = reduce_one_step(&current);
515        if next == current {
516            break;
517        }
518        current = next;
519    }
520    current
521}
522pub(super) fn reduce_one_step(expr: &Expr) -> Expr {
523    match expr {
524        Expr::App(f, arg) => {
525            if let Expr::Lam(_, _, _, body) = f.as_ref() {
526                subst_bvar_norm((**body).clone(), 0, arg)
527            } else {
528                let f2 = reduce_one_step(f);
529                Expr::App(Node::new(f2), arg.clone())
530            }
531        }
532        Expr::Let(_, _, val, body) => subst_bvar_norm((**body).clone(), 0, val),
533        other => other.clone(),
534    }
535}
536/// Normalize only constants in the expression that appear in the given whitelist.
537///
538/// Useful when you want selective unfolding of specific definitions.
539#[allow(dead_code)]
540pub fn normalize_selective(expr: &Expr, env: &Environment, whitelist: &[crate::Name]) -> Expr {
541    match expr {
542        Expr::Const(name, _) => {
543            if whitelist.contains(name) {
544                if let Some(ci) = env.find(name) {
545                    if let Some(val) = ci.value() {
546                        return normalize_selective(val, env, whitelist);
547                    }
548                }
549            }
550            expr.clone()
551        }
552        Expr::App(f, a) => {
553            let f2 = normalize_selective(f, env, whitelist);
554            let a2 = normalize_selective(a, env, whitelist);
555            if let Expr::Lam(_, _, _, body) = f2 {
556                let sub = subst_bvar_norm((*body).clone(), 0, &a2);
557                normalize_selective(&sub, env, whitelist)
558            } else {
559                Expr::App(Node::new(f2), Node::new(a2))
560            }
561        }
562        Expr::Lam(bk, n, ty, body) => Expr::Lam(
563            *bk,
564            n.clone(),
565            Node::new(normalize_selective(ty, env, whitelist)),
566            Node::new(normalize_selective(body, env, whitelist)),
567        ),
568        Expr::Pi(bk, n, ty, body) => Expr::Pi(
569            *bk,
570            n.clone(),
571            Node::new(normalize_selective(ty, env, whitelist)),
572            Node::new(normalize_selective(body, env, whitelist)),
573        ),
574        Expr::Let(_n, _ty, val, body) => {
575            let sub = subst_bvar_norm((**body).clone(), 0, val);
576            normalize_selective(&sub, env, whitelist)
577        }
578        Expr::Proj(idx, n, e) => Expr::Proj(
579            idx.clone(),
580            *n,
581            Node::new(normalize_selective(e, env, whitelist)),
582        ),
583        other => other.clone(),
584    }
585}
586#[cfg(test)]
587mod extended_normalize_tests {
588    use super::*;
589    use crate::{BinderInfo as BinderKind, Expr, Level, Literal, Name};
590    fn mk_nat(n: u64) -> Expr {
591        Expr::Lit(Literal::nat(n))
592    }
593    fn mk_const(s: &str) -> Expr {
594        Expr::Const(Name::str(s), vec![])
595    }
596    fn mk_sort(n: u32) -> Expr {
597        let mut l = Level::zero();
598        for _ in 0..n {
599            l = Level::succ(l);
600        }
601        Expr::Sort(l)
602    }
603    #[test]
604    fn test_normalize_types_pi() {
605        let pi = Expr::Pi(
606            BinderKind::Default,
607            Name::str("x"),
608            Node::new(mk_sort(0)),
609            Node::new(mk_sort(0)),
610        );
611        let result = normalize_types(&pi);
612        assert!(matches!(result, Expr::Pi(_, _, _, _)));
613    }
614    #[test]
615    fn test_head_normal_form_const() {
616        let c = mk_const("Nat");
617        let hnf = head_normal_form(&c);
618        assert_eq!(hnf, c);
619    }
620    #[test]
621    fn test_head_normal_form_app_lam() {
622        let body = Expr::BVar(0);
623        let lam = Expr::Lam(
624            BinderKind::Default,
625            Name::str("x"),
626            Node::new(mk_sort(0)),
627            Node::new(body),
628        );
629        let arg = mk_nat(5);
630        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
631        let hnf = head_normal_form(&app);
632        assert_eq!(hnf, arg);
633    }
634    #[test]
635    fn test_count_reduction_steps_zero() {
636        let c = mk_const("pure");
637        let (steps, result) = count_reduction_steps(&c);
638        assert_eq!(steps, 0);
639        assert_eq!(result, c);
640    }
641    #[test]
642    fn test_count_reduction_steps_one() {
643        let body = Expr::BVar(0);
644        let lam = Expr::Lam(
645            BinderKind::Default,
646            Name::str("x"),
647            Node::new(mk_sort(0)),
648            Node::new(body),
649        );
650        let arg = mk_nat(7);
651        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
652        let (steps, result) = count_reduction_steps(&app);
653        assert!(steps >= 1);
654        assert_eq!(result, arg);
655    }
656    #[test]
657    fn test_memoized_normalizer_cache() {
658        let mut memo = MemoizedNormalizer::new();
659        let c = mk_const("f");
660        assert_eq!(memo.cache_size(), 0);
661        memo.normalize(&c);
662        assert_eq!(memo.cache_size(), 1);
663        memo.normalize(&c);
664        assert_eq!(memo.cache_size(), 1);
665        memo.clear_cache();
666        assert_eq!(memo.cache_size(), 0);
667    }
668    #[test]
669    fn test_normalize_many() {
670        let exprs = vec![mk_nat(1), mk_const("True"), mk_nat(2)];
671        let results = normalize_many(&exprs);
672        assert_eq!(results.len(), 3);
673    }
674    #[test]
675    fn test_is_in_whnf_const() {
676        assert!(is_in_whnf(&mk_const("Nat")));
677    }
678    #[test]
679    fn test_is_in_whnf_let() {
680        let let_expr = Expr::Let(
681            Name::str("x"),
682            Node::new(mk_sort(0)),
683            Node::new(mk_nat(0)),
684            Node::new(Expr::BVar(0)),
685        );
686        assert!(!is_in_whnf(&let_expr));
687    }
688    #[test]
689    fn test_normalize_fully_depth_zero() {
690        let body = Expr::BVar(0);
691        let lam = Expr::Lam(
692            BinderKind::Default,
693            Name::str("x"),
694            Node::new(mk_sort(0)),
695            Node::new(body.clone()),
696        );
697        let arg = mk_nat(3);
698        let app = Expr::App(Node::new(lam), Node::new(arg));
699        let result = normalize_fully(&app, 0);
700        assert!(matches!(result, Expr::App(_, _)));
701    }
702    #[test]
703    fn test_reduce_n_steps_zero() {
704        let c = mk_const("x");
705        let result = reduce_n_steps(&c, 0);
706        assert_eq!(result, c);
707    }
708    #[test]
709    fn test_reduce_n_steps_one() {
710        let body = Expr::BVar(0);
711        let lam = Expr::Lam(
712            BinderKind::Default,
713            Name::str("x"),
714            Node::new(mk_sort(0)),
715            Node::new(body),
716        );
717        let arg = mk_nat(99);
718        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
719        let result = reduce_n_steps(&app, 1);
720        assert_eq!(result, arg);
721    }
722    #[test]
723    fn test_normalize_selective_no_env() {
724        let env = Environment::new();
725        let c = mk_const("myDef");
726        let whitelist = vec![Name::str("myDef")];
727        let result = normalize_selective(&c, &env, &whitelist);
728        assert_eq!(result, c);
729    }
730}
731/// Apply a normalization strategy to an expression.
732pub fn normalize_with_strategy(expr: &Expr, strategy: NormStrategy) -> Expr {
733    match strategy {
734        NormStrategy::None => expr.clone(),
735        NormStrategy::Whnf => normalize_whnf(expr),
736        NormStrategy::Full => normalize(expr),
737        NormStrategy::Bounded(depth) => normalize_fully(expr, depth),
738    }
739}
740/// Normalize only up to `n` binders deep.
741///
742/// This is useful for inspecting the head of a term without fully normalizing
743/// the bodies of lambdas/pis.
744#[allow(dead_code)]
745pub fn normalize_binders(expr: &Expr, n: usize) -> Expr {
746    if n == 0 {
747        return expr.clone();
748    }
749    match expr {
750        Expr::Lam(bi, name, ty, body) => Expr::Lam(
751            *bi,
752            name.clone(),
753            Node::new(normalize(ty)),
754            Node::new(normalize_binders(body, n - 1)),
755        ),
756        Expr::Pi(bi, name, ty, body) => Expr::Pi(
757            *bi,
758            name.clone(),
759            Node::new(normalize(ty)),
760            Node::new(normalize_binders(body, n - 1)),
761        ),
762        other => normalize(other),
763    }
764}
765/// Normalize while collecting statistics.
766#[allow(dead_code)]
767pub fn normalize_with_stats(expr: &Expr) -> (Expr, NormStats) {
768    let mut stats = NormStats::new();
769    let result = norm_stats_impl(expr, &mut stats);
770    (result, stats)
771}
772pub(super) fn norm_stats_impl(expr: &Expr, stats: &mut NormStats) -> Expr {
773    stats.visited += 1;
774    match expr {
775        Expr::App(f, arg) => {
776            let f2 = norm_stats_impl(f, stats);
777            let a2 = norm_stats_impl(arg, stats);
778            if let Expr::Lam(_, _, _, body) = &f2 {
779                stats.beta_steps += 1;
780                let sub = subst_bvar_norm((**body).clone(), 0, &a2);
781                norm_stats_impl(&sub, stats)
782            } else {
783                Expr::App(Node::new(f2), Node::new(a2))
784            }
785        }
786        Expr::Let(_, _, val, body) => {
787            stats.let_steps += 1;
788            let v2 = norm_stats_impl(val, stats);
789            let sub = subst_bvar_norm((**body).clone(), 0, &v2);
790            norm_stats_impl(&sub, stats)
791        }
792        Expr::Lam(bk, n, ty, body) => {
793            let ty2 = norm_stats_impl(ty, stats);
794            let b2 = norm_stats_impl(body, stats);
795            Expr::Lam(*bk, n.clone(), Node::new(ty2), Node::new(b2))
796        }
797        Expr::Pi(bk, n, ty, body) => {
798            let ty2 = norm_stats_impl(ty, stats);
799            let b2 = norm_stats_impl(body, stats);
800            Expr::Pi(*bk, n.clone(), Node::new(ty2), Node::new(b2))
801        }
802        other => other.clone(),
803    }
804}
805#[cfg(test)]
806mod normalize_new_tests {
807    use super::*;
808    use crate::{BinderInfo, Level, Literal, Name};
809    fn mk_nat(n: u64) -> Expr {
810        Expr::Lit(Literal::nat(n))
811    }
812    fn mk_sort0() -> Expr {
813        Expr::Sort(Level::zero())
814    }
815    #[test]
816    fn test_norm_strategy_none() {
817        let e = Expr::App(
818            Node::new(Expr::Lam(
819                BinderInfo::Default,
820                Name::str("x"),
821                Node::new(mk_sort0()),
822                Node::new(Expr::BVar(0)),
823            )),
824            Node::new(mk_nat(5)),
825        );
826        let result = normalize_with_strategy(&e, NormStrategy::None);
827        assert_eq!(result, e);
828    }
829    #[test]
830    fn test_norm_strategy_full() {
831        let e = Expr::App(
832            Node::new(Expr::Lam(
833                BinderInfo::Default,
834                Name::str("x"),
835                Node::new(mk_sort0()),
836                Node::new(Expr::BVar(0)),
837            )),
838            Node::new(mk_nat(7)),
839        );
840        let result = normalize_with_strategy(&e, NormStrategy::Full);
841        assert_eq!(result, mk_nat(7));
842    }
843    #[test]
844    fn test_norm_strategy_bounded() {
845        let e = mk_nat(3);
846        let result = normalize_with_strategy(&e, NormStrategy::Bounded(10));
847        assert_eq!(result, e);
848    }
849    #[test]
850    fn test_lazy_normal_basic() {
851        let e = mk_nat(42);
852        let lazy = LazyNormal::new(e.clone());
853        assert!(!lazy.is_evaluated());
854        assert_eq!(lazy.normalized(), &e);
855        assert!(lazy.is_evaluated());
856    }
857    #[test]
858    fn test_lazy_normal_normalizes() {
859        let body = Expr::BVar(0);
860        let lam = Expr::Lam(
861            BinderInfo::Default,
862            Name::str("x"),
863            Node::new(mk_sort0()),
864            Node::new(body),
865        );
866        let arg = mk_nat(10);
867        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
868        let lazy = LazyNormal::new(app);
869        assert_eq!(lazy.normalized(), &arg);
870    }
871    #[test]
872    fn test_normalize_with_stats_no_reductions() {
873        let e = mk_nat(1);
874        let (result, stats) = normalize_with_stats(&e);
875        assert_eq!(result, e);
876        assert_eq!(stats.beta_steps, 0);
877        assert_eq!(stats.let_steps, 0);
878        assert_eq!(stats.visited, 1);
879    }
880    #[test]
881    fn test_normalize_with_stats_beta() {
882        let body = Expr::BVar(0);
883        let lam = Expr::Lam(
884            BinderInfo::Default,
885            Name::str("x"),
886            Node::new(mk_sort0()),
887            Node::new(body),
888        );
889        let arg = mk_nat(3);
890        let app = Expr::App(Node::new(lam), Node::new(arg.clone()));
891        let (result, stats) = normalize_with_stats(&app);
892        assert_eq!(result, arg);
893        assert!(stats.beta_steps >= 1);
894    }
895    #[test]
896    fn test_normalize_with_stats_let() {
897        let val = mk_nat(5);
898        let let_expr = Expr::Let(
899            Name::str("x"),
900            Node::new(mk_sort0()),
901            Node::new(val.clone()),
902            Node::new(Expr::BVar(0)),
903        );
904        let (result, stats) = normalize_with_stats(&let_expr);
905        assert_eq!(result, val);
906        assert!(stats.let_steps >= 1);
907    }
908    #[test]
909    fn test_norm_stats_total_steps() {
910        let stats = NormStats {
911            beta_steps: 3,
912            let_steps: 2,
913            ..Default::default()
914        };
915        assert_eq!(stats.total_steps(), 5);
916    }
917    #[test]
918    fn test_normalize_binders_depth1() {
919        let nat = Expr::Const(Name::str("Nat"), vec![]);
920        let lam = Expr::Lam(
921            BinderInfo::Default,
922            Name::str("x"),
923            Node::new(nat.clone()),
924            Node::new(nat.clone()),
925        );
926        let result = normalize_binders(&lam, 1);
927        assert!(matches!(result, Expr::Lam(_, _, _, _)));
928    }
929    #[test]
930    fn test_norm_strategy_whnf() {
931        let e = mk_nat(1);
932        let result = normalize_with_strategy(&e, NormStrategy::Whnf);
933        assert_eq!(result, e);
934    }
935}
936#[cfg(test)]
937mod tests_padding_infra {
938    use super::*;
939    #[test]
940    fn test_stat_summary() {
941        let mut ss = StatSummary::new();
942        ss.record(10.0);
943        ss.record(20.0);
944        ss.record(30.0);
945        assert_eq!(ss.count(), 3);
946        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
947        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
948        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
949    }
950    #[test]
951    fn test_transform_stat() {
952        let mut ts = TransformStat::new();
953        ts.record_before(100.0);
954        ts.record_after(80.0);
955        let ratio = ts.mean_ratio().expect("ratio should be present");
956        assert!((ratio - 0.8).abs() < 1e-9);
957    }
958    #[test]
959    fn test_small_map() {
960        let mut m: SmallMap<u32, &str> = SmallMap::new();
961        m.insert(3, "three");
962        m.insert(1, "one");
963        m.insert(2, "two");
964        assert_eq!(m.get(&2), Some(&"two"));
965        assert_eq!(m.len(), 3);
966        let keys = m.keys();
967        assert_eq!(*keys[0], 1);
968        assert_eq!(*keys[2], 3);
969    }
970    #[test]
971    fn test_label_set() {
972        let mut ls = LabelSet::new();
973        ls.add("foo");
974        ls.add("bar");
975        ls.add("foo");
976        assert_eq!(ls.count(), 2);
977        assert!(ls.has("bar"));
978        assert!(!ls.has("baz"));
979    }
980    #[test]
981    fn test_config_node() {
982        let mut root = ConfigNode::section("root");
983        let child = ConfigNode::leaf("key", "value");
984        root.add_child(child);
985        assert_eq!(root.num_children(), 1);
986    }
987    #[test]
988    fn test_versioned_record() {
989        let mut vr = VersionedRecord::new(0u32);
990        vr.update(1);
991        vr.update(2);
992        assert_eq!(*vr.current(), 2);
993        assert_eq!(vr.version(), 2);
994        assert!(vr.has_history());
995        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
996    }
997    #[test]
998    fn test_simple_dag() {
999        let mut dag = SimpleDag::new(4);
1000        dag.add_edge(0, 1);
1001        dag.add_edge(1, 2);
1002        dag.add_edge(2, 3);
1003        assert!(dag.can_reach(0, 3));
1004        assert!(!dag.can_reach(3, 0));
1005        let order = dag.topological_sort().expect("order should be present");
1006        assert_eq!(order, vec![0, 1, 2, 3]);
1007    }
1008    #[test]
1009    fn test_focus_stack() {
1010        let mut fs: FocusStack<&str> = FocusStack::new();
1011        fs.focus("a");
1012        fs.focus("b");
1013        assert_eq!(fs.current(), Some(&"b"));
1014        assert_eq!(fs.depth(), 2);
1015        fs.blur();
1016        assert_eq!(fs.current(), Some(&"a"));
1017    }
1018}
1019#[cfg(test)]
1020mod tests_extra_iterators {
1021    use super::*;
1022    #[test]
1023    fn test_window_iterator() {
1024        let data = vec![1u32, 2, 3, 4, 5];
1025        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1026        assert_eq!(windows.len(), 3);
1027        assert_eq!(windows[0], &[1, 2, 3]);
1028        assert_eq!(windows[2], &[3, 4, 5]);
1029    }
1030    #[test]
1031    fn test_non_empty_vec() {
1032        let mut nev = NonEmptyVec::singleton(10u32);
1033        nev.push(20);
1034        nev.push(30);
1035        assert_eq!(nev.len(), 3);
1036        assert_eq!(*nev.first(), 10);
1037        assert_eq!(*nev.last(), 30);
1038    }
1039}
1040#[cfg(test)]
1041mod tests_padding2 {
1042    use super::*;
1043    #[test]
1044    fn test_sliding_sum() {
1045        let mut ss = SlidingSum::new(3);
1046        ss.push(1.0);
1047        ss.push(2.0);
1048        ss.push(3.0);
1049        assert!((ss.sum() - 6.0).abs() < 1e-9);
1050        ss.push(4.0);
1051        assert!((ss.sum() - 9.0).abs() < 1e-9);
1052        assert_eq!(ss.count(), 3);
1053    }
1054    #[test]
1055    fn test_path_buf() {
1056        let mut pb = PathBuf::new();
1057        pb.push("src");
1058        pb.push("main");
1059        assert_eq!(pb.as_str(), "src/main");
1060        assert_eq!(pb.depth(), 2);
1061        pb.pop();
1062        assert_eq!(pb.as_str(), "src");
1063    }
1064    #[test]
1065    fn test_string_pool() {
1066        let mut pool = StringPool::new();
1067        let s = pool.take();
1068        assert!(s.is_empty());
1069        pool.give("hello".to_string());
1070        let s2 = pool.take();
1071        assert!(s2.is_empty());
1072        assert_eq!(pool.free_count(), 0);
1073    }
1074    #[test]
1075    fn test_transitive_closure() {
1076        let mut tc = TransitiveClosure::new(4);
1077        tc.add_edge(0, 1);
1078        tc.add_edge(1, 2);
1079        tc.add_edge(2, 3);
1080        assert!(tc.can_reach(0, 3));
1081        assert!(!tc.can_reach(3, 0));
1082        let r = tc.reachable_from(0);
1083        assert_eq!(r.len(), 4);
1084    }
1085    #[test]
1086    fn test_token_bucket() {
1087        let mut tb = TokenBucket::new(100, 0);
1088        assert_eq!(tb.available(), 100);
1089        assert!(tb.try_consume(50));
1090        assert_eq!(tb.available(), 50);
1091        assert!(!tb.try_consume(60));
1092        assert_eq!(tb.capacity(), 100);
1093    }
1094    #[test]
1095    fn test_rewrite_rule_set() {
1096        let mut rrs = RewriteRuleSet::new();
1097        rrs.add(RewriteRule::unconditional(
1098            "beta",
1099            "App(Lam(x, b), v)",
1100            "b[x:=v]",
1101        ));
1102        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1103        assert_eq!(rrs.len(), 2);
1104        assert_eq!(rrs.unconditional_rules().len(), 1);
1105        assert_eq!(rrs.conditional_rules().len(), 1);
1106        assert!(rrs.get("beta").is_some());
1107        let disp = rrs
1108            .get("beta")
1109            .expect("element at \'beta\' should exist")
1110            .display();
1111        assert!(disp.contains("→"));
1112    }
1113}
1114#[cfg(test)]
1115mod tests_padding3 {
1116    use super::*;
1117    #[test]
1118    fn test_decision_node() {
1119        let tree = DecisionNode::Branch {
1120            key: "x".into(),
1121            val: "1".into(),
1122            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1123            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1124        };
1125        let mut ctx = std::collections::HashMap::new();
1126        ctx.insert("x".into(), "1".into());
1127        assert_eq!(tree.evaluate(&ctx), "yes");
1128        ctx.insert("x".into(), "2".into());
1129        assert_eq!(tree.evaluate(&ctx), "no");
1130        assert_eq!(tree.depth(), 1);
1131    }
1132    #[test]
1133    fn test_flat_substitution() {
1134        let mut sub = FlatSubstitution::new();
1135        sub.add("foo", "bar");
1136        sub.add("baz", "qux");
1137        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1138        assert_eq!(sub.len(), 2);
1139    }
1140    #[test]
1141    fn test_stopwatch() {
1142        let mut sw = Stopwatch::start();
1143        sw.split();
1144        sw.split();
1145        assert_eq!(sw.num_splits(), 2);
1146        assert!(sw.elapsed_ms() >= 0.0);
1147        for &s in sw.splits() {
1148            assert!(s >= 0.0);
1149        }
1150    }
1151    #[test]
1152    fn test_either2() {
1153        let e: Either2<i32, &str> = Either2::First(42);
1154        assert!(e.is_first());
1155        let mapped = e.map_first(|x| x * 2);
1156        assert_eq!(mapped.first(), Some(84));
1157        let e2: Either2<i32, &str> = Either2::Second("hello");
1158        assert!(e2.is_second());
1159        assert_eq!(e2.second(), Some("hello"));
1160    }
1161    #[test]
1162    fn test_write_once() {
1163        let wo: WriteOnce<u32> = WriteOnce::new();
1164        assert!(!wo.is_written());
1165        assert!(wo.write(42));
1166        assert!(!wo.write(99));
1167        assert_eq!(wo.read(), Some(42));
1168    }
1169    #[test]
1170    fn test_sparse_vec() {
1171        let mut sv: SparseVec<i32> = SparseVec::new(100);
1172        sv.set(5, 10);
1173        sv.set(50, 20);
1174        assert_eq!(*sv.get(5), 10);
1175        assert_eq!(*sv.get(50), 20);
1176        assert_eq!(*sv.get(0), 0);
1177        assert_eq!(sv.nnz(), 2);
1178        sv.set(5, 0);
1179        assert_eq!(sv.nnz(), 1);
1180    }
1181    #[test]
1182    fn test_stack_calc() {
1183        let mut calc = StackCalc::new();
1184        calc.push(3);
1185        calc.push(4);
1186        assert_eq!(calc.add(), Some(7));
1187        assert_eq!(calc.peek(), Some(7));
1188        calc.push(2);
1189        assert_eq!(calc.mul(), Some(14));
1190        assert_eq!(calc.peek(), Some(14));
1191    }
1192}
1193#[cfg(test)]
1194mod tests_final_padding {
1195    use super::*;
1196    #[test]
1197    fn test_min_heap() {
1198        let mut h = MinHeap::new();
1199        h.push(5u32);
1200        h.push(1u32);
1201        h.push(3u32);
1202        assert_eq!(h.peek(), Some(&1));
1203        assert_eq!(h.pop(), Some(1));
1204        assert_eq!(h.pop(), Some(3));
1205        assert_eq!(h.pop(), Some(5));
1206        assert!(h.is_empty());
1207    }
1208    #[test]
1209    fn test_prefix_counter() {
1210        let mut pc = PrefixCounter::new();
1211        pc.record("hello");
1212        pc.record("help");
1213        pc.record("world");
1214        assert_eq!(pc.count_with_prefix("hel"), 2);
1215        assert_eq!(pc.count_with_prefix("wor"), 1);
1216        assert_eq!(pc.count_with_prefix("xyz"), 0);
1217    }
1218    #[test]
1219    fn test_fixture() {
1220        let mut f = Fixture::new();
1221        f.set("key1", "val1");
1222        f.set("key2", "val2");
1223        assert_eq!(f.get("key1"), Some("val1"));
1224        assert_eq!(f.get("key3"), None);
1225        assert_eq!(f.len(), 2);
1226    }
1227}