Skip to main content

oxilean_kernel/expr_util/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::node_memo::NodeMemo;
6use crate::Node;
7use crate::{Expr, FVarId, Level, LevelView, Name};
8use std::rc::Rc;
9
10use super::types::{
11    ConfigNode, DecisionNode, Either2, Fixture, FlatSubstitution, FocusStack, LabelSet, MinHeap,
12    NonEmptyVec, PathBuf, PrefixCounter, RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum,
13    SmallMap, SparseVec, StackCalc, StatSummary, Stopwatch, StringPool, TokenBucket, TransformStat,
14    TransitiveClosure, VersionedRecord, WindowIterator, WriteOnce,
15};
16
17/// Extract the function head from a chain of applications.
18///
19/// `get_app_fn(f a1 a2 a3)` returns `f`.
20pub fn get_app_fn(e: &Expr) -> &Expr {
21    match e {
22        Expr::App(f, _) => get_app_fn(f),
23        _ => e,
24    }
25}
26/// Extract all arguments from a chain of applications.
27///
28/// `get_app_args(f a1 a2 a3)` returns `[a1, a2, a3]`.
29pub fn get_app_args(e: &Expr) -> Vec<&Expr> {
30    let mut args = Vec::new();
31    get_app_args_aux(e, &mut args);
32    args
33}
34fn get_app_args_aux<'a>(e: &'a Expr, args: &mut Vec<&'a Expr>) {
35    if let Expr::App(f, a) = e {
36        get_app_args_aux(f, args);
37        args.push(a);
38    }
39}
40/// Decompose an expression into its head function and arguments.
41///
42/// `get_app_fn_args(f a1 a2)` returns `(f, [a1, a2])`.
43pub fn get_app_fn_args(e: &Expr) -> (&Expr, Vec<&Expr>) {
44    let mut args = Vec::new();
45    let f = get_app_fn_args_aux(e, &mut args);
46    (f, args)
47}
48fn get_app_fn_args_aux<'a>(e: &'a Expr, args: &mut Vec<&'a Expr>) -> &'a Expr {
49    match e {
50        Expr::App(f, a) => {
51            let head = get_app_fn_args_aux(f, args);
52            args.push(a);
53            head
54        }
55        _ => e,
56    }
57}
58/// Get the number of arguments in an application chain.
59pub fn get_app_num_args(e: &Expr) -> usize {
60    match e {
61        Expr::App(f, _) => 1 + get_app_num_args(f),
62        _ => 0,
63    }
64}
65/// Construct an application from a function and a list of arguments.
66///
67/// `mk_app(f, [a1, a2, a3])` returns `f a1 a2 a3`.
68pub fn mk_app(f: Expr, args: &[Expr]) -> Expr {
69    args.iter().fold(f, |acc, arg| {
70        Expr::App(Node::new(acc), Node::new(arg.clone()))
71    })
72}
73/// Construct an application from a function and *borrowed* arguments.
74///
75/// Same as [`mk_app`] but over `&[&Expr]` (the shape produced by
76/// [`get_app_fn_args`]), so callers that decompose an application spine can
77/// keep the arguments borrowed until this single rebuild point — instead of
78/// deep-copying every argument up front and again on rebuild, which doubled
79/// the peak clone volume of reduction-heavy declarations (C16).
80pub fn mk_app_refs(f: Expr, args: &[&Expr]) -> Expr {
81    args.iter().fold(f, |acc, arg| {
82        Expr::App(Node::new(acc), Node::new((*arg).clone()))
83    })
84}
85/// The arguments of an application spine as the shared allocations the
86/// spine holds them in: `get_app_arg_nodes(f a1 a2 a3)` returns
87/// `[a1, a2, a3]`.
88///
89/// What [`get_app_args`] returns, without losing the allocation: a caller
90/// that rebuilds a spine from these ([`mk_app_nodes`]) shares each argument
91/// instead of copying its root into a new allocation.
92pub(crate) fn get_app_arg_nodes(e: &Expr) -> Vec<&Node> {
93    let mut args = Vec::new();
94    let mut cur = e;
95    while let Expr::App(f, a) = cur {
96        args.push(a);
97        cur = f;
98    }
99    args.reverse();
100    args
101}
102/// Construct an application from a function and arguments held as shared
103/// allocations: each argument goes into the result as the allocation it
104/// already is.
105///
106/// One unit of fuel per application built — each is one new allocation
107/// (see [`crate::fuel`]).
108pub(crate) fn mk_app_nodes(f: Expr, args: &[&Node]) -> Expr {
109    // `usize` is at most 64 bits wide on every supported target.
110    crate::fuel::charge(args.len() as u64);
111    args.iter()
112        .fold(f, |acc, arg| Expr::App(Node::new(acc), (*arg).clone()))
113}
114/// Construct an application from a function and a range of arguments.
115///
116/// `mk_app_range(f, args, 1, 3)` returns `f args[1] args[2]`.
117pub fn mk_app_range(f: Expr, args: &[Expr], begin: usize, end: usize) -> Expr {
118    let end = end.min(args.len());
119    let begin = begin.min(end);
120    mk_app(f, &args[begin..end])
121}
122/// Check if an expression has any loose bound variables.
123///
124/// A bound variable is "loose" if its de Bruijn index refers to a binder
125/// that is not part of the expression.
126///
127/// O(1): a term has a loose bound variable exactly when its cached
128/// `looseBVarRange` ([`Expr::range`]) is not zero.
129pub fn has_loose_bvars(e: &Expr) -> bool {
130    e.range() > 0
131}
132/// Check if an expression has a loose bound variable with index >= `depth`.
133pub fn has_loose_bvar_ge(e: &Expr, depth: u32) -> bool {
134    match e {
135        Expr::BVar(n) => *n >= depth,
136        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
137        Expr::App(f, a) => has_loose_bvar_ge(f, depth) || has_loose_bvar_ge(a, depth),
138        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
139            has_loose_bvar_ge(ty, depth) || has_loose_bvar_ge(body, depth + 1)
140        }
141        Expr::Let(_, ty, val, body) => {
142            has_loose_bvar_ge(ty, depth)
143                || has_loose_bvar_ge(val, depth)
144                || has_loose_bvar_ge(body, depth + 1)
145        }
146        Expr::Proj(_, _, e) => has_loose_bvar_ge(e, depth),
147    }
148}
149/// Check whether an expression has at most `cap` AST nodes (counted as a
150/// tree: a shared subterm counts once per occurrence).
151///
152/// Used to keep giant intermediate terms out of the whnf / def-eq caches:
153/// retaining every multi-million-node reduction step is what turned Lean
154/// core's `Int.add_mul_ediv_right` into a multi-GiB peak (Init corpus,
155/// C16). Skipping cache *retention* for oversized terms never changes any
156/// verdict — only memoisation.
157///
158/// The answer is usually read off the term's cached rebuild cost, in O(1).
159/// That cost ([`Expr::rebuild_cost`]) counts every node of the tree once
160/// and every leaf once more, so `size <= cost <= 2 * size`:
161///
162/// * `cost <= cap` — the size is within the cap (unless the cost is the
163///   saturated value, which only says "at least this much");
164/// * `cost > 2 * cap` — the size is over it. (The cost is stored saturated
165///   at [`REBUILD_COST_MAX`](crate::expr::REBUILD_COST_MAX); a saturated
166///   cost only understates the true one, so this bound still holds.)
167///
168/// Only a term whose cost falls between the two is counted, by a walk that
169/// visits at most `cap + 1` nodes. Every
170/// def-eq step and every whnf result asks this question of its terms, and
171/// counting each of them was a tenth of the run time of a Mathlib replay
172/// once hashing no longer walked them.
173pub fn expr_size_within(e: &Expr, cap: usize) -> bool {
174    let cost = u64::from(e.rebuild_cost());
175    // `usize` is at most 64 bits wide on every supported target.
176    let cap_wide = cap as u64;
177    if cost < u64::from(crate::expr::REBUILD_COST_MAX) && cost <= cap_wide {
178        return true;
179    }
180    if cost > cap_wide.saturating_mul(2) {
181        return false;
182    }
183    expr_size_within_by_walk(e, cap)
184}
185/// [`expr_size_within`] by counting: walks the term, with an early exit as
186/// soon as the cap is exceeded (iterative — safe on terms whose depth would
187/// overflow the stack).
188fn expr_size_within_by_walk(e: &Expr, cap: usize) -> bool {
189    let mut remaining = cap;
190    let mut stack: Vec<&Expr> = vec![e];
191    while let Some(cur) = stack.pop() {
192        if remaining == 0 {
193            return false;
194        }
195        remaining -= 1;
196        match cur {
197            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => {}
198            Expr::App(f, a) => {
199                stack.push(f);
200                stack.push(a);
201            }
202            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
203                stack.push(ty);
204                stack.push(body);
205            }
206            Expr::Let(_, ty, val, body) => {
207                stack.push(ty);
208                stack.push(val);
209                stack.push(body);
210            }
211            Expr::Proj(_, _, inner) => stack.push(inner),
212        }
213    }
214    true
215}
216/// Check if an expression has a specific loose bound variable at `level`.
217pub fn has_loose_bvar(e: &Expr, level: u32) -> bool {
218    match e {
219        Expr::BVar(n) => *n == level,
220        Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
221        Expr::App(f, a) => has_loose_bvar(f, level) || has_loose_bvar(a, level),
222        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
223            has_loose_bvar(ty, level) || has_loose_bvar(body, level + 1)
224        }
225        Expr::Let(_, ty, val, body) => {
226            has_loose_bvar(ty, level)
227                || has_loose_bvar(val, level)
228                || has_loose_bvar(body, level + 1)
229        }
230        Expr::Proj(_, _, e) => has_loose_bvar(e, level),
231    }
232}
233/// Check if an expression contains a specific free variable.
234pub fn has_fvar(e: &Expr, fvar: FVarId) -> bool {
235    match e {
236        Expr::FVar(id) => *id == fvar,
237        Expr::Sort(_) | Expr::BVar(_) | Expr::Const(_, _) | Expr::Lit(_) => false,
238        Expr::App(f, a) => has_fvar(f, fvar) || has_fvar(a, fvar),
239        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
240            has_fvar(ty, fvar) || has_fvar(body, fvar)
241        }
242        Expr::Let(_, ty, val, body) => {
243            has_fvar(ty, fvar) || has_fvar(val, fvar) || has_fvar(body, fvar)
244        }
245        Expr::Proj(_, _, e) => has_fvar(e, fvar),
246    }
247}
248/// Check if an expression contains any free variables.
249///
250/// O(1): read off the flag every shared subterm caches (see [`Node`]).
251pub fn has_any_fvar(e: &Expr) -> bool {
252    e.has_fvar()
253}
254/// Traverse every sub-expression, calling `f` on each.
255///
256/// The callback receives the expression and the current binding depth.
257/// Return `false` from `f` to stop traversal early.
258pub fn for_each_expr(e: &Expr, f: &mut dyn FnMut(&Expr, u32) -> bool) {
259    for_each_expr_aux(e, f, 0);
260}
261fn for_each_expr_aux(e: &Expr, f: &mut dyn FnMut(&Expr, u32) -> bool, depth: u32) {
262    if !f(e, depth) {
263        return;
264    }
265    match e {
266        Expr::App(fun, arg) => {
267            for_each_expr_aux(fun, f, depth);
268            for_each_expr_aux(arg, f, depth);
269        }
270        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
271            for_each_expr_aux(ty, f, depth);
272            for_each_expr_aux(body, f, depth + 1);
273        }
274        Expr::Let(_, ty, val, body) => {
275            for_each_expr_aux(ty, f, depth);
276            for_each_expr_aux(val, f, depth);
277            for_each_expr_aux(body, f, depth + 1);
278        }
279        Expr::Proj(_, _, e) => {
280            for_each_expr_aux(e, f, depth);
281        }
282        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => {}
283    }
284}
285/// Replace sub-expressions using a callback.
286///
287/// The callback receives the expression and binding depth.
288/// Return `Some(replacement)` to replace, or `None` to recurse.
289pub fn replace_expr(e: &Expr, f: &mut dyn FnMut(&Expr, u32) -> Option<Expr>) -> Expr {
290    replace_expr_aux(e, f, 0)
291}
292fn replace_expr_aux(e: &Expr, f: &mut dyn FnMut(&Expr, u32) -> Option<Expr>, depth: u32) -> Expr {
293    if let Some(replacement) = f(e, depth) {
294        return replacement;
295    }
296    match e {
297        Expr::App(fun, arg) => {
298            let new_fun = replace_expr_aux(fun, f, depth);
299            let new_arg = replace_expr_aux(arg, f, depth);
300            Expr::App(Node::new(new_fun), Node::new(new_arg))
301        }
302        Expr::Lam(bi, name, ty, body) => {
303            let new_ty = replace_expr_aux(ty, f, depth);
304            let new_body = replace_expr_aux(body, f, depth + 1);
305            Expr::Lam(*bi, name.clone(), Node::new(new_ty), Node::new(new_body))
306        }
307        Expr::Pi(bi, name, ty, body) => {
308            let new_ty = replace_expr_aux(ty, f, depth);
309            let new_body = replace_expr_aux(body, f, depth + 1);
310            Expr::Pi(*bi, name.clone(), Node::new(new_ty), Node::new(new_body))
311        }
312        Expr::Let(name, ty, val, body) => {
313            let new_ty = replace_expr_aux(ty, f, depth);
314            let new_val = replace_expr_aux(val, f, depth);
315            let new_body = replace_expr_aux(body, f, depth + 1);
316            Expr::Let(
317                name.clone(),
318                Node::new(new_ty),
319                Node::new(new_val),
320                Node::new(new_body),
321            )
322        }
323        Expr::Proj(name, idx, e_inner) => {
324            let new_e = replace_expr_aux(e_inner, f, depth);
325            Expr::Proj(name.clone(), *idx, Node::new(new_e))
326        }
327        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => {
328            e.clone()
329        }
330    }
331}
332/// Collect all free variables in an expression.
333pub fn collect_fvars(e: &Expr) -> Vec<FVarId> {
334    let mut fvars = Vec::new();
335    for_each_expr(e, &mut |sub, _depth| {
336        if let Expr::FVar(id) = sub {
337            if !fvars.contains(id) {
338                fvars.push(*id);
339            }
340        }
341        true
342    });
343    fvars
344}
345/// Collect all constant names referenced in an expression.
346pub fn collect_consts(e: &Expr) -> Vec<Name> {
347    let mut consts = Vec::new();
348    for_each_expr(e, &mut |sub, _depth| {
349        if let Expr::Const(name, _) = sub {
350            if !consts.contains(name) {
351                consts.push(name.clone());
352            }
353        }
354        true
355    });
356    consts
357}
358/// Lift (shift) loose bound variables by `n`.
359///
360/// Increments the index of all loose bound variables (with index >= `offset`)
361/// by `n`. Used when inserting an expression under additional binders.
362///
363/// The result shares with `e` every subterm that has no such variable — the
364/// same allocation, not a copy — and a shared subterm that does is lifted
365/// once per binder depth, like `subst::instantiate`. One unit
366/// of fuel per subterm the walk reaches.
367pub fn lift_loose_bvars(e: &Expr, n: u32, offset: u32) -> Expr {
368    if n == 0 || e.range() <= offset {
369        // Nothing to add, or no loose bound variable at or above `offset`.
370        return e.clone();
371    }
372    let mut walk = Lift {
373        by: n,
374        lifted: NodeMemo::new(),
375    };
376    crate::fuel::charge(1);
377    walk.rebuild(e, offset)
378}
379
380/// One [`lift_loose_bvars`] call.
381struct Lift<'term> {
382    /// How much is added to each loose index.
383    by: u32,
384    /// The lifted form of each shared subterm the walk has finished, by
385    /// subterm and depth.
386    lifted: NodeMemo<'term, Node>,
387}
388
389impl<'term> Lift<'term> {
390    /// The lift applied to a child edge under `depth` binders.
391    fn node(&mut self, node: &'term Node, depth: u32) -> Node {
392        crate::fuel::charge(1);
393        if node.range() <= depth {
394            // No loose bound variable at or above `depth`: untouched.
395            return node.clone();
396        }
397        if matches!(&**node, Expr::BVar(_)) {
398            // A leaf is cheaper to redo than to look up.
399            return Node::new(self.rebuild(node, depth));
400        }
401        let shared = node.is_shared();
402        if shared {
403            if let Some(done) = self.lifted.get(node, depth) {
404                return done.clone();
405            }
406        }
407        let result = Node::new(self.rebuild(node, depth));
408        if shared {
409            self.lifted.insert(node, depth, result.clone());
410        }
411        result
412    }
413
414    /// Rebuild `e`, which has a loose bound variable at or above `depth`.
415    fn rebuild(&mut self, e: &'term Expr, depth: u32) -> Expr {
416        match e {
417            Expr::BVar(idx) => {
418                if *idx >= depth {
419                    // An index that cannot be raised by `by` stays as high as
420                    // an index goes: above every binder there can be, so it
421                    // stays loose instead of wrapping round to a bound one.
422                    Expr::BVar(idx.saturating_add(self.by))
423                } else {
424                    e.clone()
425                }
426            }
427            Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => e.clone(),
428            Expr::App(f, a) => Expr::App(self.node(f, depth), self.node(a, depth)),
429            Expr::Lam(bi, name, ty, body) => Expr::Lam(
430                *bi,
431                name.clone(),
432                self.node(ty, depth),
433                self.node(body, depth + 1),
434            ),
435            Expr::Pi(bi, name, ty, body) => Expr::Pi(
436                *bi,
437                name.clone(),
438                self.node(ty, depth),
439                self.node(body, depth + 1),
440            ),
441            Expr::Let(name, ty, val, body) => Expr::Let(
442                name.clone(),
443                self.node(ty, depth),
444                self.node(val, depth),
445                self.node(body, depth + 1),
446            ),
447            Expr::Proj(name, idx, inner) => Expr::Proj(name.clone(), *idx, self.node(inner, depth)),
448        }
449    }
450}
451/// Check if a constant name occurs anywhere in an expression.
452pub fn occurs_const(e: &Expr, name: &Name) -> bool {
453    match e {
454        Expr::Const(n, _) => n == name,
455        Expr::App(f, a) => occurs_const(f, name) || occurs_const(a, name),
456        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
457            occurs_const(ty, name) || occurs_const(body, name)
458        }
459        Expr::Let(_, ty, val, body) => {
460            occurs_const(ty, name) || occurs_const(val, name) || occurs_const(body, name)
461        }
462        Expr::Proj(_, _, e) => occurs_const(e, name),
463        _ => false,
464    }
465}
466/// The first declaration name `e` mentions for which `wanted` holds, in a
467/// left-to-right walk of the term; `None` when it mentions no such name.
468///
469/// A term mentions a declaration in two ways: as a constant (`Const(n, _)`),
470/// and as the structure a projection reads (`Proj(n, _, _)` names the
471/// inductive type `n` without any constant standing for it).
472///
473/// Every shared subterm is looked at once (the set of addresses already
474/// seen — sound for the duration of this walk, during which `e` is borrowed
475/// and none of its subterms can be freed), so the cost is the number of
476/// distinct subterms, not the size of the tree they unfold to.
477pub fn find_mentioned_name<'a>(
478    e: &'a Expr,
479    mut wanted: impl FnMut(&Name) -> bool,
480) -> Option<&'a Name> {
481    let mut seen: std::collections::HashSet<*const Expr, crate::node_memo::WordBuildHasher> =
482        std::collections::HashSet::default();
483    let mut pending: Vec<&'a Node> = Vec::new();
484    let mut current: Option<&'a Expr> = Some(e);
485    loop {
486        let cur: &'a Expr = match current.take() {
487            Some(cur) => cur,
488            None => match pending.pop() {
489                Some(node) => {
490                    if !seen.insert(node.as_ptr()) {
491                        continue;
492                    }
493                    node
494                }
495                None => return None,
496            },
497        };
498        match cur {
499            Expr::Const(n, _) => {
500                if wanted(n) {
501                    return Some(n);
502                }
503            }
504            Expr::App(f, a) => {
505                pending.push(a);
506                pending.push(f);
507            }
508            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
509                pending.push(body);
510                pending.push(ty);
511            }
512            Expr::Let(_, ty, val, body) => {
513                pending.push(body);
514                pending.push(val);
515                pending.push(ty);
516            }
517            Expr::Proj(n, _, inner) => {
518                if wanted(n) {
519                    return Some(n);
520                }
521                pending.push(inner);
522            }
523            Expr::BVar(_) | Expr::FVar(_) | Expr::Sort(_) | Expr::Lit(_) => {}
524        }
525    }
526}
527/// Count the number of leading lambdas.
528pub fn count_lambdas(e: &Expr) -> u32 {
529    match e {
530        Expr::Lam(_, _, _, body) => 1 + count_lambdas(body),
531        _ => 0,
532    }
533}
534/// Count the number of leading Pi binders.
535pub fn count_pis(e: &Expr) -> u32 {
536    match e {
537        Expr::Pi(_, _, _, body) => 1 + count_pis(body),
538        _ => 0,
539    }
540}
541/// Get the body after stripping `n` leading lambdas.
542pub fn strip_lambdas(e: &Expr, n: u32) -> &Expr {
543    if n == 0 {
544        return e;
545    }
546    match e {
547        Expr::Lam(_, _, _, body) => strip_lambdas(body, n - 1),
548        _ => e,
549    }
550}
551/// Get the body after stripping `n` leading Pis.
552pub fn strip_pis(e: &Expr, n: u32) -> &Expr {
553    if n == 0 {
554        return e;
555    }
556    match e {
557        Expr::Pi(_, _, _, body) => strip_pis(body, n - 1),
558        _ => e,
559    }
560}
561/// Check if a level has metavariables.
562pub fn level_has_mvar(l: &Level) -> bool {
563    match l.view() {
564        LevelView::MVar(_) => true,
565        LevelView::Succ(l) => level_has_mvar(l),
566        LevelView::Max(l1, l2) | LevelView::IMax(l1, l2) => {
567            level_has_mvar(l1) || level_has_mvar(l2)
568        }
569        LevelView::Zero | LevelView::Param(_) => false,
570    }
571}
572/// Check if an expression has universe level metavariables.
573///
574/// O(1) in the size of the term: read off the flag every shared subterm
575/// caches (see [`Node`]); a `Sort` or a `Const` looks through its own levels.
576pub fn has_level_mvar(e: &Expr) -> bool {
577    e.has_level_mvar()
578}
579/// Check if an expression has universe level parameters.
580///
581/// O(1) in the size of the term, as [`has_level_mvar`].
582pub fn has_level_param(e: &Expr) -> bool {
583    e.has_level_param()
584}
585/// Compute the "weight" of an expression (rough size metric).
586pub fn expr_weight(e: &Expr) -> usize {
587    match e {
588        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Lit(_) => 1,
589        Expr::Const(_, _) => 1,
590        Expr::App(f, a) => 1 + expr_weight(f) + expr_weight(a),
591        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
592            1 + expr_weight(ty) + expr_weight(body)
593        }
594        Expr::Let(_, ty, val, body) => 1 + expr_weight(ty) + expr_weight(val) + expr_weight(body),
595        Expr::Proj(_, _, e) => 1 + expr_weight(e),
596    }
597}
598/// Check if the head of an application is a constant with a specific name.
599pub fn is_app_of(e: &Expr, name: &Name) -> bool {
600    matches!(get_app_fn(e), Expr::Const(n, _) if n == name)
601}
602/// Build a non-dependent arrow type: `a → b`.
603pub fn mk_arrow(a: Expr, b: Expr) -> Expr {
604    Expr::Pi(
605        crate::BinderInfo::Default,
606        Name::anonymous(),
607        Node::new(a),
608        Node::new(lift_loose_bvars(&b, 1, 0)),
609    )
610}
611/// Build `Sort 0` (Prop).
612pub fn mk_prop() -> Expr {
613    Expr::Sort(Level::zero())
614}
615/// Build `Sort (u + 1)` (Type u).
616pub fn mk_type(u: Level) -> Expr {
617    Expr::Sort(Level::succ(u))
618}
619/// Build `Sort 1` (Type 0).
620pub fn mk_type0() -> Expr {
621    Expr::Sort(Level::succ(Level::zero()))
622}
623/// Shorthand: create a constant expression.
624pub fn var(name: &str) -> Expr {
625    Expr::Const(Name::str(name), vec![])
626}
627/// Shorthand: create a bound variable.
628pub fn bvar(idx: u32) -> Expr {
629    Expr::BVar(idx)
630}
631/// Shorthand: create an application.
632pub fn app(f: Expr, a: Expr) -> Expr {
633    Expr::App(Node::new(f), Node::new(a))
634}
635/// Shorthand: create a lambda.
636pub fn lam(name: &str, ty: Expr, body: Expr) -> Expr {
637    Expr::Lam(
638        crate::BinderInfo::Default,
639        Name::str(name),
640        Node::new(ty),
641        Node::new(body),
642    )
643}
644/// Shorthand: create a pi type.
645pub fn pi(name: &str, ty: Expr, body: Expr) -> Expr {
646    Expr::Pi(
647        crate::BinderInfo::Default,
648        Name::str(name),
649        Node::new(ty),
650        Node::new(body),
651    )
652}
653/// Shorthand: `Prop` = `Sort 0`.
654pub fn prop() -> Expr {
655    Expr::Sort(crate::Level::zero())
656}
657/// Shorthand: `Sort(Level::zero())`.
658pub fn sort() -> Expr {
659    Expr::Sort(crate::Level::zero())
660}
661/// Shorthand: `Type 0` = `Sort 1`.
662pub fn type0() -> Expr {
663    Expr::Sort(crate::Level::succ(crate::Level::zero()))
664}
665#[cfg(test)]
666mod size_tests {
667    use super::*;
668    use crate::{BinderInfo, Level, Literal};
669
670    fn leaf(i: u32) -> Expr {
671        match i % 5 {
672            0 => Expr::BVar(i),
673            1 => Expr::Const(Name::str("c"), vec![Level::zero()]),
674            2 => Expr::Sort(Level::zero()),
675            3 => Expr::Lit(Literal::nat(u64::from(i))),
676            _ => Expr::FVar(crate::FVarId(u64::from(i))),
677        }
678    }
679
680    /// Terms of every constructor and many shapes: unary chains (the size
681    /// is close to the cost), wide applications (the size is close to half
682    /// the cost) and everything between.
683    fn shapes() -> Vec<Expr> {
684        let mut terms: Vec<Expr> = (0..5).map(leaf).collect();
685        // Projection chains: one leaf, any number of internal nodes.
686        let mut chain = leaf(1);
687        for i in 0..12 {
688            chain = Expr::Proj(Name::str("S"), i, Node::new(chain));
689            terms.push(chain.clone());
690        }
691        // Left-nested application spines: as many leaves as internal nodes.
692        let mut spine = leaf(1);
693        for i in 0..12 {
694            spine = Expr::App(Node::new(spine), Node::new(leaf(i)));
695            terms.push(spine.clone());
696        }
697        // Balanced trees with shared children (the size counts every
698        // occurrence).
699        let mut tree = leaf(0);
700        for _ in 0..8 {
701            let shared = Node::new(tree);
702            tree = Expr::App(shared.clone(), shared);
703            terms.push(tree.clone());
704        }
705        // Binders and lets over the above.
706        for i in 0..6usize {
707            let ty = Node::new(terms[i * 3].clone());
708            let body = Node::new(terms[i * 5 + 1].clone());
709            terms.push(Expr::Lam(
710                BinderInfo::Default,
711                Name::str("x"),
712                ty.clone(),
713                body.clone(),
714            ));
715            terms.push(Expr::Pi(
716                BinderInfo::Implicit,
717                Name::str("x"),
718                ty.clone(),
719                body.clone(),
720            ));
721            terms.push(Expr::Let(Name::str("x"), ty.clone(), body.clone(), ty));
722        }
723        terms
724    }
725
726    /// The number of nodes of the tree, by plain recursion.
727    fn size(e: &Expr) -> usize {
728        match e {
729            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 1,
730            Expr::App(f, a) => 1 + size(f) + size(a),
731            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => 1 + size(ty) + size(body),
732            Expr::Let(_, ty, val, body) => 1 + size(ty) + size(val) + size(body),
733            Expr::Proj(_, _, inner) => 1 + size(inner),
734        }
735    }
736
737    #[test]
738    fn the_cached_cost_brackets_the_size() {
739        for (i, term) in shapes().iter().enumerate() {
740            let n = size(term) as u64;
741            let cost = u64::from(term.rebuild_cost());
742            assert!(
743                n <= cost && cost <= 2 * n,
744                "term {i}: size {n}, cost {cost}"
745            );
746        }
747    }
748
749    #[test]
750    fn size_within_agrees_with_counting_at_every_cap() {
751        let terms = shapes();
752        let mut by_cost = 0usize;
753        let mut by_walk = 0usize;
754        for (i, term) in terms.iter().enumerate() {
755            let n = size(term);
756            let cost = term.rebuild_cost() as usize;
757            for cap in (0..=2 * n + 3).chain([4096, usize::MAX / 2, usize::MAX]) {
758                let expected = n <= cap;
759                assert_eq!(
760                    expr_size_within(term, cap),
761                    expected,
762                    "term {i} (size {n}, cost {cost}) at cap {cap}"
763                );
764                assert_eq!(
765                    expr_size_within_by_walk(term, cap),
766                    expected,
767                    "walk: term {i} (size {n}) at cap {cap}"
768                );
769                if cost <= cap || cost > 2 * cap {
770                    by_cost += 1;
771                } else {
772                    by_walk += 1;
773                }
774            }
775        }
776        // Both ways of answering are exercised.
777        assert!(by_cost > 1000, "{by_cost} caps decided by the cost");
778        assert!(by_walk > 1000, "{by_walk} caps decided by counting");
779    }
780
781    #[test]
782    fn a_saturated_cost_never_reads_as_small() {
783        use crate::expr::REBUILD_COST_MAX;
784        // 40 levels of a doubling tower: 2^41 - 1 nodes, the cost saturated.
785        let mut tower = leaf(0);
786        for _ in 0..40 {
787            let shared = Node::new(tower);
788            tower = Expr::App(shared.clone(), shared);
789        }
790        assert_eq!(tower.rebuild_cost(), REBUILD_COST_MAX);
791        // Over every cap the caches use, and over a cap the saturated cost
792        // itself would pass.
793        assert!(!expr_size_within(&tower, 0));
794        assert!(!expr_size_within(&tower, 4096));
795        assert!(!expr_size_within(
796            &tower,
797            (REBUILD_COST_MAX as usize) / 2 - 1
798        ));
799        // 28 levels: 2^29 - 1 nodes, the cost saturated as well (it counts
800        // every leaf twice); a cap of 2^27 is under half the saturated cost,
801        // so the answer is still O(1).
802        let mut smaller = leaf(0);
803        for _ in 0..28 {
804            let shared = Node::new(smaller);
805            smaller = Expr::App(shared.clone(), shared);
806        }
807        assert_eq!(smaller.rebuild_cost(), REBUILD_COST_MAX);
808        assert!(!expr_size_within(&smaller, 1 << 27));
809        // One level short of saturating: the cost is exact and decides.
810        let mut exact = leaf(0);
811        for _ in 0..26 {
812            let shared = Node::new(exact);
813            exact = Expr::App(shared.clone(), shared);
814        }
815        // 2^26 leaves counted twice and 2^26 - 1 applications once.
816        assert_eq!(exact.rebuild_cost(), 3 * (1 << 26) - 1);
817        assert!(exact.rebuild_cost() < REBUILD_COST_MAX);
818        assert!(expr_size_within(&exact, 3 * (1 << 26)));
819        assert!(!expr_size_within(&exact, 1 << 20));
820    }
821}
822#[cfg(test)]
823mod tests {
824    use super::*;
825    use crate::BinderInfo;
826    fn nat() -> Expr {
827        Expr::Const(Name::str("Nat"), vec![])
828    }
829    fn bool_ty() -> Expr {
830        Expr::Const(Name::str("Bool"), vec![])
831    }
832    #[test]
833    fn test_get_app_fn() {
834        let f = nat();
835        let e = Expr::App(
836            Node::new(Expr::App(Node::new(f.clone()), Node::new(Expr::BVar(0)))),
837            Node::new(Expr::BVar(1)),
838        );
839        assert_eq!(get_app_fn(&e), &f);
840    }
841    #[test]
842    fn test_get_app_args() {
843        let f = nat();
844        let a1 = Expr::BVar(0);
845        let a2 = Expr::BVar(1);
846        let e = Expr::App(
847            Node::new(Expr::App(Node::new(f), Node::new(a1.clone()))),
848            Node::new(a2.clone()),
849        );
850        let args = get_app_args(&e);
851        assert_eq!(args.len(), 2);
852        assert_eq!(args[0], &a1);
853        assert_eq!(args[1], &a2);
854    }
855    #[test]
856    fn test_get_app_fn_args() {
857        let f = nat();
858        let a = Expr::BVar(0);
859        let e = Expr::App(Node::new(f.clone()), Node::new(a.clone()));
860        let (head, args) = get_app_fn_args(&e);
861        assert_eq!(head, &f);
862        assert_eq!(args, vec![&a]);
863    }
864    #[test]
865    fn test_mk_app() {
866        let f = nat();
867        let a1 = Expr::BVar(0);
868        let a2 = Expr::BVar(1);
869        let result = mk_app(f.clone(), &[a1.clone(), a2.clone()]);
870        let (head, args) = get_app_fn_args(&result);
871        assert_eq!(head, &f);
872        assert_eq!(args.len(), 2);
873    }
874    #[test]
875    fn test_has_loose_bvars() {
876        assert!(has_loose_bvars(&Expr::BVar(0)));
877        assert!(!has_loose_bvars(&nat()));
878        assert!(!has_loose_bvars(&Expr::FVar(FVarId(1))));
879        let lam = Expr::Lam(
880            BinderInfo::Default,
881            Name::str("x"),
882            Node::new(nat()),
883            Node::new(Expr::BVar(0)),
884        );
885        assert!(!has_loose_bvars(&lam));
886        let lam2 = Expr::Lam(
887            BinderInfo::Default,
888            Name::str("x"),
889            Node::new(nat()),
890            Node::new(Expr::BVar(1)),
891        );
892        assert!(has_loose_bvars(&lam2));
893    }
894    #[test]
895    fn test_has_fvar() {
896        let id = FVarId(42);
897        let e = Expr::App(Node::new(Expr::FVar(id)), Node::new(Expr::FVar(FVarId(99))));
898        assert!(has_fvar(&e, id));
899        assert!(has_fvar(&e, FVarId(99)));
900        assert!(!has_fvar(&e, FVarId(1)));
901    }
902    #[test]
903    fn test_collect_fvars() {
904        let e = Expr::App(
905            Node::new(Expr::FVar(FVarId(1))),
906            Node::new(Expr::App(
907                Node::new(Expr::FVar(FVarId(2))),
908                Node::new(Expr::FVar(FVarId(1))),
909            )),
910        );
911        let fvars = collect_fvars(&e);
912        assert_eq!(fvars.len(), 2);
913        assert!(fvars.contains(&FVarId(1)));
914        assert!(fvars.contains(&FVarId(2)));
915    }
916    #[test]
917    fn test_for_each_expr() {
918        let e = Expr::App(Node::new(nat()), Node::new(Expr::BVar(0)));
919        let mut count = 0;
920        for_each_expr(&e, &mut |_, _| {
921            count += 1;
922            true
923        });
924        assert_eq!(count, 3);
925    }
926    #[test]
927    fn test_replace_expr() {
928        let e = Expr::App(Node::new(Expr::BVar(0)), Node::new(Expr::BVar(1)));
929        let result = replace_expr(&e, &mut |sub, _depth| {
930            if let Expr::BVar(0) = sub {
931                Some(nat())
932            } else {
933                None
934            }
935        });
936        match &result {
937            Expr::App(f, _) => assert_eq!(**f, nat()),
938            _ => panic!("Expected App"),
939        }
940    }
941    #[test]
942    fn test_lift_loose_bvars() {
943        let e = Expr::BVar(0);
944        let lifted = lift_loose_bvars(&e, 2, 0);
945        assert_eq!(lifted, Expr::BVar(2));
946        let lam = Expr::Lam(
947            BinderInfo::Default,
948            Name::str("x"),
949            Node::new(nat()),
950            Node::new(Expr::BVar(0)),
951        );
952        let lifted_lam = lift_loose_bvars(&lam, 1, 0);
953        if let Expr::Lam(_, _, _, body) = &lifted_lam {
954            assert_eq!(**body, Expr::BVar(0));
955        } else {
956            panic!("Expected Lam");
957        }
958    }
959    #[test]
960    fn test_count_lambdas_pis() {
961        let e = Expr::Lam(
962            BinderInfo::Default,
963            Name::str("x"),
964            Node::new(nat()),
965            Node::new(Expr::Lam(
966                BinderInfo::Default,
967                Name::str("y"),
968                Node::new(nat()),
969                Node::new(Expr::BVar(0)),
970            )),
971        );
972        assert_eq!(count_lambdas(&e), 2);
973        let pi = Expr::Pi(
974            BinderInfo::Default,
975            Name::str("x"),
976            Node::new(nat()),
977            Node::new(nat()),
978        );
979        assert_eq!(count_pis(&pi), 1);
980    }
981    #[test]
982    fn test_is_app_of() {
983        let e = Expr::App(
984            Node::new(Expr::Const(Name::str("f"), vec![])),
985            Node::new(Expr::BVar(0)),
986        );
987        assert!(is_app_of(&e, &Name::str("f")));
988        assert!(!is_app_of(&e, &Name::str("g")));
989    }
990    #[test]
991    fn test_mk_arrow() {
992        let arrow = mk_arrow(nat(), bool_ty());
993        assert!(arrow.is_pi());
994    }
995    #[test]
996    fn test_expr_weight() {
997        assert_eq!(expr_weight(&nat()), 1);
998        let app = Expr::App(Node::new(nat()), Node::new(Expr::BVar(0)));
999        assert_eq!(expr_weight(&app), 3);
1000    }
1001    #[test]
1002    fn test_mk_app_range() {
1003        let f = nat();
1004        let args = vec![Expr::BVar(0), Expr::BVar(1), Expr::BVar(2)];
1005        let result = mk_app_range(f.clone(), &args, 1, 3);
1006        assert_eq!(get_app_num_args(&result), 2);
1007    }
1008    #[test]
1009    fn test_occurs_const() {
1010        let e = Expr::App(Node::new(nat()), Node::new(bool_ty()));
1011        assert!(occurs_const(&e, &Name::str("Nat")));
1012        assert!(occurs_const(&e, &Name::str("Bool")));
1013        assert!(!occurs_const(&e, &Name::str("Int")));
1014    }
1015}
1016/// Check if the expression is a `Sort`.
1017#[inline]
1018pub fn is_sort(e: &Expr) -> bool {
1019    matches!(e, Expr::Sort(_))
1020}
1021/// Check if the expression is `Sort(Level::Zero)` (i.e. `Prop`).
1022#[inline]
1023pub fn is_prop(e: &Expr) -> bool {
1024    matches!(e, Expr::Sort(l) if matches!(l.view(), LevelView::Zero))
1025}
1026/// Check if the expression is `Sort(Level::Succ(Level::Zero))` (i.e. `Type 0`).
1027#[inline]
1028pub fn is_type0(e: &Expr) -> bool {
1029    matches!(e, Expr::Sort(l) if matches!(l.view(), LevelView::Succ(inner) if matches!(inner.view(), LevelView::Zero)))
1030}
1031/// Check if the expression is a lambda abstraction.
1032#[inline]
1033pub fn is_lambda(e: &Expr) -> bool {
1034    matches!(e, Expr::Lam(_, _, _, _))
1035}
1036/// Check if the expression is a Pi type.
1037#[inline]
1038pub fn is_pi(e: &Expr) -> bool {
1039    matches!(e, Expr::Pi(_, _, _, _))
1040}
1041/// Check if the expression is an FVar.
1042#[inline]
1043pub fn is_fvar(e: &Expr) -> bool {
1044    matches!(e, Expr::FVar(_))
1045}
1046/// Check if the expression is a BVar.
1047#[inline]
1048pub fn is_bvar(e: &Expr) -> bool {
1049    matches!(e, Expr::BVar(_))
1050}
1051/// Check if the expression is a Literal.
1052#[inline]
1053pub fn is_literal(e: &Expr) -> bool {
1054    matches!(e, Expr::Lit(_))
1055}
1056/// Check if the expression is a Const.
1057#[inline]
1058pub fn is_const(e: &Expr) -> bool {
1059    matches!(e, Expr::Const(_, _))
1060}
1061/// Check if the expression is an App.
1062#[inline]
1063pub fn is_app(e: &Expr) -> bool {
1064    matches!(e, Expr::App(_, _))
1065}
1066/// Check if the expression is a Let.
1067#[inline]
1068pub fn is_let(e: &Expr) -> bool {
1069    matches!(e, Expr::Let(_, _, _, _))
1070}
1071/// Check if the expression is a Proj.
1072#[inline]
1073pub fn is_proj(e: &Expr) -> bool {
1074    matches!(e, Expr::Proj(_, _, _))
1075}
1076/// Extract the level from a Sort expression.
1077#[inline]
1078pub fn get_sort_level(e: &Expr) -> Option<&Level> {
1079    if let Expr::Sort(l) = e {
1080        Some(l)
1081    } else {
1082        None
1083    }
1084}
1085/// Extract the name from a Const expression.
1086#[inline]
1087pub fn get_const_name(e: &Expr) -> Option<&Name> {
1088    if let Expr::Const(n, _) = e {
1089        Some(n)
1090    } else {
1091        None
1092    }
1093}
1094/// Extract the universe levels from a Const expression.
1095#[inline]
1096pub fn get_const_levels(e: &Expr) -> Option<&[Level]> {
1097    if let Expr::Const(_, ls) = e {
1098        Some(ls)
1099    } else {
1100        None
1101    }
1102}
1103/// Extract the FVarId from an FVar expression.
1104#[inline]
1105pub fn get_fvar_id(e: &Expr) -> Option<FVarId> {
1106    if let Expr::FVar(id) = e {
1107        Some(*id)
1108    } else {
1109        None
1110    }
1111}
1112/// Extract the BVar index from a BVar expression.
1113#[inline]
1114pub fn get_bvar_idx(e: &Expr) -> Option<u32> {
1115    if let Expr::BVar(i) = e {
1116        Some(*i)
1117    } else {
1118        None
1119    }
1120}
1121/// Decompose a Let expression into `(type, value, body)`.
1122#[inline]
1123pub fn decompose_let(e: &Expr) -> Option<(&Expr, &Expr, &Expr)> {
1124    if let Expr::Let(_, ty, val, body) = e {
1125        Some((ty, val, body))
1126    } else {
1127        None
1128    }
1129}
1130/// Decompose a Pi expression into `(binder_info, domain, codomain)`.
1131#[inline]
1132pub fn decompose_pi(e: &Expr) -> Option<(crate::BinderInfo, &Expr, &Expr)> {
1133    if let Expr::Pi(bi, _, dom, cod) = e {
1134        Some((*bi, dom, cod))
1135    } else {
1136        None
1137    }
1138}
1139/// Decompose a Lam expression into `(binder_info, domain, body)`.
1140#[inline]
1141pub fn decompose_lam(e: &Expr) -> Option<(crate::BinderInfo, &Expr, &Expr)> {
1142    if let Expr::Lam(bi, _, dom, body) = e {
1143        Some((*bi, dom, body))
1144    } else {
1145        None
1146    }
1147}
1148/// Decompose a Proj expression into `(struct_name, field_index, value)`.
1149#[inline]
1150pub fn decompose_proj(e: &Expr) -> Option<(&Name, usize, &Expr)> {
1151    if let Expr::Proj(n, idx, val) = e {
1152        Some((n, *idx as usize, val))
1153    } else {
1154        None
1155    }
1156}
1157/// Build a nested Pi type from a list of binders and a return type.
1158///
1159/// `mk_pi_n([(bi1, ty1), ..., (bin, tyn)], ret)` produces
1160/// `Pi bi1 ty1 (Pi bi2 ty2 (... (Pi bin tyn ret) ...))`.
1161pub fn mk_pi_n(binders: &[(crate::BinderInfo, Expr)], ret: Expr) -> Expr {
1162    binders.iter().rev().fold(ret, |acc, (bi, ty)| {
1163        Expr::Pi(
1164            *bi,
1165            crate::Name::anonymous(),
1166            Node::new(ty.clone()),
1167            Node::new(acc),
1168        )
1169    })
1170}
1171/// Build a nested Lam abstraction from a list of binders and a body.
1172pub fn mk_lam_n(binders: &[(crate::BinderInfo, Expr)], body: Expr) -> Expr {
1173    binders.iter().rev().fold(body, |acc, (bi, ty)| {
1174        Expr::Lam(
1175            *bi,
1176            crate::Name::anonymous(),
1177            Node::new(ty.clone()),
1178            Node::new(acc),
1179        )
1180    })
1181}
1182/// Check if `e` is an application of FVar `id`.
1183pub fn is_app_of_fvar(e: &Expr, id: FVarId) -> bool {
1184    get_app_fn(e) == &Expr::FVar(id)
1185}
1186/// Get the `n`-th argument in an application chain (0-indexed).
1187pub fn get_nth_arg(e: &Expr, n: usize) -> Option<&Expr> {
1188    let args = get_app_args(e);
1189    args.get(n).copied()
1190}
1191/// Check if an expression is "simple" (atomic: no subexpressions).
1192pub fn is_simple(e: &Expr) -> bool {
1193    matches!(
1194        e,
1195        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_)
1196    )
1197}
1198#[cfg(test)]
1199mod extended_tests {
1200    use super::*;
1201    use crate::{BinderInfo, Level};
1202    fn nat() -> Expr {
1203        Expr::Const(Name::str("Nat"), vec![])
1204    }
1205    fn prop() -> Expr {
1206        Expr::Sort(Level::zero())
1207    }
1208    fn bv(i: u32) -> Expr {
1209        Expr::BVar(i)
1210    }
1211    #[test]
1212    fn test_is_prop() {
1213        assert!(is_prop(&prop()));
1214        assert!(!is_prop(&nat()));
1215    }
1216    #[test]
1217    fn test_is_sort() {
1218        assert!(is_sort(&prop()));
1219        assert!(is_sort(&Expr::Sort(Level::succ(Level::zero()))));
1220        assert!(!is_sort(&nat()));
1221    }
1222    #[test]
1223    fn test_is_lambda_pi() {
1224        let lam = Expr::Lam(
1225            BinderInfo::Default,
1226            crate::Name::anonymous(),
1227            Node::new(prop()),
1228            Node::new(bv(0)),
1229        );
1230        let pi = Expr::Pi(
1231            BinderInfo::Default,
1232            crate::Name::anonymous(),
1233            Node::new(prop()),
1234            Node::new(bv(0)),
1235        );
1236        assert!(is_lambda(&lam));
1237        assert!(!is_lambda(&pi));
1238        assert!(is_pi(&pi));
1239        assert!(!is_pi(&lam));
1240    }
1241    #[test]
1242    fn test_is_fvar_bvar() {
1243        use crate::FVarId;
1244        assert!(is_fvar(&Expr::FVar(FVarId(0))));
1245        assert!(is_bvar(&bv(0)));
1246        assert!(!is_fvar(&nat()));
1247    }
1248    #[test]
1249    fn test_get_sort_level() {
1250        let s = Expr::Sort(Level::zero());
1251        assert_eq!(get_sort_level(&s), Some(&Level::zero()));
1252        assert_eq!(get_sort_level(&nat()), None);
1253    }
1254    #[test]
1255    fn test_get_const_name() {
1256        assert_eq!(get_const_name(&nat()), Some(&Name::str("Nat")));
1257        assert_eq!(get_const_name(&prop()), None);
1258    }
1259    #[test]
1260    fn test_get_bvar_idx() {
1261        assert_eq!(get_bvar_idx(&bv(3)), Some(3));
1262        assert_eq!(get_bvar_idx(&nat()), None);
1263    }
1264    #[test]
1265    fn test_decompose_let() {
1266        let e = Expr::Let(
1267            crate::Name::anonymous(),
1268            Node::new(nat()),
1269            Node::new(bv(0)),
1270            Node::new(bv(0)),
1271        );
1272        let (ty, val, body) = decompose_let(&e).expect("value should be present");
1273        assert_eq!(ty, &nat());
1274        assert_eq!(val, &bv(0));
1275        assert_eq!(body, &bv(0));
1276    }
1277    #[test]
1278    fn test_decompose_pi() {
1279        let e = Expr::Pi(
1280            BinderInfo::Default,
1281            crate::Name::anonymous(),
1282            Node::new(nat()),
1283            Node::new(prop()),
1284        );
1285        let (bi, dom, cod) = decompose_pi(&e).expect("value should be present");
1286        assert_eq!(bi, BinderInfo::Default);
1287        assert_eq!(dom, &nat());
1288        assert_eq!(cod, &prop());
1289    }
1290    #[test]
1291    fn test_decompose_lam() {
1292        let e = Expr::Lam(
1293            BinderInfo::Implicit,
1294            crate::Name::anonymous(),
1295            Node::new(nat()),
1296            Node::new(bv(0)),
1297        );
1298        let (bi, dom, body) = decompose_lam(&e).expect("value should be present");
1299        assert_eq!(bi, BinderInfo::Implicit);
1300        assert_eq!(dom, &nat());
1301        assert_eq!(body, &bv(0));
1302    }
1303    #[test]
1304    fn test_mk_pi_n_empty() {
1305        let ret = nat();
1306        let result = mk_pi_n(&[], ret.clone());
1307        assert_eq!(result, ret);
1308    }
1309    #[test]
1310    fn test_mk_pi_n_two() {
1311        let binders = vec![(BinderInfo::Default, nat()), (BinderInfo::Default, prop())];
1312        let ret = bv(0);
1313        let result = mk_pi_n(&binders, ret);
1314        assert!(is_pi(&result));
1315        let (_, _, inner) = decompose_pi(&result).expect("value should be present");
1316        assert!(is_pi(inner));
1317    }
1318    #[test]
1319    fn test_mk_lam_n_two() {
1320        let binders = vec![(BinderInfo::Default, nat()), (BinderInfo::Implicit, prop())];
1321        let result = mk_lam_n(&binders, bv(0));
1322        assert!(is_lambda(&result));
1323    }
1324    #[test]
1325    fn test_is_simple() {
1326        assert!(is_simple(&nat()));
1327        assert!(is_simple(&bv(0)));
1328        assert!(is_simple(&prop()));
1329        let app = Expr::App(Node::new(nat()), Node::new(bv(0)));
1330        assert!(!is_simple(&app));
1331    }
1332    #[test]
1333    fn test_get_nth_arg() {
1334        let f = nat();
1335        let arg0 = bv(0);
1336        let arg1 = bv(1);
1337        let app = Expr::App(
1338            Node::new(Expr::App(Node::new(f), Node::new(arg0.clone()))),
1339            Node::new(arg1.clone()),
1340        );
1341        assert_eq!(get_nth_arg(&app, 0), Some(&arg0));
1342        assert_eq!(get_nth_arg(&app, 1), Some(&arg1));
1343        assert_eq!(get_nth_arg(&app, 2), None);
1344    }
1345    #[test]
1346    fn test_is_literal() {
1347        use crate::Literal;
1348        let lit = Expr::Lit(Literal::nat(42));
1349        assert!(is_literal(&lit));
1350        assert!(!is_literal(&nat()));
1351    }
1352}
1353#[cfg(test)]
1354mod tests_padding_infra {
1355    use super::*;
1356    #[test]
1357    fn test_stat_summary() {
1358        let mut ss = StatSummary::new();
1359        ss.record(10.0);
1360        ss.record(20.0);
1361        ss.record(30.0);
1362        assert_eq!(ss.count(), 3);
1363        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1364        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1365        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1366    }
1367    #[test]
1368    fn test_transform_stat() {
1369        let mut ts = TransformStat::new();
1370        ts.record_before(100.0);
1371        ts.record_after(80.0);
1372        let ratio = ts.mean_ratio().expect("ratio should be present");
1373        assert!((ratio - 0.8).abs() < 1e-9);
1374    }
1375    #[test]
1376    fn test_small_map() {
1377        let mut m: SmallMap<u32, &str> = SmallMap::new();
1378        m.insert(3, "three");
1379        m.insert(1, "one");
1380        m.insert(2, "two");
1381        assert_eq!(m.get(&2), Some(&"two"));
1382        assert_eq!(m.len(), 3);
1383        let keys = m.keys();
1384        assert_eq!(*keys[0], 1);
1385        assert_eq!(*keys[2], 3);
1386    }
1387    #[test]
1388    fn test_label_set() {
1389        let mut ls = LabelSet::new();
1390        ls.add("foo");
1391        ls.add("bar");
1392        ls.add("foo");
1393        assert_eq!(ls.count(), 2);
1394        assert!(ls.has("bar"));
1395        assert!(!ls.has("baz"));
1396    }
1397    #[test]
1398    fn test_config_node() {
1399        let mut root = ConfigNode::section("root");
1400        let child = ConfigNode::leaf("key", "value");
1401        root.add_child(child);
1402        assert_eq!(root.num_children(), 1);
1403    }
1404    #[test]
1405    fn test_versioned_record() {
1406        let mut vr = VersionedRecord::new(0u32);
1407        vr.update(1);
1408        vr.update(2);
1409        assert_eq!(*vr.current(), 2);
1410        assert_eq!(vr.version(), 2);
1411        assert!(vr.has_history());
1412        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1413    }
1414    #[test]
1415    fn test_simple_dag() {
1416        let mut dag = SimpleDag::new(4);
1417        dag.add_edge(0, 1);
1418        dag.add_edge(1, 2);
1419        dag.add_edge(2, 3);
1420        assert!(dag.can_reach(0, 3));
1421        assert!(!dag.can_reach(3, 0));
1422        let order = dag.topological_sort().expect("order should be present");
1423        assert_eq!(order, vec![0, 1, 2, 3]);
1424    }
1425    #[test]
1426    fn test_focus_stack() {
1427        let mut fs: FocusStack<&str> = FocusStack::new();
1428        fs.focus("a");
1429        fs.focus("b");
1430        assert_eq!(fs.current(), Some(&"b"));
1431        assert_eq!(fs.depth(), 2);
1432        fs.blur();
1433        assert_eq!(fs.current(), Some(&"a"));
1434    }
1435}
1436#[cfg(test)]
1437mod tests_extra_iterators {
1438    use super::*;
1439    #[test]
1440    fn test_window_iterator() {
1441        let data = vec![1u32, 2, 3, 4, 5];
1442        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1443        assert_eq!(windows.len(), 3);
1444        assert_eq!(windows[0], &[1, 2, 3]);
1445        assert_eq!(windows[2], &[3, 4, 5]);
1446    }
1447    #[test]
1448    fn test_non_empty_vec() {
1449        let mut nev = NonEmptyVec::singleton(10u32);
1450        nev.push(20);
1451        nev.push(30);
1452        assert_eq!(nev.len(), 3);
1453        assert_eq!(*nev.first(), 10);
1454        assert_eq!(*nev.last(), 30);
1455    }
1456}
1457#[cfg(test)]
1458mod tests_padding2 {
1459    use super::*;
1460    #[test]
1461    fn test_sliding_sum() {
1462        let mut ss = SlidingSum::new(3);
1463        ss.push(1.0);
1464        ss.push(2.0);
1465        ss.push(3.0);
1466        assert!((ss.sum() - 6.0).abs() < 1e-9);
1467        ss.push(4.0);
1468        assert!((ss.sum() - 9.0).abs() < 1e-9);
1469        assert_eq!(ss.count(), 3);
1470    }
1471    #[test]
1472    fn test_path_buf() {
1473        let mut pb = PathBuf::new();
1474        pb.push("src");
1475        pb.push("main");
1476        assert_eq!(pb.as_str(), "src/main");
1477        assert_eq!(pb.depth(), 2);
1478        pb.pop();
1479        assert_eq!(pb.as_str(), "src");
1480    }
1481    #[test]
1482    fn test_string_pool() {
1483        let mut pool = StringPool::new();
1484        let s = pool.take();
1485        assert!(s.is_empty());
1486        pool.give("hello".to_string());
1487        let s2 = pool.take();
1488        assert!(s2.is_empty());
1489        assert_eq!(pool.free_count(), 0);
1490    }
1491    #[test]
1492    fn test_transitive_closure() {
1493        let mut tc = TransitiveClosure::new(4);
1494        tc.add_edge(0, 1);
1495        tc.add_edge(1, 2);
1496        tc.add_edge(2, 3);
1497        assert!(tc.can_reach(0, 3));
1498        assert!(!tc.can_reach(3, 0));
1499        let r = tc.reachable_from(0);
1500        assert_eq!(r.len(), 4);
1501    }
1502    #[test]
1503    fn test_token_bucket() {
1504        let mut tb = TokenBucket::new(100, 0);
1505        assert_eq!(tb.available(), 100);
1506        assert!(tb.try_consume(50));
1507        assert_eq!(tb.available(), 50);
1508        assert!(!tb.try_consume(60));
1509        assert_eq!(tb.capacity(), 100);
1510    }
1511    #[test]
1512    fn test_rewrite_rule_set() {
1513        let mut rrs = RewriteRuleSet::new();
1514        rrs.add(RewriteRule::unconditional(
1515            "beta",
1516            "App(Lam(x, b), v)",
1517            "b[x:=v]",
1518        ));
1519        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1520        assert_eq!(rrs.len(), 2);
1521        assert_eq!(rrs.unconditional_rules().len(), 1);
1522        assert_eq!(rrs.conditional_rules().len(), 1);
1523        assert!(rrs.get("beta").is_some());
1524        let disp = rrs
1525            .get("beta")
1526            .expect("element at \'beta\' should exist")
1527            .display();
1528        assert!(disp.contains("→"));
1529    }
1530}
1531#[cfg(test)]
1532mod tests_padding3 {
1533    use super::*;
1534    #[test]
1535    fn test_decision_node() {
1536        let tree = DecisionNode::Branch {
1537            key: "x".into(),
1538            val: "1".into(),
1539            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1540            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1541        };
1542        let mut ctx = std::collections::HashMap::new();
1543        ctx.insert("x".into(), "1".into());
1544        assert_eq!(tree.evaluate(&ctx), "yes");
1545        ctx.insert("x".into(), "2".into());
1546        assert_eq!(tree.evaluate(&ctx), "no");
1547        assert_eq!(tree.depth(), 1);
1548    }
1549    #[test]
1550    fn test_flat_substitution() {
1551        let mut sub = FlatSubstitution::new();
1552        sub.add("foo", "bar");
1553        sub.add("baz", "qux");
1554        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1555        assert_eq!(sub.len(), 2);
1556    }
1557    #[test]
1558    fn test_stopwatch() {
1559        let mut sw = Stopwatch::start();
1560        sw.split();
1561        sw.split();
1562        assert_eq!(sw.num_splits(), 2);
1563        assert!(sw.elapsed_ms() >= 0.0);
1564        for &s in sw.splits() {
1565            assert!(s >= 0.0);
1566        }
1567    }
1568    #[test]
1569    fn test_either2() {
1570        let e: Either2<i32, &str> = Either2::First(42);
1571        assert!(e.is_first());
1572        let mapped = e.map_first(|x| x * 2);
1573        assert_eq!(mapped.first(), Some(84));
1574        let e2: Either2<i32, &str> = Either2::Second("hello");
1575        assert!(e2.is_second());
1576        assert_eq!(e2.second(), Some("hello"));
1577    }
1578    #[test]
1579    fn test_write_once() {
1580        let wo: WriteOnce<u32> = WriteOnce::new();
1581        assert!(!wo.is_written());
1582        assert!(wo.write(42));
1583        assert!(!wo.write(99));
1584        assert_eq!(wo.read(), Some(42));
1585    }
1586    #[test]
1587    fn test_sparse_vec() {
1588        let mut sv: SparseVec<i32> = SparseVec::new(100);
1589        sv.set(5, 10);
1590        sv.set(50, 20);
1591        assert_eq!(*sv.get(5), 10);
1592        assert_eq!(*sv.get(50), 20);
1593        assert_eq!(*sv.get(0), 0);
1594        assert_eq!(sv.nnz(), 2);
1595        sv.set(5, 0);
1596        assert_eq!(sv.nnz(), 1);
1597    }
1598    #[test]
1599    fn test_stack_calc() {
1600        let mut calc = StackCalc::new();
1601        calc.push(3);
1602        calc.push(4);
1603        assert_eq!(calc.add(), Some(7));
1604        assert_eq!(calc.peek(), Some(7));
1605        calc.push(2);
1606        assert_eq!(calc.mul(), Some(14));
1607        assert_eq!(calc.peek(), Some(14));
1608    }
1609}
1610#[cfg(test)]
1611mod tests_final_padding {
1612    use super::*;
1613    #[test]
1614    fn test_min_heap() {
1615        let mut h = MinHeap::new();
1616        h.push(5u32);
1617        h.push(1u32);
1618        h.push(3u32);
1619        assert_eq!(h.peek(), Some(&1));
1620        assert_eq!(h.pop(), Some(1));
1621        assert_eq!(h.pop(), Some(3));
1622        assert_eq!(h.pop(), Some(5));
1623        assert!(h.is_empty());
1624    }
1625    #[test]
1626    fn test_prefix_counter() {
1627        let mut pc = PrefixCounter::new();
1628        pc.record("hello");
1629        pc.record("help");
1630        pc.record("world");
1631        assert_eq!(pc.count_with_prefix("hel"), 2);
1632        assert_eq!(pc.count_with_prefix("wor"), 1);
1633        assert_eq!(pc.count_with_prefix("xyz"), 0);
1634    }
1635    #[test]
1636    fn test_fixture() {
1637        let mut f = Fixture::new();
1638        f.set("key1", "val1");
1639        f.set("key2", "val2");
1640        assert_eq!(f.get("key1"), Some("val1"));
1641        assert_eq!(f.get("key3"), None);
1642        assert_eq!(f.len(), 2);
1643    }
1644}