Skip to main content

oxilean_std/eq/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::env_builder::{app, bvar, pi, pi_implicit, pi_named, prop, sort, var, EnvBuilder};
6use oxilean_kernel::Node;
7use oxilean_kernel::{Declaration, Environment, Expr, Level, Name};
8
9use super::types::{
10    DecisionResult, EqBuilder, EqChain, EqRewriteRule, EqualityDatabase, EqualityWitness, PropEq,
11    RewriteRuleDb, SetoidMorphism,
12};
13
14/// Build the `Eq.refl` proof for a given expression.
15///
16/// Returns `@Eq.refl α a`, which is the canonical reflexivity proof for
17/// propositional equality in the Lean 4 kernel.
18pub fn eq_refl(alpha: Expr, a: Expr) -> Expr {
19    app(app(app(var("Eq.refl"), alpha), a.clone()), a)
20}
21/// Build `Eq.symm` applied to a given proof.
22///
23/// Given `h : a = b`, returns `@Eq.symm α a b h : b = a`.
24pub fn eq_symm(alpha: Expr, a: Expr, b: Expr, h: Expr) -> Expr {
25    app(
26        app(app(app(app(var("Eq.symm"), alpha), a), b), h.clone()),
27        h,
28    )
29}
30/// Build `Eq.trans` applied to two proofs.
31///
32/// Given `h1 : a = b` and `h2 : b = c`, returns `@Eq.trans α a b c h1 h2`.
33pub fn eq_trans(alpha: Expr, a: Expr, b: Expr, c: Expr, h1: Expr, h2: Expr) -> Expr {
34    app(
35        app(
36            app(app(app(app(app(var("Eq.trans"), alpha), a), b), c), h1),
37            h2.clone(),
38        ),
39        h2,
40    )
41}
42/// Build `Eq.subst` applied to a proof.
43///
44/// Given `h : a = b` and `p : α → Prop` and `ha : p a`, returns `Eq.subst h ha`.
45pub fn eq_subst(alpha: Expr, pred: Expr, a: Expr, b: Expr, h: Expr, ha: Expr) -> Expr {
46    app(
47        app(app(app(app(app(var("Eq.subst"), alpha), pred), a), b), h),
48        ha,
49    )
50}
51/// Build `congrArg` — congruence under function application.
52///
53/// Given `h : a = b`, returns `@congrArg α β a b f h : f a = f b`.
54pub fn congr_arg(alpha: Expr, beta: Expr, a: Expr, b: Expr, f: Expr, h: Expr) -> Expr {
55    app(
56        app(app(app(app(app(var("congrArg"), alpha), beta), a), b), f),
57        h,
58    )
59}
60/// Build `congrFun` — congruence of function application.
61///
62/// Given `h : f = g`, returns `@congrFun α β f g h a : f a = g a`.
63pub fn congr_fun(alpha: Expr, beta: Expr, f: Expr, g: Expr, h: Expr, a: Expr) -> Expr {
64    app(
65        app(app(app(app(app(var("congrFun"), alpha), beta), f), g), h),
66        a,
67    )
68}
69/// Build `HEq.refl` — the reflexivity proof for heterogeneous equality.
70///
71/// Returns `@HEq.refl α a : HEq a a`.
72pub fn heq_refl(alpha: Expr, a: Expr) -> Expr {
73    app(app(var("HEq.refl"), alpha), a)
74}
75/// Build `eq_of_heq` — recover homogeneous equality from heterogeneous.
76///
77/// Given `h : HEq a b` (where both have the same type), returns `h' : a = b`.
78pub fn eq_of_heq(alpha: Expr, a: Expr, b: Expr, h: Expr) -> Expr {
79    app(app(app(app(var("eq_of_heq"), alpha), a), b), h)
80}
81/// Build `heq_of_eq` — lift homogeneous equality to heterogeneous.
82///
83/// Given `h : a = b`, returns `@heq_of_eq α a b h : HEq a b`.
84pub fn heq_of_eq(alpha: Expr, a: Expr, b: Expr, h: Expr) -> Expr {
85    app(app(app(app(var("heq_of_eq"), alpha), a), b), h)
86}
87/// Build the `BEq` environment entry for a type.
88///
89/// Produces the `BEq` instance declaration using a boolean equality function
90/// `beq_fn : α → α → Bool`.
91pub fn build_beq_env(env: &mut EnvBuilder, type_name: &str, beq_fn: Expr) {
92    let inst_name = format!("instBEq{type_name}");
93    let ty = var(&format!("{type_name}.BEq"));
94    let body = app(var("BEq.mk"), beq_fn);
95    env.add_definition(Name::from_str(&inst_name), ty, body);
96}
97/// Build the `DecidableEq` environment entry for a type.
98///
99/// Produces a `DecidableEq` instance via a decision procedure `dec_fn`.
100pub fn build_decidable_eq_env(env: &mut EnvBuilder, type_name: &str, dec_fn: Expr) {
101    let inst_name = format!("instDecidableEq{type_name}");
102    let ty = pi(var(type_name), pi(var(type_name), var("Prop")));
103    let body = app(var("DecidableEq.mk"), dec_fn);
104    env.add_definition(Name::from_str(&inst_name), ty, body);
105}
106/// Build the standard `HEq` environment entries.
107///
108/// Registers `HEq`, `HEq.refl`, `HEq.symm`, `HEq.trans`, and `heq_of_eq`
109/// into the given builder.
110pub fn build_heq_env(env: &mut EnvBuilder) {
111    env.add_axiom(Name::from_str("HEq"), sort(1));
112    env.add_axiom(Name::from_str("HEq.refl"), sort(1));
113    env.add_axiom(Name::from_str("HEq.symm"), sort(1));
114    env.add_axiom(Name::from_str("HEq.trans"), sort(1));
115    env.add_axiom(Name::from_str("heq_of_eq"), sort(1));
116    env.add_axiom(Name::from_str("eq_of_heq"), sort(1));
117}
118/// Decidable equality at the Rust/meta level.
119///
120/// This mirrors the Lean 4 `DecidableEq` typeclass but operates on Rust types
121/// used inside the OxiLean implementation.
122pub trait DecidableEq: PartialEq {
123    /// Returns `true` if `self == other`.
124    fn decide_eq(&self, other: &Self) -> bool {
125        self == other
126    }
127    /// Returns `Some(())` if equal, `None` if not.
128    fn witness_eq(&self, other: &Self) -> Option<()> {
129        if self == other {
130            Some(())
131        } else {
132            None
133        }
134    }
135}
136impl DecidableEq for u8 {}
137impl DecidableEq for u16 {}
138impl DecidableEq for u32 {}
139impl DecidableEq for u64 {}
140impl DecidableEq for usize {}
141impl DecidableEq for i8 {}
142impl DecidableEq for i16 {}
143impl DecidableEq for i32 {}
144impl DecidableEq for i64 {}
145impl DecidableEq for isize {}
146impl DecidableEq for bool {}
147impl DecidableEq for char {}
148impl DecidableEq for String {}
149impl DecidableEq for str {}
150impl<T: PartialEq> DecidableEq for Vec<T> {}
151impl<T: PartialEq> DecidableEq for Option<T> {}
152impl<A: PartialEq, B: PartialEq> DecidableEq for (A, B) {}
153/// Check whether two `Expr` nodes are alpha-equal (ignoring binder names).
154///
155/// This is a syntactic check only; it does not perform beta/eta reduction.
156pub fn structural_eq(a: &Expr, b: &Expr) -> bool {
157    a == b
158}
159/// Check whether two `Name`s are definitionally equal as strings.
160pub fn name_eq(a: &Name, b: &Name) -> bool {
161    a == b
162}
163/// Check equality of a list of expressions pairwise.
164///
165/// Returns `true` iff both slices have the same length and all pairs are
166/// structurally equal.
167pub fn exprs_eq(xs: &[Expr], ys: &[Expr]) -> bool {
168    xs.len() == ys.len() && xs.iter().zip(ys).all(|(x, y)| x == y)
169}
170/// Decide equality of two `u32` values, returning a `DecisionResult`.
171pub fn decide_u32_eq(a: u32, b: u32) -> DecisionResult<()> {
172    if a == b {
173        DecisionResult::IsTrue(())
174    } else {
175        DecisionResult::IsFalse(format!("{a} ≠ {b}"))
176    }
177}
178/// Decide equality of two string slices, returning a `DecisionResult`.
179pub fn decide_str_eq(a: &str, b: &str) -> DecisionResult<()> {
180    if a == b {
181        DecisionResult::IsTrue(())
182    } else {
183        DecisionResult::IsFalse(format!("{a:?} ≠ {b:?}"))
184    }
185}
186/// Decide equality of two `Name` values.
187pub fn decide_name_eq(a: &Name, b: &Name) -> DecisionResult<()> {
188    if a == b {
189        DecisionResult::IsTrue(())
190    } else {
191        DecisionResult::IsFalse(format!("{a} ≠ {b}"))
192    }
193}
194/// A setoid: a type equipped with an equivalence relation.
195///
196/// This is the Rust-level analogue of Lean 4's `Setoid` typeclass,
197/// used for quotiented types and proof-irrelevant equality.
198pub trait Setoid {
199    /// The equivalence relation.
200    fn equiv(&self, other: &Self) -> bool;
201    /// Reflexivity of the equivalence.
202    fn refl(&self) -> bool {
203        self.equiv(self)
204    }
205    /// Symmetry: if `self ~ other` then `other ~ self`.
206    fn symm(&self, other: &Self) -> bool {
207        if self.equiv(other) {
208            other.equiv(self)
209        } else {
210            true
211        }
212    }
213}
214impl<T: PartialEq> Setoid for T {
215    fn equiv(&self, other: &Self) -> bool {
216        self == other
217    }
218}
219/// Apply congruence: if `f = g` and `a = b` then `f a = g b`.
220///
221/// At the Rust meta-level this is simply function application, but this
222/// function makes the intent explicit.
223pub fn congr<A, B>(f: impl Fn(A) -> B, a: A) -> B {
224    f(a)
225}
226/// Build a proof of `a = a` (reflexivity) at the meta level.
227///
228/// This is a no-op on the Rust side but provides a uniform API.
229pub fn refl<T: PartialEq>(a: T) -> EqualityWitness<T> {
230    EqualityWitness { value: a }
231}
232/// Substitute equals for equals at the Rust meta level.
233///
234/// Given a witness `a = b` and a value `pa : P a`, returns `pa` interpreted
235/// as `P b`. Since Rust is monomorphic, this is just the identity.
236pub fn subst<T, P>(_witness: &EqualityWitness<T>, pa: P) -> P {
237    pa
238}
239/// Determine whether an expression is syntactically an equality proposition.
240///
241/// Returns `Some((ty, lhs, rhs))` if the expression has the form `@Eq ty lhs rhs`,
242/// otherwise `None`.
243pub fn as_eq(e: &Expr) -> Option<(Expr, Expr, Expr)> {
244    match e {
245        Expr::App(f, rhs) => match f.as_ref() {
246            Expr::App(g, lhs) => match g.as_ref() {
247                Expr::App(h, ty) => match h.as_ref() {
248                    Expr::Const(n, _) if n.to_string() == "Eq" => Some((
249                        ty.as_ref().clone(),
250                        lhs.as_ref().clone(),
251                        rhs.as_ref().clone(),
252                    )),
253                    _ => None,
254                },
255                _ => None,
256            },
257            _ => None,
258        },
259        _ => None,
260    }
261}
262/// Determine whether an expression is a heterogeneous equality (`HEq`).
263pub fn as_heq(e: &Expr) -> Option<(Expr, Expr, Expr, Expr)> {
264    match e {
265        Expr::App(f, rhs) => match f.as_ref() {
266            Expr::App(g, lhs) => match g.as_ref() {
267                Expr::App(h, rhs_ty) => match h.as_ref() {
268                    Expr::App(i, lhs_ty) => match i.as_ref() {
269                        Expr::Const(n, _) if n.to_string() == "HEq" => Some((
270                            lhs_ty.as_ref().clone(),
271                            lhs.as_ref().clone(),
272                            rhs_ty.as_ref().clone(),
273                            rhs.as_ref().clone(),
274                        )),
275                        _ => None,
276                    },
277                    _ => None,
278                },
279                _ => None,
280            },
281            _ => None,
282        },
283        _ => None,
284    }
285}
286/// Build an `Eq` expression `@Eq ty lhs rhs`.
287pub fn mk_eq(ty: Expr, lhs: Expr, rhs: Expr) -> Expr {
288    app(app(app(var("Eq"), ty), lhs), rhs)
289}
290/// Build an `HEq` expression `@HEq lhs_ty lhs rhs_ty rhs`.
291pub fn mk_heq(lhs_ty: Expr, lhs: Expr, rhs_ty: Expr, rhs: Expr) -> Expr {
292    app(app(app(app(var("HEq"), lhs_ty), lhs), rhs_ty), rhs)
293}
294/// Register the standard `Eq` and associated declarations into an environment.
295///
296/// This includes `Eq`, `Eq.refl`, `Eq.symm`, `Eq.trans`, `Eq.subst`,
297/// `congrArg`, `congrFun`, and `congr`.
298pub fn build_eq_env(env: &mut EnvBuilder) {
299    env.add_axiom(Name::from_str("Eq"), sort(1));
300    env.add_axiom(
301        Name::from_str("Eq.refl"),
302        pi_implicit(
303            "α",
304            sort(1),
305            pi_named(
306                "a",
307                bvar(0),
308                app(app(app(var("Eq"), bvar(1)), bvar(0)), bvar(0)),
309            ),
310        ),
311    );
312    env.add_axiom(
313        Name::from_str("Eq.symm"),
314        pi_implicit(
315            "α",
316            sort(1),
317            pi_implicit(
318                "a",
319                bvar(0),
320                pi_implicit(
321                    "b",
322                    bvar(1),
323                    pi(
324                        app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
325                        app(app(app(var("Eq"), bvar(3)), bvar(1)), bvar(2)),
326                    ),
327                ),
328            ),
329        ),
330    );
331    env.add_axiom(
332        Name::from_str("Eq.trans"),
333        pi_implicit(
334            "α",
335            sort(1),
336            pi_implicit(
337                "a",
338                bvar(0),
339                pi_implicit(
340                    "b",
341                    bvar(1),
342                    pi_implicit(
343                        "c",
344                        bvar(2),
345                        pi(
346                            app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
347                            pi(
348                                app(app(app(var("Eq"), bvar(4)), bvar(2)), bvar(1)),
349                                app(app(app(var("Eq"), bvar(5)), bvar(4)), bvar(2)),
350                            ),
351                        ),
352                    ),
353                ),
354            ),
355        ),
356    );
357    env.add_axiom(
358        Name::from_str("Eq.subst"),
359        pi_implicit(
360            "α",
361            sort(1),
362            pi_implicit(
363                "motive",
364                pi(bvar(0), prop()),
365                pi_implicit(
366                    "a",
367                    bvar(1),
368                    pi_implicit(
369                        "b",
370                        bvar(2),
371                        pi(
372                            app(app(app(var("Eq"), bvar(3)), bvar(1)), bvar(0)),
373                            pi(app(bvar(3), bvar(2)), app(bvar(4), bvar(2))),
374                        ),
375                    ),
376                ),
377            ),
378        ),
379    );
380    env.add_axiom(
381        Name::from_str("congrArg"),
382        pi_implicit(
383            "α",
384            sort(1),
385            pi_implicit(
386                "β",
387                sort(1),
388                pi_named(
389                    "f",
390                    pi(bvar(1), bvar(1)),
391                    pi_implicit(
392                        "a",
393                        bvar(2),
394                        pi_implicit(
395                            "b",
396                            bvar(3),
397                            pi(
398                                app(app(app(var("Eq"), bvar(4)), bvar(1)), bvar(0)),
399                                app(
400                                    app(app(var("Eq"), bvar(4)), app(bvar(3), bvar(2))),
401                                    app(bvar(3), bvar(1)),
402                                ),
403                            ),
404                        ),
405                    ),
406                ),
407            ),
408        ),
409    );
410    env.add_axiom(
411        Name::from_str("congrFun"),
412        pi_implicit(
413            "α",
414            sort(1),
415            pi_implicit(
416                "β",
417                sort(1),
418                pi_implicit(
419                    "f",
420                    pi(bvar(1), bvar(1)),
421                    pi_implicit(
422                        "g",
423                        pi(bvar(2), bvar(2)),
424                        pi(
425                            app(app(app(var("Eq"), pi(bvar(3), bvar(3))), bvar(1)), bvar(0)),
426                            pi_named(
427                                "a",
428                                bvar(4),
429                                app(
430                                    app(app(var("Eq"), bvar(4)), app(bvar(3), bvar(0))),
431                                    app(bvar(2), bvar(0)),
432                                ),
433                            ),
434                        ),
435                    ),
436                ),
437            ),
438        ),
439    );
440    env.add_axiom(
441        Name::from_str("congr"),
442        pi_implicit(
443            "α",
444            sort(1),
445            pi_implicit(
446                "β",
447                sort(1),
448                pi_implicit(
449                    "f",
450                    pi(bvar(1), bvar(1)),
451                    pi_implicit(
452                        "g",
453                        pi(bvar(2), bvar(2)),
454                        pi(
455                            app(app(app(var("Eq"), pi(bvar(3), bvar(3))), bvar(1)), bvar(0)),
456                            pi_implicit(
457                                "a",
458                                bvar(4),
459                                pi_implicit(
460                                    "b",
461                                    bvar(5),
462                                    pi(
463                                        app(app(app(var("Eq"), bvar(6)), bvar(1)), bvar(0)),
464                                        app(
465                                            app(app(var("Eq"), bvar(6)), app(bvar(5), bvar(2))),
466                                            app(bvar(4), bvar(1)),
467                                        ),
468                                    ),
469                                ),
470                            ),
471                        ),
472                    ),
473                ),
474            ),
475        ),
476    );
477}
478/// Register `Eq.mpr` and related transport lemmas.
479pub fn build_eq_mpr_env(env: &mut EnvBuilder) {
480    env.add_axiom(
481        Name::from_str("Eq.mpr"),
482        pi_implicit(
483            "α",
484            prop(),
485            pi_implicit(
486                "β",
487                prop(),
488                pi(
489                    app(app(app(var("Eq"), prop()), bvar(1)), bvar(0)),
490                    pi(bvar(1), bvar(3)),
491                ),
492            ),
493        ),
494    );
495    env.add_axiom(
496        Name::from_str("Eq.mp"),
497        pi_implicit(
498            "α",
499            prop(),
500            pi_implicit(
501                "β",
502                prop(),
503                pi(
504                    app(app(app(var("Eq"), prop()), bvar(1)), bvar(0)),
505                    pi(bvar(2), bvar(2)),
506                ),
507            ),
508        ),
509    );
510    env.add_axiom(
511        Name::from_str("id.def"),
512        pi_implicit(
513            "α",
514            sort(1),
515            pi_implicit(
516                "a",
517                bvar(0),
518                app(app(app(var("Eq"), bvar(1)), bvar(0)), bvar(0)),
519            ),
520        ),
521    );
522}
523#[cfg(test)]
524mod tests {
525    use super::*;
526    #[test]
527    fn test_prop_eq_refl() {
528        let ty = var("Nat");
529        let a = var("x");
530        let eq = PropEq::refl(ty, a.clone());
531        assert!(eq.is_refl());
532        assert_eq!(eq.lhs, a);
533        assert_eq!(eq.rhs, a);
534    }
535    #[test]
536    fn test_prop_eq_symm() {
537        let ty = var("Nat");
538        let a = var("a");
539        let b = var("b");
540        let eq = PropEq::new(ty.clone(), a.clone(), b.clone());
541        let sym = eq.symm();
542        assert_eq!(sym.lhs, b);
543        assert_eq!(sym.rhs, a);
544    }
545    #[test]
546    fn test_prop_eq_trans() {
547        let ty = var("Nat");
548        let a = var("a");
549        let b = var("b");
550        let c = var("c");
551        let e1 = PropEq::new(ty.clone(), a.clone(), b.clone());
552        let e2 = PropEq::new(ty.clone(), b.clone(), c.clone());
553        let e3 = e1.trans(e2).expect("trans should succeed");
554        assert_eq!(e3.lhs, a);
555        assert_eq!(e3.rhs, c);
556    }
557    #[test]
558    fn test_prop_eq_trans_mismatch() {
559        let ty = var("Nat");
560        let a = var("a");
561        let b = var("b");
562        let c = var("c");
563        let e1 = PropEq::new(ty.clone(), a.clone(), b.clone());
564        let e2 = PropEq::new(ty.clone(), c.clone(), a.clone());
565        assert!(e1.trans(e2).is_none());
566    }
567    #[test]
568    fn test_eq_chain_collapse() {
569        let ty = var("Nat");
570        let a = var("a");
571        let b = var("b");
572        let c = var("c");
573        let mut chain = EqChain::new(ty.clone());
574        chain.push(PropEq::new(ty.clone(), a.clone(), b.clone()));
575        chain.push(PropEq::new(ty.clone(), b.clone(), c.clone()));
576        let collapsed = chain.collapse().expect("collapse should succeed");
577        assert_eq!(collapsed.lhs, a);
578        assert_eq!(collapsed.rhs, c);
579    }
580    #[test]
581    fn test_eq_chain_empty() {
582        let chain = EqChain::new(var("Nat"));
583        assert!(chain.is_empty());
584        assert!(chain.collapse().is_none());
585    }
586    #[test]
587    fn test_decision_result_and() {
588        let a: DecisionResult<()> = DecisionResult::IsTrue(());
589        let b: DecisionResult<()> = DecisionResult::IsTrue(());
590        let ab = a.and(b);
591        assert!(ab.is_true());
592    }
593    #[test]
594    fn test_decision_result_and_false() {
595        let a: DecisionResult<()> = DecisionResult::IsTrue(());
596        let b: DecisionResult<()> = DecisionResult::IsFalse("no".to_string());
597        let ab = a.and(b);
598        assert!(ab.is_false());
599    }
600    #[test]
601    fn test_decision_result_or() {
602        let a: DecisionResult<()> = DecisionResult::IsFalse("no".to_string());
603        let b: DecisionResult<()> = DecisionResult::IsTrue(());
604        let ab = a.or(b);
605        assert!(ab.is_true());
606    }
607    #[test]
608    fn test_equality_database_lookup() {
609        let mut db = EqualityDatabase::new();
610        let a = Name::from_str("a");
611        let b = Name::from_str("b");
612        db.register(a.clone(), b.clone(), var("proof_ab"));
613        let found = db.lookup(&a, &b);
614        assert!(found.is_some());
615    }
616    #[test]
617    fn test_equality_database_symm() {
618        let mut db = EqualityDatabase::new();
619        let a = Name::from_str("a");
620        let b = Name::from_str("b");
621        db.register(a.clone(), b.clone(), var("proof_ab"));
622        let found = db.lookup(&b, &a);
623        assert!(found.is_some());
624    }
625    #[test]
626    fn test_decide_str_eq() {
627        assert!(decide_str_eq("hello", "hello").is_true());
628        assert!(decide_str_eq("hello", "world").is_false());
629    }
630    #[test]
631    fn test_decide_u32_eq() {
632        assert!(decide_u32_eq(42, 42).is_true());
633        assert!(decide_u32_eq(42, 43).is_false());
634    }
635    #[test]
636    fn test_equality_witness() {
637        let w = EqualityWitness::try_new(&42u32, &42u32);
638        assert!(w.is_some());
639        let w = EqualityWitness::try_new(&1u32, &2u32);
640        assert!(w.is_none());
641    }
642    #[test]
643    fn test_exprs_eq() {
644        let a = vec![var("x"), var("y")];
645        let b = vec![var("x"), var("y")];
646        assert!(exprs_eq(&a, &b));
647        let c = vec![var("x"), var("z")];
648        assert!(!exprs_eq(&a, &c));
649    }
650    #[test]
651    fn test_mk_eq_and_as_eq() {
652        let ty = var("Nat");
653        let lhs = var("a");
654        let rhs = var("b");
655        let eq_expr = mk_eq(ty.clone(), lhs.clone(), rhs.clone());
656        let parsed = as_eq(&eq_expr);
657        assert!(parsed.is_some());
658        let (t, l, r) = parsed.expect("parsed should be valid");
659        assert_eq!(t, ty);
660        assert_eq!(l, lhs);
661        assert_eq!(r, rhs);
662    }
663    #[test]
664    fn test_equality_database_len() {
665        let mut db = EqualityDatabase::new();
666        assert_eq!(db.len(), 0);
667        assert!(db.is_empty());
668        db.register(Name::from_str("a"), Name::from_str("b"), var("p"));
669        assert_eq!(db.len(), 1);
670        assert!(!db.is_empty());
671    }
672    #[test]
673    fn test_eq_chain_len() {
674        let ty = var("Nat");
675        let a = var("a");
676        let b = var("b");
677        let c = var("c");
678        let mut chain = EqChain::new(ty.clone());
679        chain.push(PropEq::new(ty.clone(), a.clone(), b.clone()));
680        chain.push(PropEq::new(ty.clone(), b.clone(), c.clone()));
681        assert_eq!(chain.len(), 2);
682    }
683}
684/// Check pairwise equality of two vectors of expressions.
685pub fn exprs_eq_pairwise(a: &[oxilean_kernel::Expr], b: &[oxilean_kernel::Expr]) -> Vec<bool> {
686    if a.len() != b.len() {
687        return vec![];
688    }
689    a.iter().zip(b.iter()).map(|(x, y)| x == y).collect()
690}
691/// Count the number of positions where two expression slices differ.
692pub fn count_diffs(a: &[oxilean_kernel::Expr], b: &[oxilean_kernel::Expr]) -> usize {
693    a.iter().zip(b.iter()).filter(|(x, y)| x != y).count()
694}
695/// Check if two expression slices are equal up to a permutation.
696///
697/// This is O(n²) and is only suitable for small slices.
698pub fn exprs_eq_mod_permutation(a: &[oxilean_kernel::Expr], b: &[oxilean_kernel::Expr]) -> bool {
699    if a.len() != b.len() {
700        return false;
701    }
702    let mut used = vec![false; b.len()];
703    'outer: for ea in a {
704        for (j, eb) in b.iter().enumerate() {
705            if !used[j] && ea == eb {
706                used[j] = true;
707                continue 'outer;
708            }
709        }
710        return false;
711    }
712    true
713}
714/// Extensional equality: two functions are equal if they agree on all inputs.
715///
716/// At the Rust meta level, this is implemented via a finite test set.
717pub fn extensionally_equal<A, B: PartialEq>(
718    f: impl Fn(&A) -> B,
719    g: impl Fn(&A) -> B,
720    test_points: &[A],
721) -> bool {
722    test_points.iter().all(|x| f(x) == g(x))
723}
724/// Leibniz equality witness: if `a = b` and `P a` holds, then `P b` holds.
725///
726/// This function demonstrates the Leibniz substitution principle at the
727/// meta level by applying the predicate to both sides.
728pub fn leibniz_subst<T: PartialEq, P>(_a: &T, _b: &T, witness: &EqualityWitness<T>, pa: P) -> P {
729    let _ = witness;
730    pa
731}
732/// Check if an expression is a reflexivity application `@Eq.refl α a`.
733///
734/// Returns `Some(a)` if so, `None` otherwise.
735pub fn is_refl_proof(e: &oxilean_kernel::Expr) -> Option<oxilean_kernel::Expr> {
736    match e {
737        oxilean_kernel::Expr::App(f, a) => match f.as_ref() {
738            oxilean_kernel::Expr::App(g, _alpha) => match g.as_ref() {
739                oxilean_kernel::Expr::Const(n, _) if n.to_string() == "Eq.refl" => {
740                    Some(a.as_ref().clone())
741                }
742                _ => None,
743            },
744            _ => None,
745        },
746        _ => None,
747    }
748}
749#[cfg(test)]
750mod eq_extended_tests {
751    use super::*;
752    fn v(s: &str) -> oxilean_kernel::Expr {
753        var(s)
754    }
755    fn n(s: &str) -> oxilean_kernel::Name {
756        oxilean_kernel::Name::from_str(s)
757    }
758    #[test]
759    fn test_exprs_eq_pairwise_same() {
760        let a = vec![v("x"), v("y")];
761        let b = vec![v("x"), v("y")];
762        let result = exprs_eq_pairwise(&a, &b);
763        assert!(result.iter().all(|&b| b));
764    }
765    #[test]
766    fn test_exprs_eq_pairwise_diff() {
767        let a = vec![v("x"), v("y")];
768        let b = vec![v("x"), v("z")];
769        let result = exprs_eq_pairwise(&a, &b);
770        assert!(result[0]);
771        assert!(!result[1]);
772    }
773    #[test]
774    fn test_exprs_eq_pairwise_length_mismatch() {
775        let result = exprs_eq_pairwise(&[v("x")], &[v("x"), v("y")]);
776        assert!(result.is_empty());
777    }
778    #[test]
779    fn test_count_diffs() {
780        let a = vec![v("x"), v("y"), v("z")];
781        let b = vec![v("x"), v("a"), v("z")];
782        assert_eq!(count_diffs(&a, &b), 1);
783    }
784    #[test]
785    fn test_exprs_eq_mod_permutation_same() {
786        let a = vec![v("x"), v("y")];
787        let b = vec![v("y"), v("x")];
788        assert!(exprs_eq_mod_permutation(&a, &b));
789    }
790    #[test]
791    fn test_exprs_eq_mod_permutation_diff() {
792        let a = vec![v("x"), v("y")];
793        let b = vec![v("x"), v("z")];
794        assert!(!exprs_eq_mod_permutation(&a, &b));
795    }
796    #[test]
797    fn test_eq_builder_empty() {
798        let b = EqBuilder::start(v("Nat"), v("a"));
799        assert_eq!(b.num_steps(), 0);
800        assert!(b.build().is_none());
801    }
802    #[test]
803    fn test_eq_builder_one_step() {
804        let builder = EqBuilder::start(v("Nat"), v("a")).step(v("b"), v("proof_ab"));
805        let eq = builder.build();
806        assert!(eq.is_some());
807    }
808    #[test]
809    fn test_extensionally_equal() {
810        let f = |x: &u32| x * 2;
811        let g = |x: &u32| x + x;
812        let pts = [0u32, 1, 2, 3, 4];
813        assert!(extensionally_equal(f, g, &pts));
814    }
815    #[test]
816    fn test_extensionally_not_equal() {
817        let f = |x: &u32| x + 1;
818        let g = |x: &u32| x * 2;
819        let pts = [0u32, 1, 2];
820        assert!(!extensionally_equal(f, g, &pts));
821    }
822    #[test]
823    fn test_leibniz_subst() {
824        let w = EqualityWitness { value: 42u32 };
825        let pa = "result";
826        let result = leibniz_subst(&42u32, &42u32, &w, pa);
827        assert_eq!(result, "result");
828    }
829    #[test]
830    fn test_eq_rewrite_rule_apply() {
831        let rule = EqRewriteRule::new(n("r"), v("a"), v("b"));
832        assert!(rule.matches(&v("a")));
833        assert_eq!(rule.apply(&v("a")), Some(v("b")));
834        assert!(rule.apply(&v("c")).is_none());
835    }
836    #[test]
837    fn test_eq_rewrite_rule_reversed() {
838        let rule = EqRewriteRule::new(n("r"), v("a"), v("b")).make_reversible();
839        let rev = rule.reversed().expect("reversed should succeed");
840        assert_eq!(rev.lhs, v("b"));
841        assert_eq!(rev.rhs, v("a"));
842    }
843    #[test]
844    fn test_eq_rewrite_rule_not_reversible() {
845        let rule = EqRewriteRule::new(n("r"), v("a"), v("b"));
846        assert!(rule.reversed().is_none());
847    }
848    #[test]
849    fn test_rewrite_rule_db_add_find() {
850        let mut db = RewriteRuleDb::new();
851        db.add(EqRewriteRule::new(n("r1"), v("x"), v("y")));
852        let found = db.find_match(&v("x"));
853        assert!(found.is_some());
854        assert_eq!(found.expect("found should be valid").rhs, v("y"));
855    }
856    #[test]
857    fn test_rewrite_rule_db_apply_all() {
858        let mut db = RewriteRuleDb::new();
859        db.add(EqRewriteRule::new(n("r1"), v("x"), v("y")));
860        db.add(EqRewriteRule::new(n("r2"), v("x"), v("z")));
861        let results = db.apply_all(&v("x"));
862        assert_eq!(results.len(), 2);
863    }
864    #[test]
865    fn test_rewrite_rule_db_remove() {
866        let mut db = RewriteRuleDb::new();
867        db.add(EqRewriteRule::new(n("r1"), v("x"), v("y")));
868        db.remove(&n("r1"));
869        assert!(db.is_empty());
870    }
871    #[test]
872    fn test_is_refl_proof_none() {
873        let e = v("not_refl");
874        assert!(is_refl_proof(&e).is_none());
875    }
876}
877pub fn eq_ext_app(f: Expr, a: Expr) -> Expr {
878    Expr::App(Node::new(f), Node::new(a))
879}
880pub fn eq_ext_app2(f: Expr, a: Expr, b: Expr) -> Expr {
881    eq_ext_app(eq_ext_app(f, a), b)
882}
883pub fn eq_ext_cst(s: &str) -> Expr {
884    Expr::Const(Name::str(s), vec![])
885}
886pub fn eq_ext_prop() -> Expr {
887    Expr::Sort(Level::zero())
888}
889pub fn eq_ext_type0() -> Expr {
890    Expr::Sort(Level::succ(Level::zero()))
891}
892pub fn eq_ext_bvar(n: u32) -> Expr {
893    Expr::BVar(n)
894}
895pub fn eq_ext_nat_ty() -> Expr {
896    eq_ext_cst("Nat")
897}
898pub fn eq_ext_bool_ty() -> Expr {
899    eq_ext_cst("Bool")
900}
901pub fn eq_ext_arrow(dom: Expr, cod: Expr) -> Expr {
902    Expr::Pi(
903        oxilean_kernel::BinderInfo::Default,
904        Name::Anonymous,
905        Node::new(dom),
906        Node::new(cod),
907    )
908}
909/// Build the type of the Eq-class reflexivity law:
910/// `∀ {α : Type} (a : α), a = a`.
911pub fn mk_eq_class_refl_ty() -> Expr {
912    pi_implicit(
913        "α",
914        sort(1),
915        pi_named(
916            "a",
917            bvar(0),
918            app(app(app(var("Eq"), bvar(1)), bvar(0)), bvar(0)),
919        ),
920    )
921}
922/// Build the type of the Eq-class symmetry law:
923/// `∀ {α : Type} {a b : α}, a = b → b = a`.
924pub fn mk_eq_class_symm_ty() -> Expr {
925    pi_implicit(
926        "α",
927        sort(1),
928        pi_implicit(
929            "a",
930            bvar(0),
931            pi_implicit(
932                "b",
933                bvar(1),
934                pi(
935                    app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
936                    app(app(app(var("Eq"), bvar(3)), bvar(1)), bvar(2)),
937                ),
938            ),
939        ),
940    )
941}
942/// Build the type of the Eq-class transitivity law:
943/// `∀ {α : Type} {a b c : α}, a = b → b = c → a = c`.
944pub fn mk_eq_class_trans_ty() -> Expr {
945    pi_implicit(
946        "α",
947        sort(1),
948        pi_implicit(
949            "a",
950            bvar(0),
951            pi_implicit(
952                "b",
953                bvar(1),
954                pi_implicit(
955                    "c",
956                    bvar(2),
957                    pi(
958                        app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
959                        pi(
960                            app(app(app(var("Eq"), bvar(4)), bvar(2)), bvar(1)),
961                            app(app(app(var("Eq"), bvar(5)), bvar(4)), bvar(2)),
962                        ),
963                    ),
964                ),
965            ),
966        ),
967    )
968}
969/// Build the type of the BEq/Eq consistency axiom:
970/// `∀ {α : Type} {a b : α}, (BEq.beq a b = true) → a = b`.
971pub fn mk_beq_eq_consistency_ty() -> Expr {
972    pi_implicit(
973        "α",
974        sort(1),
975        pi_implicit(
976            "a",
977            bvar(0),
978            pi_implicit(
979                "b",
980                bvar(1),
981                pi(
982                    app(
983                        app(
984                            app(var("Eq"), eq_ext_bool_ty()),
985                            app(app(var("BEq.beq"), bvar(1)), bvar(0)),
986                        ),
987                        var("Bool.true"),
988                    ),
989                    app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
990                ),
991            ),
992        ),
993    )
994}
995/// Build the type of the Nat equality decidability axiom:
996/// `∀ (a b : Nat), Decidable (a = b)`.
997pub fn mk_nat_decidable_eq_ty() -> Expr {
998    pi_named(
999        "a",
1000        eq_ext_nat_ty(),
1001        pi_named(
1002            "b",
1003            eq_ext_nat_ty(),
1004            app(
1005                var("Decidable"),
1006                app(app(app(var("Eq"), eq_ext_nat_ty()), bvar(1)), bvar(0)),
1007            ),
1008        ),
1009    )
1010}
1011/// Build the type of the Bool equality decidability axiom.
1012pub fn mk_bool_decidable_eq_ty() -> Expr {
1013    pi_named(
1014        "a",
1015        eq_ext_bool_ty(),
1016        pi_named(
1017            "b",
1018            eq_ext_bool_ty(),
1019            app(
1020                var("Decidable"),
1021                app(app(app(var("Eq"), eq_ext_bool_ty()), bvar(1)), bvar(0)),
1022            ),
1023        ),
1024    )
1025}
1026/// Build the type of the Char decidable equality axiom.
1027pub fn mk_char_decidable_eq_ty() -> Expr {
1028    pi_named(
1029        "a",
1030        eq_ext_cst("Char"),
1031        pi_named(
1032            "b",
1033            eq_ext_cst("Char"),
1034            app(
1035                var("Decidable"),
1036                app(app(app(var("Eq"), eq_ext_cst("Char")), bvar(1)), bvar(0)),
1037            ),
1038        ),
1039    )
1040}
1041/// Build the type of the Float equality axiom.
1042pub fn mk_float_eq_decidable_ty() -> Expr {
1043    pi_named(
1044        "a",
1045        eq_ext_cst("Float"),
1046        pi_named(
1047            "b",
1048            eq_ext_cst("Float"),
1049            app(
1050                var("Decidable"),
1051                app(app(app(var("Eq"), eq_ext_cst("Float")), bvar(1)), bvar(0)),
1052            ),
1053        ),
1054    )
1055}
1056/// Build the type of the Int decidable equality axiom.
1057pub fn mk_int_decidable_eq_ty() -> Expr {
1058    pi_named(
1059        "a",
1060        eq_ext_cst("Int"),
1061        pi_named(
1062            "b",
1063            eq_ext_cst("Int"),
1064            app(
1065                var("Decidable"),
1066                app(app(app(var("Eq"), eq_ext_cst("Int")), bvar(1)), bvar(0)),
1067            ),
1068        ),
1069    )
1070}
1071/// Build the type of the List equality axiom:
1072/// `∀ {α : Type} (xs ys : List α), Decidable (xs = ys)`.
1073pub fn mk_list_decidable_eq_ty() -> Expr {
1074    pi_implicit(
1075        "α",
1076        sort(1),
1077        pi_named(
1078            "xs",
1079            app(var("List"), bvar(0)),
1080            pi_named(
1081                "ys",
1082                app(var("List"), bvar(1)),
1083                app(
1084                    var("Decidable"),
1085                    app(
1086                        app(app(var("Eq"), app(var("List"), bvar(2))), bvar(1)),
1087                        bvar(0),
1088                    ),
1089                ),
1090            ),
1091        ),
1092    )
1093}
1094/// Build the type of the Option decidable equality axiom.
1095pub fn mk_option_decidable_eq_ty() -> Expr {
1096    pi_implicit(
1097        "α",
1098        sort(1),
1099        pi_named(
1100            "x",
1101            app(var("Option"), bvar(0)),
1102            pi_named(
1103                "y",
1104                app(var("Option"), bvar(1)),
1105                app(
1106                    var("Decidable"),
1107                    app(
1108                        app(app(var("Eq"), app(var("Option"), bvar(2))), bvar(1)),
1109                        bvar(0),
1110                    ),
1111                ),
1112            ),
1113        ),
1114    )
1115}
1116/// Build the type of the Pair (Prod) decidable equality axiom.
1117pub fn mk_pair_decidable_eq_ty() -> Expr {
1118    pi_implicit(
1119        "α",
1120        sort(1),
1121        pi_implicit(
1122            "β",
1123            sort(1),
1124            pi_named(
1125                "p",
1126                app(app(var("Prod"), bvar(1)), bvar(0)),
1127                pi_named(
1128                    "q",
1129                    app(app(var("Prod"), bvar(2)), bvar(1)),
1130                    app(
1131                        var("Decidable"),
1132                        app(
1133                            app(
1134                                app(var("Eq"), app(app(var("Prod"), bvar(3)), bvar(2))),
1135                                bvar(1),
1136                            ),
1137                            bvar(0),
1138                        ),
1139                    ),
1140                ),
1141            ),
1142        ),
1143    )
1144}
1145/// Build the type of the Leibniz equality axiom:
1146/// `∀ {α : Type} (a b : α), (∀ (P : α → Prop), P a → P b) → a = b`.
1147pub fn mk_leibniz_eq_ty() -> Expr {
1148    pi_implicit(
1149        "α",
1150        sort(1),
1151        pi_named(
1152            "a",
1153            bvar(0),
1154            pi_named(
1155                "b",
1156                bvar(1),
1157                pi(
1158                    pi_named(
1159                        "P",
1160                        pi(bvar(2), eq_ext_prop()),
1161                        pi(app(bvar(0), bvar(2)), app(bvar(1), bvar(2))),
1162                    ),
1163                    app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
1164                ),
1165            ),
1166        ),
1167    )
1168}
1169/// Build the type of the Leibniz substitution direction:
1170/// `∀ {α : Type} {a b : α}, a = b → ∀ (P : α → Prop), P a → P b`.
1171pub fn mk_leibniz_subst_ty() -> Expr {
1172    pi_implicit(
1173        "α",
1174        sort(1),
1175        pi_implicit(
1176            "a",
1177            bvar(0),
1178            pi_implicit(
1179                "b",
1180                bvar(1),
1181                pi(
1182                    app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
1183                    pi_named(
1184                        "P",
1185                        pi(bvar(3), eq_ext_prop()),
1186                        pi(app(bvar(0), bvar(3)), app(bvar(1), bvar(2))),
1187                    ),
1188                ),
1189            ),
1190        ),
1191    )
1192}
1193/// Build the type of the equality reflection axiom:
1194/// Same as Leibniz substitution: `∀ {α} {a b : α}, a = b → ∀ P, P a → P b`.
1195pub fn mk_eq_reflection_ty() -> Expr {
1196    mk_leibniz_subst_ty()
1197}
1198/// Build the type of Streicher's K axiom:
1199/// `∀ {α : Type} {a : α} (p : a = a), p = Eq.refl a`.
1200pub fn mk_k_axiom_ty() -> Expr {
1201    pi_implicit(
1202        "α",
1203        sort(1),
1204        pi_implicit(
1205            "a",
1206            bvar(0),
1207            pi_named(
1208                "p",
1209                app(app(app(var("Eq"), bvar(1)), bvar(0)), bvar(0)),
1210                app(
1211                    app(
1212                        app(
1213                            var("Eq"),
1214                            app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(1)),
1215                        ),
1216                        bvar(0),
1217                    ),
1218                    app(app(var("Eq.refl"), bvar(2)), bvar(1)),
1219                ),
1220            ),
1221        ),
1222    )
1223}
1224/// Build the type of the UIP axiom:
1225/// `∀ {α : Type} {a b : α} (p q : a = b), p = q`.
1226pub fn mk_uip_ty() -> Expr {
1227    pi_implicit(
1228        "α",
1229        sort(1),
1230        pi_implicit(
1231            "a",
1232            bvar(0),
1233            pi_implicit(
1234                "b",
1235                bvar(1),
1236                pi_named(
1237                    "p",
1238                    app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
1239                    pi_named(
1240                        "q",
1241                        app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
1242                        app(
1243                            app(
1244                                app(
1245                                    var("Eq"),
1246                                    app(app(app(var("Eq"), bvar(4)), bvar(3)), bvar(2)),
1247                                ),
1248                                bvar(1),
1249                            ),
1250                            bvar(0),
1251                        ),
1252                    ),
1253                ),
1254            ),
1255        ),
1256    )
1257}
1258/// Build the type of the J axiom (Martin-Löf path induction).
1259pub fn mk_j_axiom_ty() -> Expr {
1260    pi_implicit(
1261        "α",
1262        sort(1),
1263        pi_implicit(
1264            "a",
1265            bvar(0),
1266            pi_named(
1267                "P",
1268                pi_named(
1269                    "b",
1270                    bvar(1),
1271                    pi(
1272                        app(app(app(var("Eq"), bvar(2)), bvar(2)), bvar(0)),
1273                        eq_ext_prop(),
1274                    ),
1275                ),
1276                pi(
1277                    app(
1278                        app(bvar(0), bvar(1)),
1279                        app(app(var("Eq.refl"), bvar(2)), bvar(1)),
1280                    ),
1281                    pi_implicit(
1282                        "b",
1283                        bvar(3),
1284                        pi_named(
1285                            "h",
1286                            app(app(app(var("Eq"), bvar(4)), bvar(4)), bvar(0)),
1287                            app(app(bvar(3), bvar(1)), bvar(0)),
1288                        ),
1289                    ),
1290                ),
1291            ),
1292        ),
1293    )
1294}
1295/// Build the type of the HEq introduction rule:
1296/// `∀ {α : Type} (a : α), HEq a a`.
1297pub fn mk_heq_intro_ty() -> Expr {
1298    pi_implicit(
1299        "α",
1300        sort(1),
1301        pi_named(
1302            "a",
1303            bvar(0),
1304            app(
1305                app(app(app(var("HEq"), bvar(1)), bvar(0)), bvar(1)),
1306                bvar(0),
1307            ),
1308        ),
1309    )
1310}
1311/// Build the type of HEq type-equality consequence:
1312/// `∀ {α β : Type} {a : α} {b : β}, HEq a b → α = β`.
1313pub fn mk_heq_type_eq_ty() -> Expr {
1314    pi_implicit(
1315        "α",
1316        sort(1),
1317        pi_implicit(
1318            "β",
1319            sort(1),
1320            pi_implicit(
1321                "a",
1322                bvar(1),
1323                pi_implicit(
1324                    "b",
1325                    bvar(1),
1326                    pi(
1327                        app(
1328                            app(app(app(var("HEq"), bvar(3)), bvar(1)), bvar(2)),
1329                            bvar(0),
1330                        ),
1331                        app(app(app(var("Eq"), sort(1)), bvar(4)), bvar(3)),
1332                    ),
1333                ),
1334            ),
1335        ),
1336    )
1337}
1338/// Build the type of the general substitution axiom:
1339/// `∀ {α : Type} {a b : α} (P : α → Type), a = b → P a → P b`.
1340pub fn mk_subst_axiom_ty() -> Expr {
1341    pi_implicit(
1342        "α",
1343        sort(1),
1344        pi_implicit(
1345            "a",
1346            bvar(0),
1347            pi_implicit(
1348                "b",
1349                bvar(1),
1350                pi_named(
1351                    "P",
1352                    pi(bvar(2), sort(1)),
1353                    pi(
1354                        app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
1355                        pi(app(bvar(1), bvar(3)), app(bvar(2), bvar(3))),
1356                    ),
1357                ),
1358            ),
1359        ),
1360    )
1361}
1362/// Build the type of the general congruence axiom:
1363/// `∀ {α β : Type} (f : α → β) {a b : α}, a = b → f a = f b`.
1364pub fn mk_cong_axiom_ty() -> Expr {
1365    pi_implicit(
1366        "α",
1367        sort(1),
1368        pi_implicit(
1369            "β",
1370            sort(1),
1371            pi_named(
1372                "f",
1373                pi(bvar(1), bvar(1)),
1374                pi_implicit(
1375                    "a",
1376                    bvar(2),
1377                    pi_implicit(
1378                        "b",
1379                        bvar(3),
1380                        pi(
1381                            app(app(app(var("Eq"), bvar(4)), bvar(1)), bvar(0)),
1382                            app(
1383                                app(app(var("Eq"), bvar(4)), app(bvar(2), bvar(2))),
1384                                app(bvar(2), bvar(1)),
1385                            ),
1386                        ),
1387                    ),
1388                ),
1389            ),
1390        ),
1391    )
1392}
1393/// Build the type of the functional extensionality axiom:
1394/// `∀ {α β : Type} (f g : α → β), (∀ x, f x = g x) → f = g`.
1395pub fn mk_funext_ty() -> Expr {
1396    pi_implicit(
1397        "α",
1398        sort(1),
1399        pi_implicit(
1400            "β",
1401            sort(1),
1402            pi_named(
1403                "f",
1404                pi(bvar(1), bvar(1)),
1405                pi_named(
1406                    "g",
1407                    pi(bvar(2), bvar(2)),
1408                    pi(
1409                        pi_named(
1410                            "x",
1411                            bvar(3),
1412                            app(
1413                                app(app(var("Eq"), bvar(3)), app(bvar(2), bvar(0))),
1414                                app(bvar(1), bvar(0)),
1415                            ),
1416                        ),
1417                        app(app(app(var("Eq"), pi(bvar(4), bvar(4))), bvar(2)), bvar(1)),
1418                    ),
1419                ),
1420            ),
1421        ),
1422    )
1423}
1424/// Build the type of propositional extensionality:
1425/// `∀ (P Q : Prop), (P ↔ Q) → P = Q`.
1426pub fn mk_propext_ty() -> Expr {
1427    pi_named(
1428        "P",
1429        eq_ext_prop(),
1430        pi_named(
1431            "Q",
1432            eq_ext_prop(),
1433            pi(
1434                app(app(var("And"), pi(bvar(1), bvar(1))), pi(bvar(1), bvar(2))),
1435                app(app(app(var("Eq"), eq_ext_prop()), bvar(2)), bvar(1)),
1436            ),
1437        ),
1438    )
1439}
1440/// Build the type of the quotient soundness axiom.
1441pub fn mk_quotient_sound_ty() -> Expr {
1442    pi_implicit(
1443        "α",
1444        sort(1),
1445        pi_named(
1446            "r",
1447            pi(bvar(0), pi(bvar(1), eq_ext_prop())),
1448            pi_named(
1449                "a",
1450                bvar(1),
1451                pi_named(
1452                    "b",
1453                    bvar(2),
1454                    pi(
1455                        app(app(bvar(2), bvar(1)), bvar(0)),
1456                        app(
1457                            app(
1458                                app(var("Eq"), app(var("Quotient"), bvar(4))),
1459                                app(app(var("Quotient.mk"), bvar(4)), bvar(2)),
1460                            ),
1461                            app(app(var("Quotient.mk"), bvar(5)), bvar(1)),
1462                        ),
1463                    ),
1464                ),
1465            ),
1466        ),
1467    )
1468}
1469/// Build the type of the bisimulation-equality axiom.
1470pub fn mk_bisim_eq_ty() -> Expr {
1471    pi_implicit(
1472        "α",
1473        sort(1),
1474        pi_named(
1475            "R",
1476            pi(bvar(0), pi(bvar(1), eq_ext_prop())),
1477            pi(
1478                app(var("Bisimulation"), bvar(0)),
1479                pi_named(
1480                    "a",
1481                    bvar(2),
1482                    pi_named(
1483                        "b",
1484                        bvar(3),
1485                        pi(
1486                            app(app(bvar(3), bvar(1)), bvar(0)),
1487                            app(app(app(var("Eq"), bvar(5)), bvar(2)), bvar(1)),
1488                        ),
1489                    ),
1490                ),
1491            ),
1492        ),
1493    )
1494}
1495/// Build the type of the observational equality axiom.
1496pub fn mk_obs_eq_ty() -> Expr {
1497    pi_implicit(
1498        "α",
1499        sort(1),
1500        pi_named(
1501            "a",
1502            bvar(0),
1503            pi_named(
1504                "b",
1505                bvar(1),
1506                pi(
1507                    pi_named(
1508                        "P",
1509                        pi(bvar(2), eq_ext_prop()),
1510                        app(
1511                            app(var("And"), pi(app(bvar(0), bvar(2)), app(bvar(1), bvar(2)))),
1512                            pi(app(bvar(1), bvar(2)), app(bvar(0), bvar(3))),
1513                        ),
1514                    ),
1515                    app(app(app(var("Eq"), bvar(3)), bvar(2)), bvar(1)),
1516                ),
1517            ),
1518        ),
1519    )
1520}
1521/// Build the type of the setoid construction axiom.
1522pub fn mk_setoid_ax_ty() -> Expr {
1523    pi_named(
1524        "α",
1525        sort(1),
1526        pi_named(
1527            "r",
1528            pi(bvar(0), pi(bvar(1), eq_ext_prop())),
1529            pi(
1530                app(var("IsEquivalence"), bvar(0)),
1531                app(var("Setoid"), bvar(2)),
1532            ),
1533        ),
1534    )
1535}
1536/// Build the type of the setoid morphism axiom.
1537pub fn mk_setoid_morphism_ax_ty() -> Expr {
1538    pi_implicit(
1539        "α",
1540        sort(1),
1541        pi_implicit(
1542            "β",
1543            sort(1),
1544            pi_named(
1545                "f",
1546                pi(bvar(1), bvar(1)),
1547                pi(
1548                    app(var("Respects"), bvar(0)),
1549                    app(var("SetoidMorphism"), bvar(1)),
1550                ),
1551            ),
1552        ),
1553    )
1554}
1555/// Build the type of the groupoid path concatenation (alias for trans).
1556pub fn mk_path_concat_ty() -> Expr {
1557    mk_eq_class_trans_ty()
1558}
1559/// Build the type of the groupoid path inversion (alias for symm).
1560pub fn mk_path_inv_ty() -> Expr {
1561    mk_eq_class_symm_ty()
1562}
1563/// Build the type of the definitional equality (reflexivity in MLTT).
1564pub fn mk_def_eq_mltt_ty() -> Expr {
1565    pi_implicit(
1566        "α",
1567        sort(1),
1568        pi_named(
1569            "a",
1570            bvar(0),
1571            app(app(app(var("Eq"), bvar(1)), bvar(0)), bvar(0)),
1572        ),
1573    )
1574}
1575/// Build the type of the general DecidableEq instance.
1576pub fn mk_decidable_eq_instance_ty() -> Expr {
1577    pi_implicit(
1578        "α",
1579        sort(1),
1580        pi_named(
1581            "a",
1582            bvar(0),
1583            pi_named(
1584                "b",
1585                bvar(1),
1586                app(
1587                    var("Decidable"),
1588                    app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
1589                ),
1590            ),
1591        ),
1592    )
1593}
1594/// Build the type of the homotopy equivalence axiom.
1595pub fn mk_homotopy_equiv_ty() -> Expr {
1596    pi_implicit(
1597        "α",
1598        sort(1),
1599        pi_implicit(
1600            "β",
1601            sort(1),
1602            pi(
1603                app(app(var("HomotopyEquiv"), bvar(1)), bvar(0)),
1604                app(var("Nonempty"), app(app(var("Equiv"), bvar(2)), bvar(1))),
1605            ),
1606        ),
1607    )
1608}
1609/// Build the type of the congruence closure axiom.
1610pub fn mk_cong_closure_ty() -> Expr {
1611    pi_implicit(
1612        "α",
1613        sort(1),
1614        pi_named(
1615            "f",
1616            pi(bvar(0), bvar(0)),
1617            pi_named(
1618                "a",
1619                bvar(1),
1620                pi_named(
1621                    "b",
1622                    bvar(2),
1623                    pi(
1624                        app(app(app(var("Eq"), bvar(3)), bvar(1)), bvar(0)),
1625                        app(
1626                            app(app(var("Eq"), bvar(4)), app(bvar(2), bvar(2))),
1627                            app(bvar(2), bvar(1)),
1628                        ),
1629                    ),
1630                ),
1631            ),
1632        ),
1633    )
1634}
1635/// Build the type of the Subsingleton equality axiom.
1636pub fn mk_subsingleton_eq_ty() -> Expr {
1637    pi_implicit(
1638        "α",
1639        sort(1),
1640        pi_named(
1641            "a",
1642            bvar(0),
1643            pi_named(
1644                "b",
1645                bvar(1),
1646                app(app(app(var("Eq"), bvar(2)), bvar(1)), bvar(0)),
1647            ),
1648        ),
1649    )
1650}
1651/// Build the type of the Sigma equality (fst projection equality).
1652pub fn mk_sigma_eq_ty() -> Expr {
1653    pi_implicit(
1654        "α",
1655        sort(1),
1656        pi_named(
1657            "s",
1658            app(var("Sigma"), bvar(0)),
1659            pi_named(
1660                "t",
1661                app(var("Sigma"), bvar(1)),
1662                pi(
1663                    app(
1664                        app(app(var("Eq"), app(var("Sigma"), bvar(2))), bvar(1)),
1665                        bvar(0),
1666                    ),
1667                    app(
1668                        app(app(var("Eq"), bvar(3)), app(var("Sigma.fst"), bvar(2))),
1669                        app(var("Sigma.fst"), bvar(1)),
1670                    ),
1671                ),
1672            ),
1673        ),
1674    )
1675}
1676/// Build the type of the Subtype equality axiom.
1677pub fn mk_subtype_eq_ty() -> Expr {
1678    pi_implicit(
1679        "α",
1680        sort(1),
1681        pi_named(
1682            "s",
1683            app(var("Subtype"), bvar(0)),
1684            pi_named(
1685                "t",
1686                app(var("Subtype"), bvar(1)),
1687                pi(
1688                    app(
1689                        app(app(var("Eq"), bvar(2)), app(var("Subtype.val"), bvar(1))),
1690                        app(var("Subtype.val"), bvar(0)),
1691                    ),
1692                    app(
1693                        app(app(var("Eq"), app(var("Subtype"), bvar(3))), bvar(2)),
1694                        bvar(1),
1695                    ),
1696                ),
1697            ),
1698        ),
1699    )
1700}
1701/// Build the type of the function equality pointwise axiom.
1702pub fn mk_fun_eq_pointwise_ty() -> Expr {
1703    pi_implicit(
1704        "α",
1705        sort(1),
1706        pi_implicit(
1707            "β",
1708            sort(1),
1709            pi_implicit(
1710                "f",
1711                pi(bvar(1), bvar(1)),
1712                pi_implicit(
1713                    "g",
1714                    pi(bvar(2), bvar(2)),
1715                    pi(
1716                        app(app(app(var("Eq"), pi(bvar(3), bvar(3))), bvar(1)), bvar(0)),
1717                        pi_named(
1718                            "x",
1719                            bvar(4),
1720                            app(
1721                                app(app(var("Eq"), bvar(4)), app(bvar(3), bvar(0))),
1722                                app(bvar(2), bvar(0)),
1723                            ),
1724                        ),
1725                    ),
1726                ),
1727            ),
1728        ),
1729    )
1730}
1731/// Build the type of the Either/Sum decidable equality axiom.
1732pub fn mk_either_decidable_eq_ty() -> Expr {
1733    pi_implicit(
1734        "α",
1735        sort(1),
1736        pi_implicit(
1737            "β",
1738            sort(1),
1739            pi_named(
1740                "x",
1741                app(app(var("Sum"), bvar(1)), bvar(0)),
1742                pi_named(
1743                    "y",
1744                    app(app(var("Sum"), bvar(2)), bvar(1)),
1745                    app(
1746                        var("Decidable"),
1747                        app(
1748                            app(
1749                                app(var("Eq"), app(app(var("Sum"), bvar(3)), bvar(2))),
1750                                bvar(1),
1751                            ),
1752                            bvar(0),
1753                        ),
1754                    ),
1755                ),
1756            ),
1757        ),
1758    )
1759}
1760/// Build the type of the Result (Ok/Err) decidable equality axiom.
1761pub fn mk_result_decidable_eq_ty() -> Expr {
1762    pi_implicit(
1763        "α",
1764        sort(1),
1765        pi_implicit(
1766            "ε",
1767            sort(1),
1768            pi_named(
1769                "x",
1770                app(app(var("Except"), bvar(1)), bvar(0)),
1771                pi_named(
1772                    "y",
1773                    app(app(var("Except"), bvar(2)), bvar(1)),
1774                    app(
1775                        var("Decidable"),
1776                        app(
1777                            app(
1778                                app(var("Eq"), app(app(var("Except"), bvar(3)), bvar(2))),
1779                                bvar(1),
1780                            ),
1781                            bvar(0),
1782                        ),
1783                    ),
1784                ),
1785            ),
1786        ),
1787    )
1788}
1789/// Build the type of `Eq.ndrec` (no-dependency recursor).
1790pub fn mk_eq_ndrec_ty() -> Expr {
1791    pi_implicit(
1792        "α",
1793        sort(1),
1794        pi_implicit(
1795            "a",
1796            bvar(0),
1797            pi_named(
1798                "P",
1799                pi(bvar(1), sort(1)),
1800                pi_named(
1801                    "ha",
1802                    app(bvar(0), bvar(1)),
1803                    pi_implicit(
1804                        "b",
1805                        bvar(3),
1806                        pi(
1807                            app(app(app(var("Eq"), bvar(4)), bvar(3)), bvar(0)),
1808                            app(bvar(2), bvar(1)),
1809                        ),
1810                    ),
1811                ),
1812            ),
1813        ),
1814    )
1815}