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