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