Skip to main content

oxilean_kernel/level/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::Name;
6
7use super::types::{
8    ConfigNode, ConstraintSet, DecisionNode, Either2, Fixture, FlatSubstitution, FocusStack,
9    LabelSet, Level, LevelConstraint, LevelMVarId, LevelView, MinHeap, NonEmptyVec, PathBuf,
10    PrefixCounter, RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec,
11    StackCalc, StatSummary, Stopwatch, StringPool, TokenBucket, TransformStat, TransitiveClosure,
12    VersionedRecord, WindowIterator, WriteOnce,
13};
14
15/// Decompose a level into `(base, offset)` where level = succ^offset(base).
16///
17/// Strips all Succ wrappers. E.g., `succ(succ(param u))` -> `(param u, 2)`.
18pub fn to_offset(l: &Level) -> (&Level, u32) {
19    match l.view() {
20        LevelView::Succ(inner) => {
21            let (base, k) = to_offset(inner);
22            (base, k + 1)
23        }
24        _ => (l, 0),
25    }
26}
27/// Reconstruct a level from `(base, offset)` as `succ^offset(base)`.
28pub(super) fn from_offset(base: Level, offset: u32) -> Level {
29    let mut result = base;
30    for _ in 0..offset {
31        result = Level::succ(result);
32    }
33    result
34}
35/// Collect all arguments of a nested max expression into a flat list.
36pub(super) fn push_max_args(l: &Level, args: &mut Vec<Level>) {
37    match l.view() {
38        LevelView::Max(l1, l2) => {
39            push_max_args(l1, args);
40            push_max_args(l2, args);
41        }
42        _ => args.push(l.clone()),
43    }
44}
45/// Total order on levels for normalization.
46///
47/// This ordering ensures that normalization produces a canonical form.
48pub(super) fn is_norm_lt(l1: &Level, l2: &Level) -> bool {
49    let (b1, k1) = to_offset(l1);
50    let (b2, k2) = to_offset(l2);
51    fn kind_ord(l: &Level) -> u8 {
52        match l.view() {
53            LevelView::Zero => 0,
54            LevelView::Param(_) => 1,
55            LevelView::MVar(_) => 2,
56            LevelView::Max(_, _) => 3,
57            LevelView::IMax(_, _) => 4,
58            LevelView::Succ(_) => 5,
59        }
60    }
61    let k1_ord = kind_ord(b1);
62    let k2_ord = kind_ord(b2);
63    if k1_ord != k2_ord {
64        return k1_ord < k2_ord;
65    }
66    match (b1.view(), b2.view()) {
67        (LevelView::Param(n1), LevelView::Param(n2)) => {
68            // Structural name comparison (see `order::name_cmp`): a genuine
69            // total order, avoiding the `to_string()` comparator-inconsistency
70            // panic risk and the per-comparison allocation.
71            match super::order::name_cmp(n1, n2) {
72                std::cmp::Ordering::Less => return true,
73                std::cmp::Ordering::Greater => return false,
74                std::cmp::Ordering::Equal => {}
75            }
76        }
77        (LevelView::MVar(m1), LevelView::MVar(m2)) if m1.0 != m2.0 => {
78            return m1.0 < m2.0;
79        }
80        (LevelView::MVar(_), LevelView::MVar(_)) => {}
81        (LevelView::Max(a1, a2), LevelView::Max(b1_inner, b2_inner))
82        | (LevelView::IMax(a1, a2), LevelView::IMax(b1_inner, b2_inner)) => {
83            if a1 != b1_inner {
84                return is_norm_lt(a1, b1_inner);
85            }
86            if a2 != b2_inner {
87                return is_norm_lt(a2, b2_inner);
88            }
89        }
90        _ => {}
91    }
92    k1 < k2
93}
94/// Normalize a universe level to canonical form.
95///
96/// Follows LEAN 4's normalization algorithm:
97/// 1. Flatten nested max expressions
98/// 2. Normalize each argument recursively
99/// 3. Sort arguments
100/// 4. Merge duplicates (keep larger offsets)
101/// 5. Remove subsumed explicit levels
102pub fn normalize(l: &Level) -> Level {
103    let (base, k) = to_offset(l);
104    match base.view() {
105        LevelView::Zero | LevelView::Param(_) | LevelView::MVar(_) => l.clone(),
106        LevelView::Succ(_) => l.clone(),
107        LevelView::IMax(l1, l2) => {
108            let l1_norm = normalize(l1);
109            let l2_norm = normalize(l2);
110            if l2_norm.is_zero() {
111                // imax(_, 0) = 0; with offset: Succ^k(0)
112                return from_offset(Level::zero(), k);
113            }
114            if l2_norm.is_not_zero() {
115                // imax(u, v) = max(u, v) when v != 0.
116                // Distribute the outer offset k into each Max argument so the
117                // result is in the same canonical form as the Max branch
118                // (offsets are carried inside each component, not outside).
119                return normalize(&Level::max(
120                    from_offset(l1_norm, k),
121                    from_offset(l2_norm, k),
122                ));
123            }
124            if l1_norm.is_zero() {
125                // imax(0, v) = v; with offset: Succ^k(v).
126                // If v is a Max, from_offset produces Succ^k(Max(a,b)) which
127                // is not the same canonical form as Max(Succ^k(a), Succ^k(b)).
128                // Re-normalize to distribute the offset inside any Max.
129                let with_offset = from_offset(l2_norm, k);
130                return if k == 0 {
131                    with_offset
132                } else {
133                    normalize(&with_offset)
134                };
135            }
136            if l1_norm == l2_norm {
137                // imax(u, u) = u (Lean `mk_imax`: `else if (l1 == l2) return l1;`).
138                // Reachable when neither side is statically zero/non-zero, e.g.
139                // both are the same `Param`. This is the single highest-impact
140                // fix: without it every `Sort u -> Sort u` re-inference lands on
141                // `Sort (imax u u)` and mass-fails against the stored `Sort u`.
142                return from_offset(l1_norm, k);
143            }
144            from_offset(Level::imax(l1_norm, l2_norm), k)
145        }
146        LevelView::Max(_, _) => {
147            let mut args = Vec::new();
148            push_max_args(base, &mut args);
149            // Normalize each argument.  A component such as Succ(Max(…)) may
150            // normalize to a fresh Max node; those must be re-flattened so the
151            // resulting list has no nested Max nodes (idempotency requirement).
152            let mut normed: Vec<Level> = Vec::new();
153            for a in args {
154                let normalized_a = normalize(&from_offset(a, k));
155                // Re-flatten in case normalization produced a new Max.
156                push_max_args(&normalized_a, &mut normed);
157            }
158            normed.sort_by(|a, b| {
159                if is_norm_lt(a, b) {
160                    std::cmp::Ordering::Less
161                } else if a == b {
162                    std::cmp::Ordering::Equal
163                } else {
164                    std::cmp::Ordering::Greater
165                }
166            });
167            let mut merged: Vec<Level> = Vec::new();
168            for arg in normed {
169                let dominated = if let Some(last) = merged.last() {
170                    let (base_last, k_last) = to_offset(last);
171                    let (base_arg, k_arg) = to_offset(&arg);
172                    if base_last == base_arg {
173                        if k_arg > k_last {
174                            merged.pop();
175                            false
176                        } else {
177                            true
178                        }
179                    } else {
180                        false
181                    }
182                } else {
183                    false
184                };
185                if !dominated {
186                    merged.push(arg);
187                }
188            }
189            if merged.len() > 1 {
190                // Numeral subsumption (Lean drops an explicit numeral arg `n`
191                // when another arg is provably `>= n`). After merging there is
192                // at most one arg with base `Zero` (a numeral `succ^n(0)`); it
193                // is subsumed when some *other* arg `succ^k(base)` with a
194                // non-`Zero` base has offset `k >= n`, since such an arg is
195                // always `>= k` for every assignment. Generalises the previous
196                // "drop literal Zero when a sibling is non-zero" rule (that was
197                // the `n == 0` special case) and fixes e.g.
198                // `max(1, succ u) = succ u`, `max(0, u) = u`.
199                let max_nonnumeral_offset = merged
200                    .iter()
201                    .filter_map(|a| {
202                        let (b, k) = to_offset(a);
203                        if matches!(b.view(), LevelView::Zero) {
204                            None
205                        } else {
206                            Some(k)
207                        }
208                    })
209                    .max();
210                if let Some(bound) = max_nonnumeral_offset {
211                    merged.retain(|a| {
212                        let (b, k) = to_offset(a);
213                        // Keep everything except a numeral arg subsumed by the
214                        // bound. A numeral arg has base `Zero`.
215                        !(matches!(b.view(), LevelView::Zero) && k <= bound)
216                    });
217                }
218            }
219            match merged.pop() {
220                None => Level::zero(),
221                Some(last) => {
222                    let mut result = last;
223                    while let Some(arg) = merged.pop() {
224                        result = Level::max(arg, result);
225                    }
226                    result
227                }
228            }
229        }
230    }
231}
232/// Check if two levels are semantically equivalent.
233///
234/// Two levels are equivalent iff each is `<=` the other under the complete
235/// [`is_leq`] decision procedure, i.e. they evaluate equally under every
236/// assignment of the universe parameters to naturals. This is Lean's
237/// `Level.isEquiv` and is both **sound and complete** (unlike a bare
238/// `normalize`-equality, which is only sound — see the level audit for the
239/// `imax(u,u) = u`, `max(0,u) = u`, ... cases that normalize-equality misses).
240pub fn is_equivalent(l1: &Level, l2: &Level) -> bool {
241    l1 == l2 || (is_leq(l1, l2) && is_leq(l2, l1))
242}
243/// Check if `l1 >= l2` (level ordering).
244///
245/// Returns true iff `l1` is guaranteed to be `>= l2` for **all** assignments of
246/// the universe parameters to naturals. Defined as [`is_leq`]`(l2, l1)`.
247pub fn is_geq(l1: &Level, l2: &Level) -> bool {
248    is_leq(l2, l1)
249}
250/// Check if `l1 <= l2` (level ordering).
251///
252/// This is the complete `leq` decision procedure with the parameter case-split
253/// (`p := 0` / `p := succ p`). It is sound **and** complete over the finite
254/// assignment space: `is_leq(a, b)` is true iff `eval(a, ρ) <= eval(b, ρ)` for
255/// every assignment `ρ` of the parameters to naturals.
256pub fn is_leq(l1: &Level, l2: &Level) -> bool {
257    super::order::is_leq_core(l1, l2, 0)
258}
259/// Instantiate level parameters using a substitution.
260///
261/// Replaces `Param(name)` with the corresponding level if `name` is found
262/// in `param_names`.
263pub fn instantiate_level(level: &Level, param_names: &[Name], levels: &[Level]) -> Level {
264    if param_names.is_empty() {
265        return level.clone();
266    }
267    match level.view() {
268        LevelView::Param(name) => {
269            for (i, pn) in param_names.iter().enumerate() {
270                if pn == name {
271                    if let Some(l) = levels.get(i) {
272                        return l.clone();
273                    }
274                }
275            }
276            level.clone()
277        }
278        LevelView::Succ(l) => Level::succ(instantiate_level(l, param_names, levels)),
279        LevelView::Max(l1, l2) => Level::max(
280            instantiate_level(l1, param_names, levels),
281            instantiate_level(l2, param_names, levels),
282        ),
283        LevelView::IMax(l1, l2) => Level::imax(
284            instantiate_level(l1, param_names, levels),
285            instantiate_level(l2, param_names, levels),
286        ),
287        LevelView::Zero | LevelView::MVar(_) => level.clone(),
288    }
289}
290/// Collect all parameter names used in a level.
291pub fn collect_level_params(l: &Level, params: &mut Vec<Name>) {
292    match l.view() {
293        LevelView::Param(name) => {
294            if !params.contains(name) {
295                params.push(name.clone());
296            }
297        }
298        LevelView::Succ(l) => collect_level_params(l, params),
299        LevelView::Max(l1, l2) | LevelView::IMax(l1, l2) => {
300            collect_level_params(l1, params);
301            collect_level_params(l2, params);
302        }
303        LevelView::Zero | LevelView::MVar(_) => {}
304    }
305}
306/// Collect all metavariable IDs used in a level.
307pub fn collect_level_mvars(l: &Level, mvars: &mut Vec<LevelMVarId>) {
308    match l.view() {
309        LevelView::MVar(id) => {
310            if !mvars.contains(&id) {
311                mvars.push(id);
312            }
313        }
314        LevelView::Succ(l) => collect_level_mvars(l, mvars),
315        LevelView::Max(l1, l2) | LevelView::IMax(l1, l2) => {
316            collect_level_mvars(l1, mvars);
317            collect_level_mvars(l2, mvars);
318        }
319        LevelView::Zero | LevelView::Param(_) => {}
320    }
321}
322/// Replace level metavariables using a substitution function.
323pub fn instantiate_level_mvars(
324    level: &Level,
325    subst: &dyn Fn(LevelMVarId) -> Option<Level>,
326) -> Level {
327    match level.view() {
328        LevelView::MVar(id) => {
329            if let Some(l) = subst(id) {
330                instantiate_level_mvars(&l, subst)
331            } else {
332                level.clone()
333            }
334        }
335        LevelView::Succ(l) => Level::succ(instantiate_level_mvars(l, subst)),
336        LevelView::Max(l1, l2) => Level::max(
337            instantiate_level_mvars(l1, subst),
338            instantiate_level_mvars(l2, subst),
339        ),
340        LevelView::IMax(l1, l2) => Level::imax(
341            instantiate_level_mvars(l1, subst),
342            instantiate_level_mvars(l2, subst),
343        ),
344        LevelView::Zero | LevelView::Param(_) => level.clone(),
345    }
346}
347#[cfg(test)]
348mod tests {
349    use super::*;
350    #[test]
351    fn test_level_construction() {
352        let l0 = Level::zero();
353        let l1 = Level::succ(Level::zero());
354        let l_param = Level::param(Name::str("u"));
355        let l_max = Level::max(l1.clone(), l_param.clone());
356        assert!(l0.is_zero());
357        assert!(l_param.is_param());
358        assert!(!l_max.is_zero());
359    }
360    #[test]
361    fn test_level_display() {
362        let l0 = Level::zero();
363        let l1 = Level::succ(Level::zero());
364        let l_max = Level::max(l0.clone(), l1.clone());
365        let l_imax = Level::imax(l0, l1);
366        assert_eq!(l_max.to_string(), "max(0, 1)");
367        assert_eq!(l_imax.to_string(), "imax(0, 1)");
368    }
369    #[test]
370    fn test_level_mvar() {
371        let mv = Level::mvar(LevelMVarId(42));
372        assert!(mv.is_mvar());
373        assert!(mv.has_mvar());
374        assert!(!mv.has_param());
375        assert_eq!(mv.to_string(), "?u_42");
376    }
377    #[test]
378    fn test_is_not_zero() {
379        assert!(!Level::zero().is_not_zero());
380        assert!(Level::succ(Level::zero()).is_not_zero());
381        assert!(Level::succ(Level::param(Name::str("u"))).is_not_zero());
382        assert!(!Level::param(Name::str("u")).is_not_zero());
383        let m = Level::max(Level::zero(), Level::succ(Level::param(Name::str("u"))));
384        assert!(m.is_not_zero());
385    }
386    #[test]
387    fn test_to_offset() {
388        let l = Level::succ(Level::succ(Level::param(Name::str("u"))));
389        let (base, k) = to_offset(&l);
390        assert_eq!(*base, Level::param(Name::str("u")));
391        assert_eq!(k, 2);
392        let zero = Level::zero();
393        let (base0, k0) = to_offset(&zero);
394        assert_eq!(*base0, Level::zero());
395        assert_eq!(k0, 0);
396    }
397    #[test]
398    fn test_from_nat_to_nat() {
399        for n in 0..5 {
400            let l = Level::from_nat(n);
401            assert_eq!(l.to_nat(), Some(n));
402        }
403        assert_eq!(Level::param(Name::str("u")).to_nat(), None);
404    }
405    #[test]
406    fn test_normalize_zero() {
407        assert_eq!(normalize(&Level::zero()), Level::zero());
408    }
409    #[test]
410    fn test_normalize_succ() {
411        let l = Level::succ(Level::succ(Level::zero()));
412        assert_eq!(normalize(&l), l);
413    }
414    #[test]
415    fn test_normalize_max_same() {
416        let u = Level::param(Name::str("u"));
417        let m = Level::max(u.clone(), u.clone());
418        assert_eq!(normalize(&m), u);
419    }
420    #[test]
421    fn test_normalize_max_zero() {
422        let u = Level::param(Name::str("u"));
423        let m = Level::max(u.clone(), Level::zero());
424        let n = normalize(&m);
425        assert!(is_equivalent(&n, &u) || is_equivalent(&n, &m));
426    }
427    #[test]
428    fn test_normalize_max_ordering() {
429        let u = Level::param(Name::str("u"));
430        let v = Level::param(Name::str("v"));
431        let m1 = Level::max(u.clone(), v.clone());
432        let m2 = Level::max(v, u);
433        assert_eq!(normalize(&m1), normalize(&m2));
434    }
435    #[test]
436    fn test_normalize_imax_zero() {
437        let u = Level::param(Name::str("u"));
438        let im = Level::imax(u, Level::zero());
439        assert_eq!(normalize(&im), Level::zero());
440    }
441    #[test]
442    fn test_normalize_imax_succ() {
443        let u = Level::param(Name::str("u"));
444        let v = Level::param(Name::str("v"));
445        let im = Level::imax(u.clone(), Level::succ(v.clone()));
446        let expected = normalize(&Level::max(u, Level::succ(v)));
447        assert_eq!(normalize(&im), expected);
448    }
449    #[test]
450    fn test_is_equivalent() {
451        let u = Level::param(Name::str("u"));
452        let v = Level::param(Name::str("v"));
453        assert!(is_equivalent(
454            &Level::max(u.clone(), v.clone()),
455            &Level::max(v, u),
456        ));
457        assert!(!is_equivalent(
458            &Level::param(Name::str("u")),
459            &Level::param(Name::str("v")),
460        ));
461        assert!(is_equivalent(
462            &Level::succ(Level::zero()),
463            &Level::succ(Level::zero()),
464        ));
465    }
466    #[test]
467    fn test_is_geq() {
468        let u = Level::param(Name::str("u"));
469        let l0 = Level::zero();
470        let l1 = Level::succ(Level::zero());
471        let l2 = Level::succ(Level::succ(Level::zero()));
472        assert!(is_geq(&u, &l0));
473        assert!(is_geq(&l2, &l1));
474        assert!(!is_geq(&l1, &l2));
475        assert!(is_geq(&Level::succ(u.clone()), &u));
476        let v = Level::param(Name::str("v"));
477        assert!(is_geq(&Level::max(u.clone(), v.clone()), &u));
478        assert!(is_geq(&Level::max(u.clone(), v.clone()), &v));
479    }
480    #[test]
481    fn test_instantiate_level() {
482        let l = Level::max(
483            Level::param(Name::str("u")),
484            Level::succ(Level::param(Name::str("v"))),
485        );
486        let result = instantiate_level(
487            &l,
488            &[Name::str("u"), Name::str("v")],
489            &[Level::zero(), Level::succ(Level::zero())],
490        );
491        let expected = Level::max(Level::zero(), Level::succ(Level::succ(Level::zero())));
492        assert_eq!(result, expected);
493    }
494    #[test]
495    fn test_collect_level_params() {
496        let l = Level::max(
497            Level::param(Name::str("u")),
498            Level::succ(Level::param(Name::str("v"))),
499        );
500        let mut params = Vec::new();
501        collect_level_params(&l, &mut params);
502        assert_eq!(params.len(), 2);
503        assert!(params.contains(&Name::str("u")));
504        assert!(params.contains(&Name::str("v")));
505    }
506    #[test]
507    fn test_instantiate_level_mvars() {
508        let l = Level::max(
509            Level::mvar(LevelMVarId(0)),
510            Level::succ(Level::mvar(LevelMVarId(1))),
511        );
512        let result = instantiate_level_mvars(&l, &|id| {
513            if id.0 == 0 {
514                Some(Level::zero())
515            } else if id.0 == 1 {
516                Some(Level::param(Name::str("u")))
517            } else {
518                None
519            }
520        });
521        let expected = Level::max(Level::zero(), Level::succ(Level::param(Name::str("u"))));
522        assert_eq!(result, expected);
523    }
524    #[test]
525    fn test_depth() {
526        assert_eq!(Level::zero().depth(), 0);
527        assert_eq!(Level::succ(Level::zero()).depth(), 1);
528        assert_eq!(
529            Level::max(Level::param(Name::str("u")), Level::succ(Level::zero())).depth(),
530            2
531        );
532    }
533}
534/// Compute the minimum of two levels: min(l1, l2).
535///
536/// Defined as `imax(l1, succ(l2))` is NOT min; instead use:
537/// min(l1, l2) = l1 if l1 <= l2, else l2. This is approximate
538/// (returns a conservative lower bound for polymorphic levels).
539#[allow(dead_code)]
540pub fn level_min(l1: &Level, l2: &Level) -> Level {
541    if l1.is_zero() || l2.is_zero() {
542        return Level::zero();
543    }
544    if is_leq(l1, l2) {
545        return l1.clone();
546    }
547    if is_leq(l2, l1) {
548        return l2.clone();
549    }
550    Level::zero()
551}
552/// Create `max(max(l1, l2), l3)` and normalize.
553#[allow(dead_code)]
554pub fn level_max3(l1: Level, l2: Level, l3: Level) -> Level {
555    normalize(&Level::max(Level::max(l1, l2), l3))
556}
557/// Bump a level by `n` successor applications.
558///
559/// `level_add(l, n)` = `succ^n(l)`.
560#[allow(dead_code)]
561pub fn level_add(l: Level, n: u32) -> Level {
562    let mut result = l;
563    for _ in 0..n {
564        result = Level::succ(result);
565    }
566    result
567}
568/// The universe level for `Type n` (= succ^(n+1)(0)).
569#[allow(dead_code)]
570pub fn type_level(n: u32) -> Level {
571    Level::from_nat(n + 1)
572}
573/// Check if `l` is definitely a successor (non-zero) level.
574/// Returns true for `succ(anything)`.
575#[allow(dead_code)]
576pub fn is_definitely_succ(l: &Level) -> bool {
577    matches!(l.view(), LevelView::Succ(_))
578}
579/// Collect all levels appearing in a `Vec<Level>`, deduplicating by structural equality.
580#[allow(dead_code)]
581pub fn dedup_levels(levels: Vec<Level>) -> Vec<Level> {
582    let mut seen: Vec<Level> = Vec::new();
583    for l in levels {
584        if !seen.contains(&l) {
585            seen.push(l);
586        }
587    }
588    seen
589}
590#[cfg(test)]
591mod extra_level_tests {
592    use super::*;
593    #[test]
594    fn test_level_min_zeros() {
595        assert_eq!(
596            level_min(&Level::zero(), &Level::succ(Level::zero())),
597            Level::zero()
598        );
599    }
600    #[test]
601    fn test_level_min_equal() {
602        let l1 = Level::succ(Level::zero());
603        let l2 = Level::succ(Level::zero());
604        assert_eq!(level_min(&l1, &l2), l1);
605    }
606    #[test]
607    fn test_level_max3() {
608        let l0 = Level::zero();
609        let l1 = Level::succ(Level::zero());
610        let l2 = Level::succ(Level::succ(Level::zero()));
611        let result = level_max3(l0, l1, l2.clone());
612        assert!(is_equivalent(&result, &l2));
613    }
614    #[test]
615    fn test_level_add_zero_times() {
616        let l = Level::param(Name::str("u"));
617        assert_eq!(level_add(l.clone(), 0), l);
618    }
619    #[test]
620    fn test_level_add_two_times() {
621        let l = Level::zero();
622        let result = level_add(l, 2);
623        assert_eq!(result.to_nat(), Some(2));
624    }
625    #[test]
626    fn test_type_level_zero() {
627        assert_eq!(type_level(0), Level::succ(Level::zero()));
628    }
629    #[test]
630    fn test_type_level_two() {
631        assert_eq!(type_level(2).to_nat(), Some(3));
632    }
633    #[test]
634    fn test_is_definitely_succ_true() {
635        assert!(is_definitely_succ(&Level::succ(Level::zero())));
636    }
637    #[test]
638    fn test_is_definitely_succ_false() {
639        assert!(!is_definitely_succ(&Level::zero()));
640        assert!(!is_definitely_succ(&Level::param(Name::str("u"))));
641    }
642    #[test]
643    fn test_dedup_levels() {
644        let l0 = Level::zero();
645        let l1 = Level::succ(Level::zero());
646        let levels = vec![l0.clone(), l0.clone(), l1.clone(), l1.clone()];
647        let deduped = dedup_levels(levels);
648        assert_eq!(deduped.len(), 2);
649    }
650    #[test]
651    fn test_collect_level_mvars_multiple() {
652        let l = Level::max(Level::mvar(LevelMVarId(0)), Level::mvar(LevelMVarId(1)));
653        let mut mvars = Vec::new();
654        collect_level_mvars(&l, &mut mvars);
655        assert_eq!(mvars.len(), 2);
656    }
657    #[test]
658    fn test_level_is_leq() {
659        let l0 = Level::zero();
660        let l1 = Level::succ(Level::zero());
661        assert!(is_leq(&l0, &l1));
662        assert!(!is_leq(&l1, &l0));
663        assert!(is_leq(&l0, &l0));
664    }
665}
666/// Flatten a level to a list of `(base, offset)` pairs representing max arguments.
667///
668/// E.g., `max(max(u, v+1), w+2)` → `[(u,0), (v,1), (w,2)]`.
669#[allow(dead_code)]
670pub fn flatten_max(l: &Level) -> Vec<(Level, u32)> {
671    let norm = normalize(l);
672    let mut result = Vec::new();
673    push_flat(&norm, &mut result);
674    result
675}
676pub(super) fn push_flat(l: &Level, acc: &mut Vec<(Level, u32)>) {
677    match l.view() {
678        LevelView::Max(l1, l2) => {
679            push_flat(l1, acc);
680            push_flat(l2, acc);
681        }
682        _ => {
683            let (base, k) = to_offset(l);
684            acc.push((base.clone(), k));
685        }
686    }
687}
688/// Check if the level is a concrete numeral (succ^n(zero)).
689#[allow(dead_code)]
690pub fn is_numeral(l: &Level) -> bool {
691    l.to_nat().is_some()
692}
693/// Check if a level expression is ground (no parameters, no mvars).
694#[allow(dead_code)]
695pub fn is_ground(l: &Level) -> bool {
696    !l.has_param() && !l.has_mvar()
697}
698/// Evaluate a ground (no params, no mvars) level to a concrete `u32`.
699///
700/// Returns `None` if the level has parameters or metavariables.
701#[allow(dead_code)]
702pub fn eval_ground_level(l: &Level) -> Option<u32> {
703    if !is_ground(l) {
704        return None;
705    }
706    let norm = normalize(l);
707    norm.to_nat()
708}
709/// Create a level that is the `max` of a slice of levels.
710///
711/// Returns `Level::Zero` for an empty slice.
712#[allow(dead_code)]
713pub fn max_of_slice(levels: &[Level]) -> Level {
714    let mut iter = levels.iter();
715    match iter.next() {
716        None => Level::zero(),
717        Some(first) => iter.fold(first.clone(), |acc, l| Level::max(acc, l.clone())),
718    }
719}
720/// Create a level that is the `imax` of a slice of levels, left-folded.
721///
722/// Useful for computing the sort level of Pi types.
723#[allow(dead_code)]
724pub fn imax_fold(levels: &[Level]) -> Level {
725    let mut iter = levels.iter();
726    match iter.next() {
727        None => Level::zero(),
728        Some(first) => iter.fold(first.clone(), |acc, l| Level::imax(acc, l.clone())),
729    }
730}
731/// Level of `Prop` (= 0).
732///
733/// A function rather than a `const` since [`Level`] is now `Rc`-backed (Stage F)
734/// and its zero is heap-allocated; `Level::zero()` is the canonical constructor.
735#[allow(dead_code)]
736pub fn prop_level() -> Level {
737    Level::zero()
738}
739/// Level of `Type 0` (= 1).
740#[allow(dead_code)]
741pub fn type0_level() -> Level {
742    Level::succ(Level::zero())
743}
744/// Level of `Type 1` (= 2).
745#[allow(dead_code)]
746pub fn type1_level() -> Level {
747    Level::succ(Level::succ(Level::zero()))
748}
749#[cfg(test)]
750mod extra2_level_tests {
751    use super::*;
752    #[test]
753    fn test_level_constraint_leq_satisfied() {
754        let l0 = Level::zero();
755        let l1 = Level::succ(Level::zero());
756        let c = LevelConstraint::Leq(l0, l1);
757        assert!(c.is_satisfied());
758    }
759    #[test]
760    fn test_level_constraint_leq_violated() {
761        let l1 = Level::succ(Level::zero());
762        let l0 = Level::zero();
763        let c = LevelConstraint::Leq(l1, l0);
764        assert!(!c.is_satisfied());
765    }
766    #[test]
767    fn test_level_constraint_eq_satisfied() {
768        let l = Level::succ(Level::zero());
769        let c = LevelConstraint::Eq(l.clone(), l);
770        assert!(c.is_satisfied());
771    }
772    #[test]
773    fn test_constraint_set_all_satisfied() {
774        let mut cs = ConstraintSet::new();
775        cs.add_leq(Level::zero(), Level::succ(Level::zero()));
776        cs.add_eq(Level::zero(), Level::zero());
777        assert!(cs.all_satisfied());
778    }
779    #[test]
780    fn test_constraint_set_unsatisfied() {
781        let mut cs = ConstraintSet::new();
782        cs.add_leq(Level::succ(Level::zero()), Level::zero());
783        assert!(!cs.all_satisfied());
784        assert_eq!(cs.unsatisfied().len(), 1);
785    }
786    #[test]
787    fn test_flatten_max_single() {
788        let l = Level::param(Name::str("u"));
789        let flat = flatten_max(&l);
790        assert_eq!(flat.len(), 1);
791    }
792    #[test]
793    fn test_flatten_max_binary() {
794        let u = Level::param(Name::str("u"));
795        let v = Level::param(Name::str("v"));
796        let m = Level::max(u, v);
797        let flat = flatten_max(&m);
798        assert_eq!(flat.len(), 2);
799    }
800    #[test]
801    fn test_is_numeral_true() {
802        assert!(is_numeral(&Level::zero()));
803        assert!(is_numeral(&Level::succ(Level::zero())));
804        assert!(is_numeral(&Level::from_nat(5)));
805    }
806    #[test]
807    fn test_is_numeral_false() {
808        assert!(!is_numeral(&Level::param(Name::str("u"))));
809    }
810    #[test]
811    fn test_is_ground_zero() {
812        assert!(is_ground(&Level::zero()));
813    }
814    #[test]
815    fn test_is_ground_param() {
816        assert!(!is_ground(&Level::param(Name::str("u"))));
817    }
818    #[test]
819    fn test_eval_ground_level_numeral() {
820        let l = Level::from_nat(3);
821        assert_eq!(eval_ground_level(&l), Some(3));
822    }
823    #[test]
824    fn test_eval_ground_level_param() {
825        let l = Level::param(Name::str("u"));
826        assert_eq!(eval_ground_level(&l), None);
827    }
828    #[test]
829    fn test_max_of_slice_empty() {
830        let result = max_of_slice(&[]);
831        assert_eq!(result, Level::zero());
832    }
833    #[test]
834    fn test_max_of_slice_single() {
835        let l = Level::succ(Level::zero());
836        let result = max_of_slice(std::slice::from_ref(&l));
837        assert_eq!(result, l);
838    }
839    #[test]
840    fn test_max_of_slice_multiple() {
841        let l0 = Level::zero();
842        let l1 = Level::succ(Level::zero());
843        let l2 = Level::succ(Level::succ(Level::zero()));
844        let result = max_of_slice(&[l0, l1, l2.clone()]);
845        assert!(is_equivalent(&normalize(&result), &l2));
846    }
847    #[test]
848    fn test_imax_fold_empty() {
849        assert_eq!(imax_fold(&[]), Level::zero());
850    }
851    #[test]
852    fn test_type0_level() {
853        assert_eq!(type0_level().to_nat(), Some(1));
854    }
855    #[test]
856    fn test_type1_level() {
857        assert_eq!(type1_level().to_nat(), Some(2));
858    }
859    #[test]
860    fn test_constraint_set_len() {
861        let mut cs = ConstraintSet::new();
862        cs.add_leq(Level::zero(), Level::succ(Level::zero()));
863        assert_eq!(cs.len(), 1);
864        assert!(!cs.is_empty());
865    }
866}
867#[cfg(test)]
868mod tests_padding_infra {
869    use super::*;
870    #[test]
871    fn test_stat_summary() {
872        let mut ss = StatSummary::new();
873        ss.record(10.0);
874        ss.record(20.0);
875        ss.record(30.0);
876        assert_eq!(ss.count(), 3);
877        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
878        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
879        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
880    }
881    #[test]
882    fn test_transform_stat() {
883        let mut ts = TransformStat::new();
884        ts.record_before(100.0);
885        ts.record_after(80.0);
886        let ratio = ts.mean_ratio().expect("ratio should be present");
887        assert!((ratio - 0.8).abs() < 1e-9);
888    }
889    #[test]
890    fn test_small_map() {
891        let mut m: SmallMap<u32, &str> = SmallMap::new();
892        m.insert(3, "three");
893        m.insert(1, "one");
894        m.insert(2, "two");
895        assert_eq!(m.get(&2), Some(&"two"));
896        assert_eq!(m.len(), 3);
897        let keys = m.keys();
898        assert_eq!(*keys[0], 1);
899        assert_eq!(*keys[2], 3);
900    }
901    #[test]
902    fn test_label_set() {
903        let mut ls = LabelSet::new();
904        ls.add("foo");
905        ls.add("bar");
906        ls.add("foo");
907        assert_eq!(ls.count(), 2);
908        assert!(ls.has("bar"));
909        assert!(!ls.has("baz"));
910    }
911    #[test]
912    fn test_config_node() {
913        let mut root = ConfigNode::section("root");
914        let child = ConfigNode::leaf("key", "value");
915        root.add_child(child);
916        assert_eq!(root.num_children(), 1);
917    }
918    #[test]
919    fn test_versioned_record() {
920        let mut vr = VersionedRecord::new(0u32);
921        vr.update(1);
922        vr.update(2);
923        assert_eq!(*vr.current(), 2);
924        assert_eq!(vr.version(), 2);
925        assert!(vr.has_history());
926        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
927    }
928    #[test]
929    fn test_simple_dag() {
930        let mut dag = SimpleDag::new(4);
931        dag.add_edge(0, 1);
932        dag.add_edge(1, 2);
933        dag.add_edge(2, 3);
934        assert!(dag.can_reach(0, 3));
935        assert!(!dag.can_reach(3, 0));
936        let order = dag.topological_sort().expect("order should be present");
937        assert_eq!(order, vec![0, 1, 2, 3]);
938    }
939    #[test]
940    fn test_focus_stack() {
941        let mut fs: FocusStack<&str> = FocusStack::new();
942        fs.focus("a");
943        fs.focus("b");
944        assert_eq!(fs.current(), Some(&"b"));
945        assert_eq!(fs.depth(), 2);
946        fs.blur();
947        assert_eq!(fs.current(), Some(&"a"));
948    }
949}
950#[cfg(test)]
951mod tests_extra_iterators {
952    use super::*;
953    #[test]
954    fn test_window_iterator() {
955        let data = vec![1u32, 2, 3, 4, 5];
956        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
957        assert_eq!(windows.len(), 3);
958        assert_eq!(windows[0], &[1, 2, 3]);
959        assert_eq!(windows[2], &[3, 4, 5]);
960    }
961    #[test]
962    fn test_non_empty_vec() {
963        let mut nev = NonEmptyVec::singleton(10u32);
964        nev.push(20);
965        nev.push(30);
966        assert_eq!(nev.len(), 3);
967        assert_eq!(*nev.first(), 10);
968        assert_eq!(*nev.last(), 30);
969    }
970}
971#[cfg(test)]
972mod tests_padding2 {
973    use super::*;
974    #[test]
975    fn test_sliding_sum() {
976        let mut ss = SlidingSum::new(3);
977        ss.push(1.0);
978        ss.push(2.0);
979        ss.push(3.0);
980        assert!((ss.sum() - 6.0).abs() < 1e-9);
981        ss.push(4.0);
982        assert!((ss.sum() - 9.0).abs() < 1e-9);
983        assert_eq!(ss.count(), 3);
984    }
985    #[test]
986    fn test_path_buf() {
987        let mut pb = PathBuf::new();
988        pb.push("src");
989        pb.push("main");
990        assert_eq!(pb.as_str(), "src/main");
991        assert_eq!(pb.depth(), 2);
992        pb.pop();
993        assert_eq!(pb.as_str(), "src");
994    }
995    #[test]
996    fn test_string_pool() {
997        let mut pool = StringPool::new();
998        let s = pool.take();
999        assert!(s.is_empty());
1000        pool.give("hello".to_string());
1001        let s2 = pool.take();
1002        assert!(s2.is_empty());
1003        assert_eq!(pool.free_count(), 0);
1004    }
1005    #[test]
1006    fn test_transitive_closure() {
1007        let mut tc = TransitiveClosure::new(4);
1008        tc.add_edge(0, 1);
1009        tc.add_edge(1, 2);
1010        tc.add_edge(2, 3);
1011        assert!(tc.can_reach(0, 3));
1012        assert!(!tc.can_reach(3, 0));
1013        let r = tc.reachable_from(0);
1014        assert_eq!(r.len(), 4);
1015    }
1016    #[test]
1017    fn test_token_bucket() {
1018        let mut tb = TokenBucket::new(100, 0);
1019        assert_eq!(tb.available(), 100);
1020        assert!(tb.try_consume(50));
1021        assert_eq!(tb.available(), 50);
1022        assert!(!tb.try_consume(60));
1023        assert_eq!(tb.capacity(), 100);
1024    }
1025    #[test]
1026    fn test_rewrite_rule_set() {
1027        let mut rrs = RewriteRuleSet::new();
1028        rrs.add(RewriteRule::unconditional(
1029            "beta",
1030            "App(Lam(x, b), v)",
1031            "b[x:=v]",
1032        ));
1033        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1034        assert_eq!(rrs.len(), 2);
1035        assert_eq!(rrs.unconditional_rules().len(), 1);
1036        assert_eq!(rrs.conditional_rules().len(), 1);
1037        assert!(rrs.get("beta").is_some());
1038        let disp = rrs
1039            .get("beta")
1040            .expect("element at \'beta\' should exist")
1041            .display();
1042        assert!(disp.contains("→"));
1043    }
1044}
1045#[cfg(test)]
1046mod tests_padding3 {
1047    use super::*;
1048    #[test]
1049    fn test_decision_node() {
1050        let tree = DecisionNode::Branch {
1051            key: "x".into(),
1052            val: "1".into(),
1053            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1054            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1055        };
1056        let mut ctx = std::collections::HashMap::new();
1057        ctx.insert("x".into(), "1".into());
1058        assert_eq!(tree.evaluate(&ctx), "yes");
1059        ctx.insert("x".into(), "2".into());
1060        assert_eq!(tree.evaluate(&ctx), "no");
1061        assert_eq!(tree.depth(), 1);
1062    }
1063    #[test]
1064    fn test_flat_substitution() {
1065        let mut sub = FlatSubstitution::new();
1066        sub.add("foo", "bar");
1067        sub.add("baz", "qux");
1068        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1069        assert_eq!(sub.len(), 2);
1070    }
1071    #[test]
1072    fn test_stopwatch() {
1073        let mut sw = Stopwatch::start();
1074        sw.split();
1075        sw.split();
1076        assert_eq!(sw.num_splits(), 2);
1077        assert!(sw.elapsed_ms() >= 0.0);
1078        for &s in sw.splits() {
1079            assert!(s >= 0.0);
1080        }
1081    }
1082    #[test]
1083    fn test_either2() {
1084        let e: Either2<i32, &str> = Either2::First(42);
1085        assert!(e.is_first());
1086        let mapped = e.map_first(|x| x * 2);
1087        assert_eq!(mapped.first(), Some(84));
1088        let e2: Either2<i32, &str> = Either2::Second("hello");
1089        assert!(e2.is_second());
1090        assert_eq!(e2.second(), Some("hello"));
1091    }
1092    #[test]
1093    fn test_write_once() {
1094        let wo: WriteOnce<u32> = WriteOnce::new();
1095        assert!(!wo.is_written());
1096        assert!(wo.write(42));
1097        assert!(!wo.write(99));
1098        assert_eq!(wo.read(), Some(42));
1099    }
1100    #[test]
1101    fn test_sparse_vec() {
1102        let mut sv: SparseVec<i32> = SparseVec::new(100);
1103        sv.set(5, 10);
1104        sv.set(50, 20);
1105        assert_eq!(*sv.get(5), 10);
1106        assert_eq!(*sv.get(50), 20);
1107        assert_eq!(*sv.get(0), 0);
1108        assert_eq!(sv.nnz(), 2);
1109        sv.set(5, 0);
1110        assert_eq!(sv.nnz(), 1);
1111    }
1112    #[test]
1113    fn test_stack_calc() {
1114        let mut calc = StackCalc::new();
1115        calc.push(3);
1116        calc.push(4);
1117        assert_eq!(calc.add(), Some(7));
1118        assert_eq!(calc.peek(), Some(7));
1119        calc.push(2);
1120        assert_eq!(calc.mul(), Some(14));
1121        assert_eq!(calc.peek(), Some(14));
1122    }
1123}
1124#[cfg(test)]
1125mod tests_final_padding {
1126    use super::*;
1127    #[test]
1128    fn test_min_heap() {
1129        let mut h = MinHeap::new();
1130        h.push(5u32);
1131        h.push(1u32);
1132        h.push(3u32);
1133        assert_eq!(h.peek(), Some(&1));
1134        assert_eq!(h.pop(), Some(1));
1135        assert_eq!(h.pop(), Some(3));
1136        assert_eq!(h.pop(), Some(5));
1137        assert!(h.is_empty());
1138    }
1139    #[test]
1140    fn test_prefix_counter() {
1141        let mut pc = PrefixCounter::new();
1142        pc.record("hello");
1143        pc.record("help");
1144        pc.record("world");
1145        assert_eq!(pc.count_with_prefix("hel"), 2);
1146        assert_eq!(pc.count_with_prefix("wor"), 1);
1147        assert_eq!(pc.count_with_prefix("xyz"), 0);
1148    }
1149    #[test]
1150    fn test_fixture() {
1151        let mut f = Fixture::new();
1152        f.set("key1", "val1");
1153        f.set("key2", "val2");
1154        assert_eq!(f.get("key1"), Some("val1"));
1155        assert_eq!(f.get("key3"), None);
1156        assert_eq!(f.len(), 2);
1157    }
1158}
1159#[cfg(test)]
1160mod normalize_merge_tests;