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/// Count the number of leading lambdas.
467pub fn count_lambdas(e: &Expr) -> u32 {
468    match e {
469        Expr::Lam(_, _, _, body) => 1 + count_lambdas(body),
470        _ => 0,
471    }
472}
473/// Count the number of leading Pi binders.
474pub fn count_pis(e: &Expr) -> u32 {
475    match e {
476        Expr::Pi(_, _, _, body) => 1 + count_pis(body),
477        _ => 0,
478    }
479}
480/// Get the body after stripping `n` leading lambdas.
481pub fn strip_lambdas(e: &Expr, n: u32) -> &Expr {
482    if n == 0 {
483        return e;
484    }
485    match e {
486        Expr::Lam(_, _, _, body) => strip_lambdas(body, n - 1),
487        _ => e,
488    }
489}
490/// Get the body after stripping `n` leading Pis.
491pub fn strip_pis(e: &Expr, n: u32) -> &Expr {
492    if n == 0 {
493        return e;
494    }
495    match e {
496        Expr::Pi(_, _, _, body) => strip_pis(body, n - 1),
497        _ => e,
498    }
499}
500/// Check if a level has metavariables.
501pub fn level_has_mvar(l: &Level) -> bool {
502    match l.view() {
503        LevelView::MVar(_) => true,
504        LevelView::Succ(l) => level_has_mvar(l),
505        LevelView::Max(l1, l2) | LevelView::IMax(l1, l2) => {
506            level_has_mvar(l1) || level_has_mvar(l2)
507        }
508        LevelView::Zero | LevelView::Param(_) => false,
509    }
510}
511/// Check if an expression has universe level metavariables.
512///
513/// O(1) in the size of the term: read off the flag every shared subterm
514/// caches (see [`Node`]); a `Sort` or a `Const` looks through its own levels.
515pub fn has_level_mvar(e: &Expr) -> bool {
516    e.has_level_mvar()
517}
518/// Check if an expression has universe level parameters.
519///
520/// O(1) in the size of the term, as [`has_level_mvar`].
521pub fn has_level_param(e: &Expr) -> bool {
522    e.has_level_param()
523}
524/// Compute the "weight" of an expression (rough size metric).
525pub fn expr_weight(e: &Expr) -> usize {
526    match e {
527        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Lit(_) => 1,
528        Expr::Const(_, _) => 1,
529        Expr::App(f, a) => 1 + expr_weight(f) + expr_weight(a),
530        Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
531            1 + expr_weight(ty) + expr_weight(body)
532        }
533        Expr::Let(_, ty, val, body) => 1 + expr_weight(ty) + expr_weight(val) + expr_weight(body),
534        Expr::Proj(_, _, e) => 1 + expr_weight(e),
535    }
536}
537/// Check if the head of an application is a constant with a specific name.
538pub fn is_app_of(e: &Expr, name: &Name) -> bool {
539    matches!(get_app_fn(e), Expr::Const(n, _) if n == name)
540}
541/// Build a non-dependent arrow type: `a → b`.
542pub fn mk_arrow(a: Expr, b: Expr) -> Expr {
543    Expr::Pi(
544        crate::BinderInfo::Default,
545        Name::anonymous(),
546        Node::new(a),
547        Node::new(lift_loose_bvars(&b, 1, 0)),
548    )
549}
550/// Build `Sort 0` (Prop).
551pub fn mk_prop() -> Expr {
552    Expr::Sort(Level::zero())
553}
554/// Build `Sort (u + 1)` (Type u).
555pub fn mk_type(u: Level) -> Expr {
556    Expr::Sort(Level::succ(u))
557}
558/// Build `Sort 1` (Type 0).
559pub fn mk_type0() -> Expr {
560    Expr::Sort(Level::succ(Level::zero()))
561}
562/// Shorthand: create a constant expression.
563pub fn var(name: &str) -> Expr {
564    Expr::Const(Name::str(name), vec![])
565}
566/// Shorthand: create a bound variable.
567pub fn bvar(idx: u32) -> Expr {
568    Expr::BVar(idx)
569}
570/// Shorthand: create an application.
571pub fn app(f: Expr, a: Expr) -> Expr {
572    Expr::App(Node::new(f), Node::new(a))
573}
574/// Shorthand: create a lambda.
575pub fn lam(name: &str, ty: Expr, body: Expr) -> Expr {
576    Expr::Lam(
577        crate::BinderInfo::Default,
578        Name::str(name),
579        Node::new(ty),
580        Node::new(body),
581    )
582}
583/// Shorthand: create a pi type.
584pub fn pi(name: &str, ty: Expr, body: Expr) -> Expr {
585    Expr::Pi(
586        crate::BinderInfo::Default,
587        Name::str(name),
588        Node::new(ty),
589        Node::new(body),
590    )
591}
592/// Shorthand: `Prop` = `Sort 0`.
593pub fn prop() -> Expr {
594    Expr::Sort(crate::Level::zero())
595}
596/// Shorthand: `Sort(Level::zero())`.
597pub fn sort() -> Expr {
598    Expr::Sort(crate::Level::zero())
599}
600/// Shorthand: `Type 0` = `Sort 1`.
601pub fn type0() -> Expr {
602    Expr::Sort(crate::Level::succ(crate::Level::zero()))
603}
604#[cfg(test)]
605mod size_tests {
606    use super::*;
607    use crate::{BinderInfo, Level, Literal};
608
609    fn leaf(i: u32) -> Expr {
610        match i % 5 {
611            0 => Expr::BVar(i),
612            1 => Expr::Const(Name::str("c"), vec![Level::zero()]),
613            2 => Expr::Sort(Level::zero()),
614            3 => Expr::Lit(Literal::nat(u64::from(i))),
615            _ => Expr::FVar(crate::FVarId(u64::from(i))),
616        }
617    }
618
619    /// Terms of every constructor and many shapes: unary chains (the size
620    /// is close to the cost), wide applications (the size is close to half
621    /// the cost) and everything between.
622    fn shapes() -> Vec<Expr> {
623        let mut terms: Vec<Expr> = (0..5).map(leaf).collect();
624        // Projection chains: one leaf, any number of internal nodes.
625        let mut chain = leaf(1);
626        for i in 0..12 {
627            chain = Expr::Proj(Name::str("S"), i, Node::new(chain));
628            terms.push(chain.clone());
629        }
630        // Left-nested application spines: as many leaves as internal nodes.
631        let mut spine = leaf(1);
632        for i in 0..12 {
633            spine = Expr::App(Node::new(spine), Node::new(leaf(i)));
634            terms.push(spine.clone());
635        }
636        // Balanced trees with shared children (the size counts every
637        // occurrence).
638        let mut tree = leaf(0);
639        for _ in 0..8 {
640            let shared = Node::new(tree);
641            tree = Expr::App(shared.clone(), shared);
642            terms.push(tree.clone());
643        }
644        // Binders and lets over the above.
645        for i in 0..6usize {
646            let ty = Node::new(terms[i * 3].clone());
647            let body = Node::new(terms[i * 5 + 1].clone());
648            terms.push(Expr::Lam(
649                BinderInfo::Default,
650                Name::str("x"),
651                ty.clone(),
652                body.clone(),
653            ));
654            terms.push(Expr::Pi(
655                BinderInfo::Implicit,
656                Name::str("x"),
657                ty.clone(),
658                body.clone(),
659            ));
660            terms.push(Expr::Let(Name::str("x"), ty.clone(), body.clone(), ty));
661        }
662        terms
663    }
664
665    /// The number of nodes of the tree, by plain recursion.
666    fn size(e: &Expr) -> usize {
667        match e {
668            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 1,
669            Expr::App(f, a) => 1 + size(f) + size(a),
670            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => 1 + size(ty) + size(body),
671            Expr::Let(_, ty, val, body) => 1 + size(ty) + size(val) + size(body),
672            Expr::Proj(_, _, inner) => 1 + size(inner),
673        }
674    }
675
676    #[test]
677    fn the_cached_cost_brackets_the_size() {
678        for (i, term) in shapes().iter().enumerate() {
679            let n = size(term) as u64;
680            let cost = u64::from(term.rebuild_cost());
681            assert!(
682                n <= cost && cost <= 2 * n,
683                "term {i}: size {n}, cost {cost}"
684            );
685        }
686    }
687
688    #[test]
689    fn size_within_agrees_with_counting_at_every_cap() {
690        let terms = shapes();
691        let mut by_cost = 0usize;
692        let mut by_walk = 0usize;
693        for (i, term) in terms.iter().enumerate() {
694            let n = size(term);
695            let cost = term.rebuild_cost() as usize;
696            for cap in (0..=2 * n + 3).chain([4096, usize::MAX / 2, usize::MAX]) {
697                let expected = n <= cap;
698                assert_eq!(
699                    expr_size_within(term, cap),
700                    expected,
701                    "term {i} (size {n}, cost {cost}) at cap {cap}"
702                );
703                assert_eq!(
704                    expr_size_within_by_walk(term, cap),
705                    expected,
706                    "walk: term {i} (size {n}) at cap {cap}"
707                );
708                if cost <= cap || cost > 2 * cap {
709                    by_cost += 1;
710                } else {
711                    by_walk += 1;
712                }
713            }
714        }
715        // Both ways of answering are exercised.
716        assert!(by_cost > 1000, "{by_cost} caps decided by the cost");
717        assert!(by_walk > 1000, "{by_walk} caps decided by counting");
718    }
719
720    #[test]
721    fn a_saturated_cost_never_reads_as_small() {
722        use crate::expr::REBUILD_COST_MAX;
723        // 40 levels of a doubling tower: 2^41 - 1 nodes, the cost saturated.
724        let mut tower = leaf(0);
725        for _ in 0..40 {
726            let shared = Node::new(tower);
727            tower = Expr::App(shared.clone(), shared);
728        }
729        assert_eq!(tower.rebuild_cost(), REBUILD_COST_MAX);
730        // Over every cap the caches use, and over a cap the saturated cost
731        // itself would pass.
732        assert!(!expr_size_within(&tower, 0));
733        assert!(!expr_size_within(&tower, 4096));
734        assert!(!expr_size_within(
735            &tower,
736            (REBUILD_COST_MAX as usize) / 2 - 1
737        ));
738        // 28 levels: 2^29 - 1 nodes, the cost saturated as well (it counts
739        // every leaf twice); a cap of 2^27 is under half the saturated cost,
740        // so the answer is still O(1).
741        let mut smaller = leaf(0);
742        for _ in 0..28 {
743            let shared = Node::new(smaller);
744            smaller = Expr::App(shared.clone(), shared);
745        }
746        assert_eq!(smaller.rebuild_cost(), REBUILD_COST_MAX);
747        assert!(!expr_size_within(&smaller, 1 << 27));
748        // One level short of saturating: the cost is exact and decides.
749        let mut exact = leaf(0);
750        for _ in 0..26 {
751            let shared = Node::new(exact);
752            exact = Expr::App(shared.clone(), shared);
753        }
754        // 2^26 leaves counted twice and 2^26 - 1 applications once.
755        assert_eq!(exact.rebuild_cost(), 3 * (1 << 26) - 1);
756        assert!(exact.rebuild_cost() < REBUILD_COST_MAX);
757        assert!(expr_size_within(&exact, 3 * (1 << 26)));
758        assert!(!expr_size_within(&exact, 1 << 20));
759    }
760}
761#[cfg(test)]
762mod tests {
763    use super::*;
764    use crate::BinderInfo;
765    fn nat() -> Expr {
766        Expr::Const(Name::str("Nat"), vec![])
767    }
768    fn bool_ty() -> Expr {
769        Expr::Const(Name::str("Bool"), vec![])
770    }
771    #[test]
772    fn test_get_app_fn() {
773        let f = nat();
774        let e = Expr::App(
775            Node::new(Expr::App(Node::new(f.clone()), Node::new(Expr::BVar(0)))),
776            Node::new(Expr::BVar(1)),
777        );
778        assert_eq!(get_app_fn(&e), &f);
779    }
780    #[test]
781    fn test_get_app_args() {
782        let f = nat();
783        let a1 = Expr::BVar(0);
784        let a2 = Expr::BVar(1);
785        let e = Expr::App(
786            Node::new(Expr::App(Node::new(f), Node::new(a1.clone()))),
787            Node::new(a2.clone()),
788        );
789        let args = get_app_args(&e);
790        assert_eq!(args.len(), 2);
791        assert_eq!(args[0], &a1);
792        assert_eq!(args[1], &a2);
793    }
794    #[test]
795    fn test_get_app_fn_args() {
796        let f = nat();
797        let a = Expr::BVar(0);
798        let e = Expr::App(Node::new(f.clone()), Node::new(a.clone()));
799        let (head, args) = get_app_fn_args(&e);
800        assert_eq!(head, &f);
801        assert_eq!(args, vec![&a]);
802    }
803    #[test]
804    fn test_mk_app() {
805        let f = nat();
806        let a1 = Expr::BVar(0);
807        let a2 = Expr::BVar(1);
808        let result = mk_app(f.clone(), &[a1.clone(), a2.clone()]);
809        let (head, args) = get_app_fn_args(&result);
810        assert_eq!(head, &f);
811        assert_eq!(args.len(), 2);
812    }
813    #[test]
814    fn test_has_loose_bvars() {
815        assert!(has_loose_bvars(&Expr::BVar(0)));
816        assert!(!has_loose_bvars(&nat()));
817        assert!(!has_loose_bvars(&Expr::FVar(FVarId(1))));
818        let lam = Expr::Lam(
819            BinderInfo::Default,
820            Name::str("x"),
821            Node::new(nat()),
822            Node::new(Expr::BVar(0)),
823        );
824        assert!(!has_loose_bvars(&lam));
825        let lam2 = Expr::Lam(
826            BinderInfo::Default,
827            Name::str("x"),
828            Node::new(nat()),
829            Node::new(Expr::BVar(1)),
830        );
831        assert!(has_loose_bvars(&lam2));
832    }
833    #[test]
834    fn test_has_fvar() {
835        let id = FVarId(42);
836        let e = Expr::App(Node::new(Expr::FVar(id)), Node::new(Expr::FVar(FVarId(99))));
837        assert!(has_fvar(&e, id));
838        assert!(has_fvar(&e, FVarId(99)));
839        assert!(!has_fvar(&e, FVarId(1)));
840    }
841    #[test]
842    fn test_collect_fvars() {
843        let e = Expr::App(
844            Node::new(Expr::FVar(FVarId(1))),
845            Node::new(Expr::App(
846                Node::new(Expr::FVar(FVarId(2))),
847                Node::new(Expr::FVar(FVarId(1))),
848            )),
849        );
850        let fvars = collect_fvars(&e);
851        assert_eq!(fvars.len(), 2);
852        assert!(fvars.contains(&FVarId(1)));
853        assert!(fvars.contains(&FVarId(2)));
854    }
855    #[test]
856    fn test_for_each_expr() {
857        let e = Expr::App(Node::new(nat()), Node::new(Expr::BVar(0)));
858        let mut count = 0;
859        for_each_expr(&e, &mut |_, _| {
860            count += 1;
861            true
862        });
863        assert_eq!(count, 3);
864    }
865    #[test]
866    fn test_replace_expr() {
867        let e = Expr::App(Node::new(Expr::BVar(0)), Node::new(Expr::BVar(1)));
868        let result = replace_expr(&e, &mut |sub, _depth| {
869            if let Expr::BVar(0) = sub {
870                Some(nat())
871            } else {
872                None
873            }
874        });
875        match &result {
876            Expr::App(f, _) => assert_eq!(**f, nat()),
877            _ => panic!("Expected App"),
878        }
879    }
880    #[test]
881    fn test_lift_loose_bvars() {
882        let e = Expr::BVar(0);
883        let lifted = lift_loose_bvars(&e, 2, 0);
884        assert_eq!(lifted, Expr::BVar(2));
885        let lam = Expr::Lam(
886            BinderInfo::Default,
887            Name::str("x"),
888            Node::new(nat()),
889            Node::new(Expr::BVar(0)),
890        );
891        let lifted_lam = lift_loose_bvars(&lam, 1, 0);
892        if let Expr::Lam(_, _, _, body) = &lifted_lam {
893            assert_eq!(**body, Expr::BVar(0));
894        } else {
895            panic!("Expected Lam");
896        }
897    }
898    #[test]
899    fn test_count_lambdas_pis() {
900        let e = Expr::Lam(
901            BinderInfo::Default,
902            Name::str("x"),
903            Node::new(nat()),
904            Node::new(Expr::Lam(
905                BinderInfo::Default,
906                Name::str("y"),
907                Node::new(nat()),
908                Node::new(Expr::BVar(0)),
909            )),
910        );
911        assert_eq!(count_lambdas(&e), 2);
912        let pi = Expr::Pi(
913            BinderInfo::Default,
914            Name::str("x"),
915            Node::new(nat()),
916            Node::new(nat()),
917        );
918        assert_eq!(count_pis(&pi), 1);
919    }
920    #[test]
921    fn test_is_app_of() {
922        let e = Expr::App(
923            Node::new(Expr::Const(Name::str("f"), vec![])),
924            Node::new(Expr::BVar(0)),
925        );
926        assert!(is_app_of(&e, &Name::str("f")));
927        assert!(!is_app_of(&e, &Name::str("g")));
928    }
929    #[test]
930    fn test_mk_arrow() {
931        let arrow = mk_arrow(nat(), bool_ty());
932        assert!(arrow.is_pi());
933    }
934    #[test]
935    fn test_expr_weight() {
936        assert_eq!(expr_weight(&nat()), 1);
937        let app = Expr::App(Node::new(nat()), Node::new(Expr::BVar(0)));
938        assert_eq!(expr_weight(&app), 3);
939    }
940    #[test]
941    fn test_mk_app_range() {
942        let f = nat();
943        let args = vec![Expr::BVar(0), Expr::BVar(1), Expr::BVar(2)];
944        let result = mk_app_range(f.clone(), &args, 1, 3);
945        assert_eq!(get_app_num_args(&result), 2);
946    }
947    #[test]
948    fn test_occurs_const() {
949        let e = Expr::App(Node::new(nat()), Node::new(bool_ty()));
950        assert!(occurs_const(&e, &Name::str("Nat")));
951        assert!(occurs_const(&e, &Name::str("Bool")));
952        assert!(!occurs_const(&e, &Name::str("Int")));
953    }
954}
955/// Check if the expression is a `Sort`.
956#[inline]
957pub fn is_sort(e: &Expr) -> bool {
958    matches!(e, Expr::Sort(_))
959}
960/// Check if the expression is `Sort(Level::Zero)` (i.e. `Prop`).
961#[inline]
962pub fn is_prop(e: &Expr) -> bool {
963    matches!(e, Expr::Sort(l) if matches!(l.view(), LevelView::Zero))
964}
965/// Check if the expression is `Sort(Level::Succ(Level::Zero))` (i.e. `Type 0`).
966#[inline]
967pub fn is_type0(e: &Expr) -> bool {
968    matches!(e, Expr::Sort(l) if matches!(l.view(), LevelView::Succ(inner) if matches!(inner.view(), LevelView::Zero)))
969}
970/// Check if the expression is a lambda abstraction.
971#[inline]
972pub fn is_lambda(e: &Expr) -> bool {
973    matches!(e, Expr::Lam(_, _, _, _))
974}
975/// Check if the expression is a Pi type.
976#[inline]
977pub fn is_pi(e: &Expr) -> bool {
978    matches!(e, Expr::Pi(_, _, _, _))
979}
980/// Check if the expression is an FVar.
981#[inline]
982pub fn is_fvar(e: &Expr) -> bool {
983    matches!(e, Expr::FVar(_))
984}
985/// Check if the expression is a BVar.
986#[inline]
987pub fn is_bvar(e: &Expr) -> bool {
988    matches!(e, Expr::BVar(_))
989}
990/// Check if the expression is a Literal.
991#[inline]
992pub fn is_literal(e: &Expr) -> bool {
993    matches!(e, Expr::Lit(_))
994}
995/// Check if the expression is a Const.
996#[inline]
997pub fn is_const(e: &Expr) -> bool {
998    matches!(e, Expr::Const(_, _))
999}
1000/// Check if the expression is an App.
1001#[inline]
1002pub fn is_app(e: &Expr) -> bool {
1003    matches!(e, Expr::App(_, _))
1004}
1005/// Check if the expression is a Let.
1006#[inline]
1007pub fn is_let(e: &Expr) -> bool {
1008    matches!(e, Expr::Let(_, _, _, _))
1009}
1010/// Check if the expression is a Proj.
1011#[inline]
1012pub fn is_proj(e: &Expr) -> bool {
1013    matches!(e, Expr::Proj(_, _, _))
1014}
1015/// Extract the level from a Sort expression.
1016#[inline]
1017pub fn get_sort_level(e: &Expr) -> Option<&Level> {
1018    if let Expr::Sort(l) = e {
1019        Some(l)
1020    } else {
1021        None
1022    }
1023}
1024/// Extract the name from a Const expression.
1025#[inline]
1026pub fn get_const_name(e: &Expr) -> Option<&Name> {
1027    if let Expr::Const(n, _) = e {
1028        Some(n)
1029    } else {
1030        None
1031    }
1032}
1033/// Extract the universe levels from a Const expression.
1034#[inline]
1035pub fn get_const_levels(e: &Expr) -> Option<&[Level]> {
1036    if let Expr::Const(_, ls) = e {
1037        Some(ls)
1038    } else {
1039        None
1040    }
1041}
1042/// Extract the FVarId from an FVar expression.
1043#[inline]
1044pub fn get_fvar_id(e: &Expr) -> Option<FVarId> {
1045    if let Expr::FVar(id) = e {
1046        Some(*id)
1047    } else {
1048        None
1049    }
1050}
1051/// Extract the BVar index from a BVar expression.
1052#[inline]
1053pub fn get_bvar_idx(e: &Expr) -> Option<u32> {
1054    if let Expr::BVar(i) = e {
1055        Some(*i)
1056    } else {
1057        None
1058    }
1059}
1060/// Decompose a Let expression into `(type, value, body)`.
1061#[inline]
1062pub fn decompose_let(e: &Expr) -> Option<(&Expr, &Expr, &Expr)> {
1063    if let Expr::Let(_, ty, val, body) = e {
1064        Some((ty, val, body))
1065    } else {
1066        None
1067    }
1068}
1069/// Decompose a Pi expression into `(binder_info, domain, codomain)`.
1070#[inline]
1071pub fn decompose_pi(e: &Expr) -> Option<(crate::BinderInfo, &Expr, &Expr)> {
1072    if let Expr::Pi(bi, _, dom, cod) = e {
1073        Some((*bi, dom, cod))
1074    } else {
1075        None
1076    }
1077}
1078/// Decompose a Lam expression into `(binder_info, domain, body)`.
1079#[inline]
1080pub fn decompose_lam(e: &Expr) -> Option<(crate::BinderInfo, &Expr, &Expr)> {
1081    if let Expr::Lam(bi, _, dom, body) = e {
1082        Some((*bi, dom, body))
1083    } else {
1084        None
1085    }
1086}
1087/// Decompose a Proj expression into `(struct_name, field_index, value)`.
1088#[inline]
1089pub fn decompose_proj(e: &Expr) -> Option<(&Name, usize, &Expr)> {
1090    if let Expr::Proj(n, idx, val) = e {
1091        Some((n, *idx as usize, val))
1092    } else {
1093        None
1094    }
1095}
1096/// Build a nested Pi type from a list of binders and a return type.
1097///
1098/// `mk_pi_n([(bi1, ty1), ..., (bin, tyn)], ret)` produces
1099/// `Pi bi1 ty1 (Pi bi2 ty2 (... (Pi bin tyn ret) ...))`.
1100pub fn mk_pi_n(binders: &[(crate::BinderInfo, Expr)], ret: Expr) -> Expr {
1101    binders.iter().rev().fold(ret, |acc, (bi, ty)| {
1102        Expr::Pi(
1103            *bi,
1104            crate::Name::anonymous(),
1105            Node::new(ty.clone()),
1106            Node::new(acc),
1107        )
1108    })
1109}
1110/// Build a nested Lam abstraction from a list of binders and a body.
1111pub fn mk_lam_n(binders: &[(crate::BinderInfo, Expr)], body: Expr) -> Expr {
1112    binders.iter().rev().fold(body, |acc, (bi, ty)| {
1113        Expr::Lam(
1114            *bi,
1115            crate::Name::anonymous(),
1116            Node::new(ty.clone()),
1117            Node::new(acc),
1118        )
1119    })
1120}
1121/// Check if `e` is an application of FVar `id`.
1122pub fn is_app_of_fvar(e: &Expr, id: FVarId) -> bool {
1123    get_app_fn(e) == &Expr::FVar(id)
1124}
1125/// Get the `n`-th argument in an application chain (0-indexed).
1126pub fn get_nth_arg(e: &Expr, n: usize) -> Option<&Expr> {
1127    let args = get_app_args(e);
1128    args.get(n).copied()
1129}
1130/// Check if an expression is "simple" (atomic: no subexpressions).
1131pub fn is_simple(e: &Expr) -> bool {
1132    matches!(
1133        e,
1134        Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_)
1135    )
1136}
1137#[cfg(test)]
1138mod extended_tests {
1139    use super::*;
1140    use crate::{BinderInfo, Level};
1141    fn nat() -> Expr {
1142        Expr::Const(Name::str("Nat"), vec![])
1143    }
1144    fn prop() -> Expr {
1145        Expr::Sort(Level::zero())
1146    }
1147    fn bv(i: u32) -> Expr {
1148        Expr::BVar(i)
1149    }
1150    #[test]
1151    fn test_is_prop() {
1152        assert!(is_prop(&prop()));
1153        assert!(!is_prop(&nat()));
1154    }
1155    #[test]
1156    fn test_is_sort() {
1157        assert!(is_sort(&prop()));
1158        assert!(is_sort(&Expr::Sort(Level::succ(Level::zero()))));
1159        assert!(!is_sort(&nat()));
1160    }
1161    #[test]
1162    fn test_is_lambda_pi() {
1163        let lam = Expr::Lam(
1164            BinderInfo::Default,
1165            crate::Name::anonymous(),
1166            Node::new(prop()),
1167            Node::new(bv(0)),
1168        );
1169        let pi = Expr::Pi(
1170            BinderInfo::Default,
1171            crate::Name::anonymous(),
1172            Node::new(prop()),
1173            Node::new(bv(0)),
1174        );
1175        assert!(is_lambda(&lam));
1176        assert!(!is_lambda(&pi));
1177        assert!(is_pi(&pi));
1178        assert!(!is_pi(&lam));
1179    }
1180    #[test]
1181    fn test_is_fvar_bvar() {
1182        use crate::FVarId;
1183        assert!(is_fvar(&Expr::FVar(FVarId(0))));
1184        assert!(is_bvar(&bv(0)));
1185        assert!(!is_fvar(&nat()));
1186    }
1187    #[test]
1188    fn test_get_sort_level() {
1189        let s = Expr::Sort(Level::zero());
1190        assert_eq!(get_sort_level(&s), Some(&Level::zero()));
1191        assert_eq!(get_sort_level(&nat()), None);
1192    }
1193    #[test]
1194    fn test_get_const_name() {
1195        assert_eq!(get_const_name(&nat()), Some(&Name::str("Nat")));
1196        assert_eq!(get_const_name(&prop()), None);
1197    }
1198    #[test]
1199    fn test_get_bvar_idx() {
1200        assert_eq!(get_bvar_idx(&bv(3)), Some(3));
1201        assert_eq!(get_bvar_idx(&nat()), None);
1202    }
1203    #[test]
1204    fn test_decompose_let() {
1205        let e = Expr::Let(
1206            crate::Name::anonymous(),
1207            Node::new(nat()),
1208            Node::new(bv(0)),
1209            Node::new(bv(0)),
1210        );
1211        let (ty, val, body) = decompose_let(&e).expect("value should be present");
1212        assert_eq!(ty, &nat());
1213        assert_eq!(val, &bv(0));
1214        assert_eq!(body, &bv(0));
1215    }
1216    #[test]
1217    fn test_decompose_pi() {
1218        let e = Expr::Pi(
1219            BinderInfo::Default,
1220            crate::Name::anonymous(),
1221            Node::new(nat()),
1222            Node::new(prop()),
1223        );
1224        let (bi, dom, cod) = decompose_pi(&e).expect("value should be present");
1225        assert_eq!(bi, BinderInfo::Default);
1226        assert_eq!(dom, &nat());
1227        assert_eq!(cod, &prop());
1228    }
1229    #[test]
1230    fn test_decompose_lam() {
1231        let e = Expr::Lam(
1232            BinderInfo::Implicit,
1233            crate::Name::anonymous(),
1234            Node::new(nat()),
1235            Node::new(bv(0)),
1236        );
1237        let (bi, dom, body) = decompose_lam(&e).expect("value should be present");
1238        assert_eq!(bi, BinderInfo::Implicit);
1239        assert_eq!(dom, &nat());
1240        assert_eq!(body, &bv(0));
1241    }
1242    #[test]
1243    fn test_mk_pi_n_empty() {
1244        let ret = nat();
1245        let result = mk_pi_n(&[], ret.clone());
1246        assert_eq!(result, ret);
1247    }
1248    #[test]
1249    fn test_mk_pi_n_two() {
1250        let binders = vec![(BinderInfo::Default, nat()), (BinderInfo::Default, prop())];
1251        let ret = bv(0);
1252        let result = mk_pi_n(&binders, ret);
1253        assert!(is_pi(&result));
1254        let (_, _, inner) = decompose_pi(&result).expect("value should be present");
1255        assert!(is_pi(inner));
1256    }
1257    #[test]
1258    fn test_mk_lam_n_two() {
1259        let binders = vec![(BinderInfo::Default, nat()), (BinderInfo::Implicit, prop())];
1260        let result = mk_lam_n(&binders, bv(0));
1261        assert!(is_lambda(&result));
1262    }
1263    #[test]
1264    fn test_is_simple() {
1265        assert!(is_simple(&nat()));
1266        assert!(is_simple(&bv(0)));
1267        assert!(is_simple(&prop()));
1268        let app = Expr::App(Node::new(nat()), Node::new(bv(0)));
1269        assert!(!is_simple(&app));
1270    }
1271    #[test]
1272    fn test_get_nth_arg() {
1273        let f = nat();
1274        let arg0 = bv(0);
1275        let arg1 = bv(1);
1276        let app = Expr::App(
1277            Node::new(Expr::App(Node::new(f), Node::new(arg0.clone()))),
1278            Node::new(arg1.clone()),
1279        );
1280        assert_eq!(get_nth_arg(&app, 0), Some(&arg0));
1281        assert_eq!(get_nth_arg(&app, 1), Some(&arg1));
1282        assert_eq!(get_nth_arg(&app, 2), None);
1283    }
1284    #[test]
1285    fn test_is_literal() {
1286        use crate::Literal;
1287        let lit = Expr::Lit(Literal::nat(42));
1288        assert!(is_literal(&lit));
1289        assert!(!is_literal(&nat()));
1290    }
1291}
1292#[cfg(test)]
1293mod tests_padding_infra {
1294    use super::*;
1295    #[test]
1296    fn test_stat_summary() {
1297        let mut ss = StatSummary::new();
1298        ss.record(10.0);
1299        ss.record(20.0);
1300        ss.record(30.0);
1301        assert_eq!(ss.count(), 3);
1302        assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1303        assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1304        assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1305    }
1306    #[test]
1307    fn test_transform_stat() {
1308        let mut ts = TransformStat::new();
1309        ts.record_before(100.0);
1310        ts.record_after(80.0);
1311        let ratio = ts.mean_ratio().expect("ratio should be present");
1312        assert!((ratio - 0.8).abs() < 1e-9);
1313    }
1314    #[test]
1315    fn test_small_map() {
1316        let mut m: SmallMap<u32, &str> = SmallMap::new();
1317        m.insert(3, "three");
1318        m.insert(1, "one");
1319        m.insert(2, "two");
1320        assert_eq!(m.get(&2), Some(&"two"));
1321        assert_eq!(m.len(), 3);
1322        let keys = m.keys();
1323        assert_eq!(*keys[0], 1);
1324        assert_eq!(*keys[2], 3);
1325    }
1326    #[test]
1327    fn test_label_set() {
1328        let mut ls = LabelSet::new();
1329        ls.add("foo");
1330        ls.add("bar");
1331        ls.add("foo");
1332        assert_eq!(ls.count(), 2);
1333        assert!(ls.has("bar"));
1334        assert!(!ls.has("baz"));
1335    }
1336    #[test]
1337    fn test_config_node() {
1338        let mut root = ConfigNode::section("root");
1339        let child = ConfigNode::leaf("key", "value");
1340        root.add_child(child);
1341        assert_eq!(root.num_children(), 1);
1342    }
1343    #[test]
1344    fn test_versioned_record() {
1345        let mut vr = VersionedRecord::new(0u32);
1346        vr.update(1);
1347        vr.update(2);
1348        assert_eq!(*vr.current(), 2);
1349        assert_eq!(vr.version(), 2);
1350        assert!(vr.has_history());
1351        assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1352    }
1353    #[test]
1354    fn test_simple_dag() {
1355        let mut dag = SimpleDag::new(4);
1356        dag.add_edge(0, 1);
1357        dag.add_edge(1, 2);
1358        dag.add_edge(2, 3);
1359        assert!(dag.can_reach(0, 3));
1360        assert!(!dag.can_reach(3, 0));
1361        let order = dag.topological_sort().expect("order should be present");
1362        assert_eq!(order, vec![0, 1, 2, 3]);
1363    }
1364    #[test]
1365    fn test_focus_stack() {
1366        let mut fs: FocusStack<&str> = FocusStack::new();
1367        fs.focus("a");
1368        fs.focus("b");
1369        assert_eq!(fs.current(), Some(&"b"));
1370        assert_eq!(fs.depth(), 2);
1371        fs.blur();
1372        assert_eq!(fs.current(), Some(&"a"));
1373    }
1374}
1375#[cfg(test)]
1376mod tests_extra_iterators {
1377    use super::*;
1378    #[test]
1379    fn test_window_iterator() {
1380        let data = vec![1u32, 2, 3, 4, 5];
1381        let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1382        assert_eq!(windows.len(), 3);
1383        assert_eq!(windows[0], &[1, 2, 3]);
1384        assert_eq!(windows[2], &[3, 4, 5]);
1385    }
1386    #[test]
1387    fn test_non_empty_vec() {
1388        let mut nev = NonEmptyVec::singleton(10u32);
1389        nev.push(20);
1390        nev.push(30);
1391        assert_eq!(nev.len(), 3);
1392        assert_eq!(*nev.first(), 10);
1393        assert_eq!(*nev.last(), 30);
1394    }
1395}
1396#[cfg(test)]
1397mod tests_padding2 {
1398    use super::*;
1399    #[test]
1400    fn test_sliding_sum() {
1401        let mut ss = SlidingSum::new(3);
1402        ss.push(1.0);
1403        ss.push(2.0);
1404        ss.push(3.0);
1405        assert!((ss.sum() - 6.0).abs() < 1e-9);
1406        ss.push(4.0);
1407        assert!((ss.sum() - 9.0).abs() < 1e-9);
1408        assert_eq!(ss.count(), 3);
1409    }
1410    #[test]
1411    fn test_path_buf() {
1412        let mut pb = PathBuf::new();
1413        pb.push("src");
1414        pb.push("main");
1415        assert_eq!(pb.as_str(), "src/main");
1416        assert_eq!(pb.depth(), 2);
1417        pb.pop();
1418        assert_eq!(pb.as_str(), "src");
1419    }
1420    #[test]
1421    fn test_string_pool() {
1422        let mut pool = StringPool::new();
1423        let s = pool.take();
1424        assert!(s.is_empty());
1425        pool.give("hello".to_string());
1426        let s2 = pool.take();
1427        assert!(s2.is_empty());
1428        assert_eq!(pool.free_count(), 0);
1429    }
1430    #[test]
1431    fn test_transitive_closure() {
1432        let mut tc = TransitiveClosure::new(4);
1433        tc.add_edge(0, 1);
1434        tc.add_edge(1, 2);
1435        tc.add_edge(2, 3);
1436        assert!(tc.can_reach(0, 3));
1437        assert!(!tc.can_reach(3, 0));
1438        let r = tc.reachable_from(0);
1439        assert_eq!(r.len(), 4);
1440    }
1441    #[test]
1442    fn test_token_bucket() {
1443        let mut tb = TokenBucket::new(100, 0);
1444        assert_eq!(tb.available(), 100);
1445        assert!(tb.try_consume(50));
1446        assert_eq!(tb.available(), 50);
1447        assert!(!tb.try_consume(60));
1448        assert_eq!(tb.capacity(), 100);
1449    }
1450    #[test]
1451    fn test_rewrite_rule_set() {
1452        let mut rrs = RewriteRuleSet::new();
1453        rrs.add(RewriteRule::unconditional(
1454            "beta",
1455            "App(Lam(x, b), v)",
1456            "b[x:=v]",
1457        ));
1458        rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1459        assert_eq!(rrs.len(), 2);
1460        assert_eq!(rrs.unconditional_rules().len(), 1);
1461        assert_eq!(rrs.conditional_rules().len(), 1);
1462        assert!(rrs.get("beta").is_some());
1463        let disp = rrs
1464            .get("beta")
1465            .expect("element at \'beta\' should exist")
1466            .display();
1467        assert!(disp.contains("→"));
1468    }
1469}
1470#[cfg(test)]
1471mod tests_padding3 {
1472    use super::*;
1473    #[test]
1474    fn test_decision_node() {
1475        let tree = DecisionNode::Branch {
1476            key: "x".into(),
1477            val: "1".into(),
1478            yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1479            no_branch: Box::new(DecisionNode::Leaf("no".into())),
1480        };
1481        let mut ctx = std::collections::HashMap::new();
1482        ctx.insert("x".into(), "1".into());
1483        assert_eq!(tree.evaluate(&ctx), "yes");
1484        ctx.insert("x".into(), "2".into());
1485        assert_eq!(tree.evaluate(&ctx), "no");
1486        assert_eq!(tree.depth(), 1);
1487    }
1488    #[test]
1489    fn test_flat_substitution() {
1490        let mut sub = FlatSubstitution::new();
1491        sub.add("foo", "bar");
1492        sub.add("baz", "qux");
1493        assert_eq!(sub.apply("foo and baz"), "bar and qux");
1494        assert_eq!(sub.len(), 2);
1495    }
1496    #[test]
1497    fn test_stopwatch() {
1498        let mut sw = Stopwatch::start();
1499        sw.split();
1500        sw.split();
1501        assert_eq!(sw.num_splits(), 2);
1502        assert!(sw.elapsed_ms() >= 0.0);
1503        for &s in sw.splits() {
1504            assert!(s >= 0.0);
1505        }
1506    }
1507    #[test]
1508    fn test_either2() {
1509        let e: Either2<i32, &str> = Either2::First(42);
1510        assert!(e.is_first());
1511        let mapped = e.map_first(|x| x * 2);
1512        assert_eq!(mapped.first(), Some(84));
1513        let e2: Either2<i32, &str> = Either2::Second("hello");
1514        assert!(e2.is_second());
1515        assert_eq!(e2.second(), Some("hello"));
1516    }
1517    #[test]
1518    fn test_write_once() {
1519        let wo: WriteOnce<u32> = WriteOnce::new();
1520        assert!(!wo.is_written());
1521        assert!(wo.write(42));
1522        assert!(!wo.write(99));
1523        assert_eq!(wo.read(), Some(42));
1524    }
1525    #[test]
1526    fn test_sparse_vec() {
1527        let mut sv: SparseVec<i32> = SparseVec::new(100);
1528        sv.set(5, 10);
1529        sv.set(50, 20);
1530        assert_eq!(*sv.get(5), 10);
1531        assert_eq!(*sv.get(50), 20);
1532        assert_eq!(*sv.get(0), 0);
1533        assert_eq!(sv.nnz(), 2);
1534        sv.set(5, 0);
1535        assert_eq!(sv.nnz(), 1);
1536    }
1537    #[test]
1538    fn test_stack_calc() {
1539        let mut calc = StackCalc::new();
1540        calc.push(3);
1541        calc.push(4);
1542        assert_eq!(calc.add(), Some(7));
1543        assert_eq!(calc.peek(), Some(7));
1544        calc.push(2);
1545        assert_eq!(calc.mul(), Some(14));
1546        assert_eq!(calc.peek(), Some(14));
1547    }
1548}
1549#[cfg(test)]
1550mod tests_final_padding {
1551    use super::*;
1552    #[test]
1553    fn test_min_heap() {
1554        let mut h = MinHeap::new();
1555        h.push(5u32);
1556        h.push(1u32);
1557        h.push(3u32);
1558        assert_eq!(h.peek(), Some(&1));
1559        assert_eq!(h.pop(), Some(1));
1560        assert_eq!(h.pop(), Some(3));
1561        assert_eq!(h.pop(), Some(5));
1562        assert!(h.is_empty());
1563    }
1564    #[test]
1565    fn test_prefix_counter() {
1566        let mut pc = PrefixCounter::new();
1567        pc.record("hello");
1568        pc.record("help");
1569        pc.record("world");
1570        assert_eq!(pc.count_with_prefix("hel"), 2);
1571        assert_eq!(pc.count_with_prefix("wor"), 1);
1572        assert_eq!(pc.count_with_prefix("xyz"), 0);
1573    }
1574    #[test]
1575    fn test_fixture() {
1576        let mut f = Fixture::new();
1577        f.set("key1", "val1");
1578        f.set("key2", "val2");
1579        assert_eq!(f.get("key1"), Some("val1"));
1580        assert_eq!(f.get("key3"), None);
1581        assert_eq!(f.len(), 2);
1582    }
1583}