Skip to main content

logicaffeine_kernel/
prelude.rs

1//! Standard Library for the Kernel.
2//!
3//! Defines fundamental types and logical connectives:
4//! - Entity: domain of individuals (for FOL)
5//! - Nat: natural numbers
6//! - True, False: propositional constants
7//! - Eq: propositional equality
8//! - And, Or: logical connectives
9
10use crate::context::Context;
11use crate::term::{Literal, Term, Universe};
12
13/// Standard library definitions.
14pub struct StandardLibrary;
15
16impl StandardLibrary {
17    /// Register all standard library definitions in the context.
18    pub fn register(ctx: &mut Context) {
19        Self::register_entity(ctx);
20        Self::register_nat(ctx);
21        Self::register_monolist(ctx);
22        Self::register_bool(ctx);
23        Self::register_tlist(ctx);
24        Self::register_true(ctx);
25        Self::register_false(ctx);
26        Self::register_not(ctx);
27        Self::register_classical(ctx);
28        Self::register_eq(ctx);
29        Self::register_and(ctx);
30        Self::register_or(ctx);
31        Self::register_ex(ctx);
32        Self::register_decidable(ctx);
33        Self::register_of_decide(ctx);
34        Self::register_dec_eq_bool(ctx);
35        Self::register_dec_eq_nat(ctx);
36        Self::register_native_decide(ctx);
37        Self::register_quot(ctx);
38        Self::register_acc(ctx);
39        Self::register_primitives(ctx);
40        Self::register_int_ring_axioms(ctx);
41        Self::register_int_order_axioms(ctx);
42        Self::register_reflection(ctx);
43        Self::register_hardware(ctx);
44    }
45
46    /// Classical logic: double-negation elimination `dne : Π(P:Prop). ¬¬P → P`,
47    /// i.e. `Π(P:Prop). ((P → False) → False) → P`. This makes the logic CLASSICAL —
48    /// exactly as Lean/Coq's Mathlib are classical via `Classical.em`/`choice` — which
49    /// ordinary mathematics (and proof by contradiction: Euclid I.6/I.7/I.27 …)
50    /// requires. It is an EXPLICIT, type-checked axiom in the trusted base, recorded
51    /// in the certificate's axiom-set version; the de Bruijn criterion is preserved
52    /// (every classical proof still elaborates to a kernel `Term` re-checked here).
53    fn register_classical(ctx: &mut Context) {
54        let false_t = || Term::Global("False".to_string());
55        let p = || Term::Var("P".to_string());
56        // ¬P = P → False
57        let not_p = Term::Pi {
58            param: "_".to_string(),
59            param_type: Box::new(p()),
60            body_type: Box::new(false_t()),
61        };
62        // ¬¬P = (P → False) → False
63        let not_not_p = Term::Pi {
64            param: "_".to_string(),
65            param_type: Box::new(not_p),
66            body_type: Box::new(false_t()),
67        };
68        // dne : Π(P:Prop). ¬¬P → P
69        let dne_type = Term::Pi {
70            param: "P".to_string(),
71            param_type: Box::new(Term::Sort(Universe::Prop)),
72            body_type: Box::new(Term::Pi {
73                param: "_".to_string(),
74                param_type: Box::new(not_not_p),
75                body_type: Box::new(p()),
76            }),
77        };
78        ctx.add_declaration("dne", dne_type);
79    }
80
81    /// The commutative-ring axioms for `Int` — the ENTIRE trusted arithmetic base.
82    ///
83    /// `Int` is an opaque type (no constructors, `register_primitives`), so these
84    /// standard ring laws are not derivable in-kernel; they are the axiomatization
85    /// of `(Int, add, mul, 0, 1)` as a commutative ring — the textbook foundation,
86    /// not a soundness shortcut. The proof-producing arithmetic procedure builds
87    /// every arithmetic proof from these (closed/literal goals need none — they
88    /// hold by `add`/`mul` computation + `refl`). They are the *only* arithmetic
89    /// things trusted beyond the kernel; `phase98`'s TCB-inventory test locks the
90    /// exact set so it can never silently grow. Replaceable later (without any
91    /// downstream churn) by defining `Int` from `Nat` and proving them.
92    fn register_int_ring_axioms(ctx: &mut Context) {
93        let int = || Term::Global("Int".to_string());
94        let lit = |n: i64| Term::Lit(Literal::Int(n));
95        // (op a b)
96        let bin = |op: &str, a: Term, b: Term| {
97            Term::App(
98                Box::new(Term::App(Box::new(Term::Global(op.to_string())), Box::new(a))),
99                Box::new(b),
100            )
101        };
102        // Eq Int l r
103        let eq_int = |l: Term, r: Term| {
104            Term::App(
105                Box::new(Term::App(
106                    Box::new(Term::App(
107                        Box::new(Term::Global("Eq".to_string())),
108                        Box::new(Term::Global("Int".to_string())),
109                    )),
110                    Box::new(l),
111                )),
112                Box::new(r),
113            )
114        };
115        // Π over the given Int-typed parameter names, body = eq.
116        let forall_ints = |names: &[&str], body: Term| {
117            names.iter().rev().fold(body, |acc, name| Term::Pi {
118                param: name.to_string(),
119                param_type: Box::new(int()),
120                body_type: Box::new(acc),
121            })
122        };
123        let v = |s: &str| Term::Var(s.to_string());
124
125        // add_comm : Π a b. Eq Int (add a b) (add b a)
126        ctx.add_declaration(
127            "add_comm",
128            forall_ints(
129                &["a", "b"],
130                eq_int(bin("add", v("a"), v("b")), bin("add", v("b"), v("a"))),
131            ),
132        );
133        // add_assoc : Π a b c. Eq Int (add (add a b) c) (add a (add b c))
134        ctx.add_declaration(
135            "add_assoc",
136            forall_ints(
137                &["a", "b", "c"],
138                eq_int(
139                    bin("add", bin("add", v("a"), v("b")), v("c")),
140                    bin("add", v("a"), bin("add", v("b"), v("c"))),
141                ),
142            ),
143        );
144        // add_zero : Π a. Eq Int (add a 0) a
145        ctx.add_declaration(
146            "add_zero",
147            forall_ints(&["a"], eq_int(bin("add", v("a"), lit(0)), v("a"))),
148        );
149        // mul_comm : Π a b. Eq Int (mul a b) (mul b a)
150        ctx.add_declaration(
151            "mul_comm",
152            forall_ints(
153                &["a", "b"],
154                eq_int(bin("mul", v("a"), v("b")), bin("mul", v("b"), v("a"))),
155            ),
156        );
157        // mul_assoc : Π a b c. Eq Int (mul (mul a b) c) (mul a (mul b c))
158        ctx.add_declaration(
159            "mul_assoc",
160            forall_ints(
161                &["a", "b", "c"],
162                eq_int(
163                    bin("mul", bin("mul", v("a"), v("b")), v("c")),
164                    bin("mul", v("a"), bin("mul", v("b"), v("c"))),
165                ),
166            ),
167        );
168        // mul_one : Π a. Eq Int (mul a 1) a
169        ctx.add_declaration(
170            "mul_one",
171            forall_ints(&["a"], eq_int(bin("mul", v("a"), lit(1)), v("a"))),
172        );
173        // mul_zero : Π a. Eq Int (mul a 0) 0. Not derivable from the others without
174        // an additive-inverse axiom; needed so the normalizer can drop a monomial
175        // whose coefficient cancels to zero (e.g. in a Farkas combination).
176        ctx.add_declaration(
177            "mul_zero",
178            forall_ints(&["a"], eq_int(bin("mul", v("a"), lit(0)), lit(0))),
179        );
180        // mul_distrib_add : Π a b c. Eq Int (mul a (add b c)) (add (mul a b) (mul a c))
181        ctx.add_declaration(
182            "mul_distrib_add",
183            forall_ints(
184                &["a", "b", "c"],
185                eq_int(
186                    bin("mul", v("a"), bin("add", v("b"), v("c"))),
187                    bin("add", bin("mul", v("a"), v("b")), bin("mul", v("a"), v("c"))),
188                ),
189            ),
190        );
191    }
192
193    /// The standard order axioms for `Int`, completing the ordered-ring trusted
194    /// base. `Int` is opaque, so just as the ring laws are axioms here (Coq `Int63`
195    /// / Lean primitives), the order laws are too: reflexivity, transitivity,
196    /// monotone addition, and scaling by a non-negative factor — the primitives a
197    /// Farkas linear-arithmetic certificate is reconstructed from. `m ≤ n` is the
198    /// shallow `Eq Bool (le m n) true` (decidable by computation on literals, see
199    /// `reduction.rs`); these axioms extend it to the symbolic case. Replaceable
200    /// later (no downstream churn) by defining `≤` from `Nat` and proving them.
201    /// Locked by `phase98`'s TCB inventory.
202    fn register_int_order_axioms(ctx: &mut Context) {
203        let int = || Term::Global("Int".to_string());
204        let v = |s: &str| Term::Var(s.to_string());
205        let g = |s: &str| Term::Global(s.to_string());
206        let lit = |n: i64| Term::Lit(Literal::Int(n));
207        let bin = |op: &str, a: Term, b: Term| {
208            Term::App(
209                Box::new(Term::App(Box::new(Term::Global(op.to_string())), Box::new(a))),
210                Box::new(b),
211            )
212        };
213        // le_prop a b = Eq Bool (le a b) true  — the shallow `a ≤ b`.
214        let le_prop = |a: Term, b: Term| {
215            Term::App(
216                Box::new(Term::App(
217                    Box::new(Term::App(Box::new(g("Eq")), Box::new(g("Bool")))),
218                    Box::new(bin("le", a, b)),
219                )),
220                Box::new(g("true")),
221            )
222        };
223        let arrow = |dom: Term, cod: Term| Term::Pi {
224            param: "_".to_string(),
225            param_type: Box::new(dom),
226            body_type: Box::new(cod),
227        };
228        let forall_ints = |names: &[&str], body: Term| {
229            names.iter().rev().fold(body, |acc, name| Term::Pi {
230                param: name.to_string(),
231                param_type: Box::new(int()),
232                body_type: Box::new(acc),
233            })
234        };
235
236        // le_refl : Π a. a ≤ a
237        ctx.add_declaration("le_refl", forall_ints(&["a"], le_prop(v("a"), v("a"))));
238        // le_trans : Π a b c. a ≤ b → b ≤ c → a ≤ c
239        ctx.add_declaration(
240            "le_trans",
241            forall_ints(
242                &["a", "b", "c"],
243                arrow(
244                    le_prop(v("a"), v("b")),
245                    arrow(le_prop(v("b"), v("c")), le_prop(v("a"), v("c"))),
246                ),
247            ),
248        );
249        // le_add_mono : Π a b c d. a ≤ b → c ≤ d → (a + c) ≤ (b + d)
250        ctx.add_declaration(
251            "le_add_mono",
252            forall_ints(
253                &["a", "b", "c", "d"],
254                arrow(
255                    le_prop(v("a"), v("b")),
256                    arrow(
257                        le_prop(v("c"), v("d")),
258                        le_prop(bin("add", v("a"), v("c")), bin("add", v("b"), v("d"))),
259                    ),
260                ),
261            ),
262        );
263        // le_mul_nonneg : Π k a b. 0 ≤ k → a ≤ b → (k * a) ≤ (k * b)
264        ctx.add_declaration(
265            "le_mul_nonneg",
266            forall_ints(
267                &["k", "a", "b"],
268                arrow(
269                    le_prop(lit(0), v("k")),
270                    arrow(
271                        le_prop(v("a"), v("b")),
272                        le_prop(bin("mul", v("k"), v("a")), bin("mul", v("k"), v("b"))),
273                    ),
274                ),
275            ),
276        );
277        // le_sub : Π a b. a ≤ b → 0 ≤ b + (-1)·a   (the Farkas "move to one side").
278        // Written with add/mul (not sub) so the ring oracle, whose axioms are over
279        // add/mul, can prove the combined-term equalities during reconstruction.
280        ctx.add_declaration(
281            "le_sub",
282            forall_ints(
283                &["a", "b"],
284                arrow(
285                    le_prop(v("a"), v("b")),
286                    le_prop(lit(0), bin("add", v("b"), bin("mul", lit(-1), v("a")))),
287                ),
288            ),
289        );
290
291        // lt_prop a b = Eq Bool (lt a b) true  — the shallow `a < b`.
292        let lt_prop = |a: Term, b: Term| {
293            Term::App(
294                Box::new(Term::App(
295                    Box::new(Term::App(Box::new(g("Eq")), Box::new(g("Bool")))),
296                    Box::new(bin("lt", a, b)),
297                )),
298                Box::new(g("true")),
299            )
300        };
301        // lt_succ_le : Π a b. a < b → (a + 1) ≤ b.  Integer DISCRETENESS — the fact
302        // rational Fourier-Motzkin lacks: over ℤ a strict `<` is a `≤` shifted by one,
303        // so nothing lives strictly between `a` and `a+1`. This single axiom is what
304        // lets `omega` refute strict systems the rational solver reports satisfiable
305        // (`x < y ∧ y < x+1`), and it is discharged by ordinary Farkas afterward.
306        ctx.add_declaration(
307            "lt_succ_le",
308            forall_ints(
309                &["a", "b"],
310                arrow(lt_prop(v("a"), v("b")), le_prop(bin("add", v("a"), lit(1)), v("b"))),
311            ),
312        );
313        // lt_add1_le : Π a b. a < (b + 1) → a ≤ b.  The upper-side companion of
314        // `lt_succ_le` (`a < b+1 ⟺ a ≤ b` over ℤ): when the strict bound already has
315        // the shape `b + 1`, this yields the CANCELLED `a ≤ b` directly instead of the
316        // constant-laden `a + 1 ≤ b + 1`, keeping the reconstructed Farkas terms small.
317        ctx.add_declaration(
318            "lt_add1_le",
319            forall_ints(
320                &["a", "b"],
321                arrow(lt_prop(v("a"), bin("add", v("b"), lit(1))), le_prop(v("a"), v("b"))),
322            ),
323        );
324        // le_total : Π a b. (a ≤ b) ∨ (b ≤ a).  Linear order totality — the case-split
325        // seam for disequality (`a ≠ b` ⟹ split `a < b ∨ b < a`) and for reducing a
326        // positive `≤` goal to a refutation of its negation.
327        let or = |p: Term, q: Term| {
328            Term::App(
329                Box::new(Term::App(Box::new(g("Or")), Box::new(p))),
330                Box::new(q),
331            )
332        };
333        ctx.add_declaration(
334            "le_total",
335            forall_ints(
336                &["a", "b"],
337                or(le_prop(v("a"), v("b")), le_prop(v("b"), v("a"))),
338            ),
339        );
340    }
341
342    /// Primitive types and operations.
343    ///
344    /// Int : Type 0 (64-bit signed integer)
345    /// Float : Type 0 (64-bit floating point)
346    /// Text : Type 0 (UTF-8 string)
347    /// Duration : Type 0 (nanoseconds, i64 - physical time)
348    /// Date : Type 0 (days since epoch, i32 - calendar date)
349    /// Moment : Type 0 (nanoseconds since epoch, i64 - instant in UTC)
350    ///
351    /// add, sub, mul, div, mod : Int -> Int -> Int
352    fn register_primitives(ctx: &mut Context) {
353        // Opaque types (no constructors, cannot be pattern-matched)
354        ctx.add_inductive("Int", Term::Sort(Universe::Type(0)));
355        ctx.add_inductive("Float", Term::Sort(Universe::Type(0)));
356        ctx.add_inductive("Text", Term::Sort(Universe::Type(0)));
357
358        // Temporal types (opaque, no constructors)
359        ctx.add_inductive("Duration", Term::Sort(Universe::Type(0)));
360        ctx.add_inductive("Date", Term::Sort(Universe::Type(0)));
361        ctx.add_inductive("Moment", Term::Sort(Universe::Type(0)));
362
363        let int = Term::Global("Int".to_string());
364
365        // Binary Int -> Int -> Int type
366        let bin_int_type = Term::Pi {
367            param: "_".to_string(),
368            param_type: Box::new(int.clone()),
369            body_type: Box::new(Term::Pi {
370                param: "_".to_string(),
371                param_type: Box::new(int.clone()),
372                body_type: Box::new(int.clone()),
373            }),
374        };
375
376        // Register arithmetic builtins as declarations (axioms with computational behavior)
377        ctx.add_declaration("add", bin_int_type.clone());
378        ctx.add_declaration("sub", bin_int_type.clone());
379        ctx.add_declaration("mul", bin_int_type.clone());
380        ctx.add_declaration("div", bin_int_type.clone());
381        ctx.add_declaration("mod", bin_int_type);
382
383        // Comparison builtins: Int -> Int -> Bool, decided by computation on
384        // literals (see `reduction.rs`). `Eq Bool (le m n) true` is the shallow
385        // encoding of `m ≤ n`, provable by `refl` exactly when it holds.
386        let bin_int_bool_type = Term::Pi {
387            param: "_".to_string(),
388            param_type: Box::new(int.clone()),
389            body_type: Box::new(Term::Pi {
390                param: "_".to_string(),
391                param_type: Box::new(int.clone()),
392                body_type: Box::new(Term::Global("Bool".to_string())),
393            }),
394        };
395        ctx.add_declaration("le", bin_int_bool_type.clone());
396        ctx.add_declaration("lt", bin_int_bool_type.clone());
397        ctx.add_declaration("ge", bin_int_bool_type.clone());
398        ctx.add_declaration("gt", bin_int_bool_type);
399
400        // Temporal operations
401        let duration = Term::Global("Duration".to_string());
402        let date = Term::Global("Date".to_string());
403        let moment = Term::Global("Moment".to_string());
404        let bool_type = Term::Global("Bool".to_string());
405
406        // Duration -> Duration -> Duration
407        let bin_duration_type = Term::Pi {
408            param: "_".to_string(),
409            param_type: Box::new(duration.clone()),
410            body_type: Box::new(Term::Pi {
411                param: "_".to_string(),
412                param_type: Box::new(duration.clone()),
413                body_type: Box::new(duration.clone()),
414            }),
415        };
416
417        // Duration -> Int -> Duration
418        let duration_int_duration_type = Term::Pi {
419            param: "_".to_string(),
420            param_type: Box::new(duration.clone()),
421            body_type: Box::new(Term::Pi {
422                param: "_".to_string(),
423                param_type: Box::new(int.clone()),
424                body_type: Box::new(duration.clone()),
425            }),
426        };
427
428        // Duration arithmetic: add, sub, mul, div
429        ctx.add_declaration("add_duration", bin_duration_type.clone());
430        ctx.add_declaration("sub_duration", bin_duration_type.clone());
431        ctx.add_declaration("mul_duration", duration_int_duration_type.clone());
432        ctx.add_declaration("div_duration", duration_int_duration_type);
433
434        // Date -> Int -> Date (add days offset)
435        let date_int_date_type = Term::Pi {
436            param: "_".to_string(),
437            param_type: Box::new(date.clone()),
438            body_type: Box::new(Term::Pi {
439                param: "_".to_string(),
440                param_type: Box::new(int.clone()),
441                body_type: Box::new(date.clone()),
442            }),
443        };
444        ctx.add_declaration("date_add_days", date_int_date_type);
445
446        // Date -> Date -> Int (difference in days)
447        let date_date_int_type = Term::Pi {
448            param: "_".to_string(),
449            param_type: Box::new(date.clone()),
450            body_type: Box::new(Term::Pi {
451                param: "_".to_string(),
452                param_type: Box::new(date.clone()),
453                body_type: Box::new(int.clone()),
454            }),
455        };
456        ctx.add_declaration("date_sub_date", date_date_int_type);
457
458        // Moment -> Duration -> Moment
459        let moment_duration_moment_type = Term::Pi {
460            param: "_".to_string(),
461            param_type: Box::new(moment.clone()),
462            body_type: Box::new(Term::Pi {
463                param: "_".to_string(),
464                param_type: Box::new(duration.clone()),
465                body_type: Box::new(moment.clone()),
466            }),
467        };
468        ctx.add_declaration("moment_add_duration", moment_duration_moment_type);
469
470        // Moment -> Moment -> Duration
471        let moment_moment_duration_type = Term::Pi {
472            param: "_".to_string(),
473            param_type: Box::new(moment.clone()),
474            body_type: Box::new(Term::Pi {
475                param: "_".to_string(),
476                param_type: Box::new(moment.clone()),
477                body_type: Box::new(duration.clone()),
478            }),
479        };
480        ctx.add_declaration("moment_sub_moment", moment_moment_duration_type);
481
482        // Comparison operations: X -> X -> Bool
483        let date_date_bool_type = Term::Pi {
484            param: "_".to_string(),
485            param_type: Box::new(date.clone()),
486            body_type: Box::new(Term::Pi {
487                param: "_".to_string(),
488                param_type: Box::new(date),
489                body_type: Box::new(bool_type.clone()),
490            }),
491        };
492        ctx.add_declaration("date_lt", date_date_bool_type);
493
494        let moment_moment_bool_type = Term::Pi {
495            param: "_".to_string(),
496            param_type: Box::new(moment.clone()),
497            body_type: Box::new(Term::Pi {
498                param: "_".to_string(),
499                param_type: Box::new(moment),
500                body_type: Box::new(bool_type.clone()),
501            }),
502        };
503        ctx.add_declaration("moment_lt", moment_moment_bool_type);
504
505        let duration_duration_bool_type = Term::Pi {
506            param: "_".to_string(),
507            param_type: Box::new(duration.clone()),
508            body_type: Box::new(Term::Pi {
509                param: "_".to_string(),
510                param_type: Box::new(duration),
511                body_type: Box::new(bool_type),
512            }),
513        };
514        ctx.add_declaration("duration_lt", duration_duration_bool_type);
515    }
516
517    /// Entity : Type 0
518    ///
519    /// The domain of individuals for first-order logic.
520    /// Proper names like "Socrates" are declared as Entity.
521    /// Predicates like "Man" are declared as Entity → Prop.
522    fn register_entity(ctx: &mut Context) {
523        ctx.add_inductive("Entity", Term::Sort(Universe::Type(0)));
524    }
525
526    /// Nat : Type 0
527    /// Zero : Nat
528    /// Succ : Nat → Nat
529    fn register_nat(ctx: &mut Context) {
530        let nat = Term::Global("Nat".to_string());
531
532        // Nat : Type 0
533        ctx.add_inductive("Nat", Term::Sort(Universe::Type(0)));
534
535        // Zero : Nat
536        ctx.add_constructor("Zero", "Nat", nat.clone());
537
538        // Succ : Nat → Nat
539        ctx.add_constructor(
540            "Succ",
541            "Nat",
542            Term::Pi {
543                param: "_".to_string(),
544                param_type: Box::new(nat.clone()),
545                body_type: Box::new(nat),
546            },
547        );
548    }
549
550    /// Monomorphic `EList` (a list of `Entity`) — the concrete inductive that
551    /// structural induction runs over. The kernel's parametric `TList A` carries a
552    /// type argument, so its constructors are `Π(A:Type)…`; the generic
553    /// `InductionScheme` eliminator builds a `match` over a *bare* `Global(ind_type)`,
554    /// which needs a non-parametric type. `EList = μX. ENil | ECons Entity X` is that
555    /// target: it gives the `induction` tactic an `ENil`/`ECons` recursor that
556    /// certifies — `fix rec. λl:EList. match l { ENil => …, ECons h t => … }`. The
557    /// `E`-prefixed names deliberately avoid the user-space `List`/`Nil`/`Cons` a REPL
558    /// program defines (typically a parametric `List A`), so registering this in the
559    /// prelude never shadows a user inductive.
560    ///
561    /// `EList : Type 0`
562    /// `ENil  : EList`
563    /// `ECons : Entity → EList → EList`
564    fn register_monolist(ctx: &mut Context) {
565        let list = Term::Global("EList".to_string());
566        let entity = Term::Global("Entity".to_string());
567
568        // EList : Type 0
569        ctx.add_inductive("EList", Term::Sort(Universe::Type(0)));
570
571        // ENil : EList
572        ctx.add_constructor("ENil", "EList", list.clone());
573
574        // ECons : Entity → EList → EList
575        ctx.add_constructor(
576            "ECons",
577            "EList",
578            Term::Pi {
579                param: "_".to_string(),
580                param_type: Box::new(entity),
581                body_type: Box::new(Term::Pi {
582                    param: "_".to_string(),
583                    param_type: Box::new(list.clone()),
584                    body_type: Box::new(list),
585                }),
586            },
587        );
588    }
589
590    /// Bool : Type 0
591    /// true : Bool
592    /// false : Bool
593    fn register_bool(ctx: &mut Context) {
594        let bool_type = Term::Global("Bool".to_string());
595
596        // Bool : Type 0
597        ctx.add_inductive("Bool", Term::Sort(Universe::Type(0)));
598
599        // true : Bool
600        ctx.add_constructor("true", "Bool", bool_type.clone());
601
602        // false : Bool
603        ctx.add_constructor("false", "Bool", bool_type);
604    }
605
606    /// TList : Type 0 -> Type 0
607    /// TNil : Π(A : Type 0). TList A
608    /// TCons : Π(A : Type 0). A -> TList A -> TList A
609    fn register_tlist(ctx: &mut Context) {
610        let type0 = Term::Sort(Universe::Type(0));
611        let a = Term::Var("A".to_string());
612
613        // TList : Type 0 -> Type 0
614        let tlist_type = Term::Pi {
615            param: "A".to_string(),
616            param_type: Box::new(type0.clone()),
617            body_type: Box::new(type0.clone()),
618        };
619        ctx.add_inductive("TList", tlist_type);
620
621        // TList A
622        let tlist_a = Term::App(
623            Box::new(Term::Global("TList".to_string())),
624            Box::new(a.clone()),
625        );
626
627        // TNil : Π(A : Type 0). TList A
628        let tnil_type = Term::Pi {
629            param: "A".to_string(),
630            param_type: Box::new(type0.clone()),
631            body_type: Box::new(tlist_a.clone()),
632        };
633        ctx.add_constructor("TNil", "TList", tnil_type);
634
635        // TCons : Π(A : Type 0). A -> TList A -> TList A
636        let tcons_type = Term::Pi {
637            param: "A".to_string(),
638            param_type: Box::new(type0),
639            body_type: Box::new(Term::Pi {
640                param: "_".to_string(),
641                param_type: Box::new(a.clone()),
642                body_type: Box::new(Term::Pi {
643                    param: "_".to_string(),
644                    param_type: Box::new(tlist_a.clone()),
645                    body_type: Box::new(tlist_a),
646                }),
647            }),
648        };
649        ctx.add_constructor("TCons", "TList", tcons_type);
650
651        // Convenience aliases for tactic lists (Syntax -> Derivation)
652        Self::register_tactic_list_helpers(ctx);
653    }
654
655    /// TTactics : Type 0 = TList (Syntax -> Derivation)
656    /// TacNil : TTactics
657    /// TacCons : (Syntax -> Derivation) -> TTactics -> TTactics
658    ///
659    /// Convenience wrappers to avoid explicit type arguments for tactic lists.
660    fn register_tactic_list_helpers(ctx: &mut Context) {
661        let syntax = Term::Global("Syntax".to_string());
662        let derivation = Term::Global("Derivation".to_string());
663
664        // Tactic type: Syntax -> Derivation
665        let tactic_type = Term::Pi {
666            param: "_".to_string(),
667            param_type: Box::new(syntax.clone()),
668            body_type: Box::new(derivation.clone()),
669        };
670
671        // TTactics = TList (Syntax -> Derivation)
672        let ttactics = Term::App(
673            Box::new(Term::Global("TList".to_string())),
674            Box::new(tactic_type.clone()),
675        );
676
677        // TTactics : Type 0
678        ctx.add_definition("TTactics".to_string(), Term::Sort(Universe::Type(0)), ttactics.clone());
679
680        // TacNil : TTactics = TNil (Syntax -> Derivation)
681        let tac_nil_body = Term::App(
682            Box::new(Term::Global("TNil".to_string())),
683            Box::new(tactic_type.clone()),
684        );
685        ctx.add_definition("TacNil".to_string(), ttactics.clone(), tac_nil_body);
686
687        // TacCons : (Syntax -> Derivation) -> TTactics -> TTactics
688        let tac_cons_type = Term::Pi {
689            param: "t".to_string(),
690            param_type: Box::new(tactic_type.clone()),
691            body_type: Box::new(Term::Pi {
692                param: "ts".to_string(),
693                param_type: Box::new(ttactics.clone()),
694                body_type: Box::new(ttactics.clone()),
695            }),
696        };
697
698        // TacCons t ts = TCons (Syntax -> Derivation) t ts
699        let tac_cons_body = Term::Lambda {
700            param: "t".to_string(),
701            param_type: Box::new(tactic_type.clone()),
702            body: Box::new(Term::Lambda {
703                param: "ts".to_string(),
704                param_type: Box::new(ttactics.clone()),
705                body: Box::new(Term::App(
706                    Box::new(Term::App(
707                        Box::new(Term::App(
708                            Box::new(Term::Global("TCons".to_string())),
709                            Box::new(tactic_type),
710                        )),
711                        Box::new(Term::Var("t".to_string())),
712                    )),
713                    Box::new(Term::Var("ts".to_string())),
714                )),
715            }),
716        };
717        ctx.add_definition("TacCons".to_string(), tac_cons_type, tac_cons_body);
718    }
719
720    /// True : Prop
721    /// I : True
722    fn register_true(ctx: &mut Context) {
723        ctx.add_inductive("True", Term::Sort(Universe::Prop));
724        ctx.add_constructor("I", "True", Term::Global("True".to_string()));
725    }
726
727    /// False : Prop
728    /// (no constructors)
729    fn register_false(ctx: &mut Context) {
730        ctx.add_inductive("False", Term::Sort(Universe::Prop));
731    }
732
733    /// Not : Prop -> Prop
734    /// Not P := P -> False
735    fn register_not(ctx: &mut Context) {
736        // Type: Π(P : Prop). Prop
737        let not_type = Term::Pi {
738            param: "P".to_string(),
739            param_type: Box::new(Term::Sort(Universe::Prop)),
740            body_type: Box::new(Term::Sort(Universe::Prop)),
741        };
742
743        // Body: λ(P : Prop). Π(_ : P). False
744        let not_body = Term::Lambda {
745            param: "P".to_string(),
746            param_type: Box::new(Term::Sort(Universe::Prop)),
747            body: Box::new(Term::Pi {
748                param: "_".to_string(),
749                param_type: Box::new(Term::Var("P".to_string())),
750                body_type: Box::new(Term::Global("False".to_string())),
751            }),
752        };
753
754        ctx.add_definition("Not".to_string(), not_type, not_body);
755    }
756
757    /// `Decidable (p:Prop) : Type` — the data of a decision procedure for `p`, with
758    /// `isTrue : p → Decidable p` and `isFalse : ¬p → Decidable p`. Type-valued (so it may
759    /// be eliminated into `Type` to compute a `Bool`), auto-derives its recursor, and
760    /// defines `decide : Π(p). Decidable p → Bool` (isTrue ↝ true, isFalse ↝ false).
761    fn register_decidable(ctx: &mut Context) {
762        let prop = || Term::Sort(Universe::Prop);
763        let type0 = || Term::Sort(Universe::Type(0));
764        let g = |s: &str| Term::Global(s.to_string());
765        let v = |s: &str| Term::Var(s.to_string());
766        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
767        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
768            param: p.to_string(),
769            param_type: Box::new(t),
770            body: Box::new(b),
771        };
772        let pi = |p: &str, t: Term, b: Term| Term::Pi {
773            param: p.to_string(),
774            param_type: Box::new(t),
775            body_type: Box::new(b),
776        };
777        let dec = |p: Term| ap(g("Decidable"), p);
778        let not = |p: Term| ap(g("Not"), p);
779
780        // Decidable : Π(p:Prop). Type 0   (one uniform parameter, Type-valued)
781        ctx.add_indexed_inductive("Decidable", pi("p", prop(), type0()), 1);
782        // isTrue  : Π(p:Prop). p → Decidable p
783        ctx.add_constructor("isTrue", "Decidable", pi("p", prop(), pi("_", v("p"), dec(v("p")))));
784        // isFalse : Π(p:Prop). Not p → Decidable p
785        ctx.add_constructor(
786            "isFalse",
787            "Decidable",
788            pi("p", prop(), pi("_", not(v("p")), dec(v("p")))),
789        );
790
791        // Decidable_rec — auto-derived dependent eliminator, kernel-checked (not an axiom).
792        let (rec_ty, rec_body) = crate::recursor::derive_recursor(ctx, "Decidable")
793            .expect("Decidable's eliminator must derive");
794        ctx.add_definition("Decidable_rec".to_string(), rec_ty, rec_body);
795
796        // decide : Π(p:Prop). Decidable p → Bool
797        //   := λp inst. Decidable_rec p (λ_. Bool) (λh. true) (λh. false) inst
798        let decide_ty = pi("p", prop(), pi("_", dec(v("p")), g("Bool")));
799        let decide_body = lm(
800            "p",
801            prop(),
802            lm(
803                "inst",
804                dec(v("p")),
805                ap(
806                    ap(
807                        ap(
808                            ap(ap(g("Decidable_rec"), v("p")), lm("_", dec(v("p")), g("Bool"))),
809                            lm("_", v("p"), g("true")),
810                        ),
811                        lm("_", not(v("p")), g("false")),
812                    ),
813                    v("inst"),
814                ),
815            ),
816        );
817        ctx.add_definition("decide".to_string(), decide_ty, decide_body);
818    }
819
820    /// `of_decide_eq_true : Π(p:Prop). Π(inst:Decidable p). Eq Bool (decide p inst) true → p`
821    /// — the bridge that turns a computed `decide` into a proof, PROVEN from `Decidable_rec`
822    /// and Bool no-confusion (via `Eq_rec_dep`), NOT axiomatized. This is what makes the
823    /// `decide` tactic sound with zero additions to the trusted base.
824    fn register_of_decide(ctx: &mut Context) {
825        let prop = || Term::Sort(Universe::Prop);
826        let g = |s: &str| Term::Global(s.to_string());
827        let v = |s: &str| Term::Var(s.to_string());
828        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
829        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
830            param: p.to_string(),
831            param_type: Box::new(t),
832            body: Box::new(b),
833        };
834        let pi = |p: &str, t: Term, b: Term| Term::Pi {
835            param: p.to_string(),
836            param_type: Box::new(t),
837            body_type: Box::new(b),
838        };
839        let dec = |p: Term| ap(g("Decidable"), p);
840        let not = |p: Term| ap(g("Not"), p);
841        // Eq Bool x y
842        let eqb = |x: Term, y: Term| ap(ap(ap(g("Eq"), g("Bool")), x), y);
843        // decide p inst
844        let decide = |p: Term, inst: Term| ap(ap(g("decide"), p), inst);
845        let is_true = |p: Term, h: Term| ap(ap(g("isTrue"), p), h);
846        let is_false = |p: Term, h: Term| ap(ap(g("isFalse"), p), h);
847
848        // of_decide_eq_true : Π(p:Prop). Π(inst:Decidable p). Eq Bool (decide p inst) true → p
849        let ty = pi(
850            "p",
851            prop(),
852            pi(
853                "inst",
854                dec(v("p")),
855                pi("_", eqb(decide(v("p"), v("inst")), g("true")), v("p")),
856            ),
857        );
858
859        // Motive for the recursion on `inst`: λinst. Eq Bool (decide p inst) true → p.
860        let motive = lm(
861            "inst",
862            dec(v("p")),
863            pi("_", eqb(decide(v("p"), v("inst")), g("true")), v("p")),
864        );
865        // isTrue branch: `decide p (isTrue p hp) ≡ true`, so just return the witness `hp`.
866        let f_istrue = lm(
867            "hp",
868            v("p"),
869            lm(
870                "_",
871                eqb(decide(v("p"), is_true(v("p"), v("hp"))), g("true")),
872                v("hp"),
873            ),
874        );
875        // isFalse branch: `decide p (isFalse p hnp) ≡ false`, so the hypothesis is
876        // `false = true`; Bool no-confusion turns it into `p`. Transport `discr` along the
877        // equality, where `discr b := match b with true => p | false => True`, taking the
878        // inhabitant `I : True` at `false` to a proof of `p` at `true`.
879        let discr = Term::Match {
880            discriminant: Box::new(v("b")),
881            motive: Box::new(lm("_", g("Bool"), prop())),
882            cases: vec![v("p"), g("True")], // Bool constructor order: [true, false]
883        };
884        let bool_motive = lm("b", g("Bool"), lm("_", eqb(g("false"), v("b")), discr));
885        // Eq_rec_dep Bool false bool_motive I true h  :  p
886        let noconf = ap(
887            ap(
888                ap(ap(ap(ap(g("Eq_rec_dep"), g("Bool")), g("false")), bool_motive), g("I")),
889                g("true"),
890            ),
891            v("h"),
892        );
893        let f_isfalse = lm(
894            "hnp",
895            not(v("p")),
896            lm(
897                "h",
898                eqb(decide(v("p"), is_false(v("p"), v("hnp"))), g("true")),
899                noconf,
900            ),
901        );
902
903        // λp inst h. Decidable_rec p motive f_istrue f_isfalse inst h
904        let rec_app = ap(
905            ap(
906                ap(ap(ap(ap(g("Decidable_rec"), v("p")), motive), f_istrue), f_isfalse),
907                v("inst"),
908            ),
909            v("h"),
910        );
911        let body = lm(
912            "p",
913            prop(),
914            lm(
915                "inst",
916                dec(v("p")),
917                lm("h", eqb(decide(v("p"), v("inst")), g("true")), rec_app),
918            ),
919        );
920        ctx.add_definition("of_decide_eq_true".to_string(), ty, body);
921    }
922
923    /// Decidable equality of `Bool` (`decEqBool : Π(a b:Bool). Decidable (Eq Bool a b)`),
924    /// with the two Bool no-confusion lemmas it needs — all derived. This is a concrete
925    /// `Decidable` INSTANCE, so `decide` can actually discharge `Eq Bool _ _` goals.
926    fn register_dec_eq_bool(ctx: &mut Context) {
927        let prop = || Term::Sort(Universe::Prop);
928        let g = |s: &str| Term::Global(s.to_string());
929        let v = |s: &str| Term::Var(s.to_string());
930        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
931        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
932            param: p.to_string(),
933            param_type: Box::new(t),
934            body: Box::new(b),
935        };
936        let eqb = |x: Term, y: Term| ap(ap(ap(g("Eq"), g("Bool")), x), y);
937        let dec = |p: Term| ap(g("Decidable"), p);
938        let is_true = |p: Term, h: Term| ap(ap(g("isTrue"), p), h);
939        let is_false = |p: Term, h: Term| ap(ap(g("isFalse"), p), h);
940        let refl = |x: Term| ap(ap(g("refl"), g("Bool")), x);
941        // `match b return (λ_:Bool. Prop) with { <true-case>, <false-case> }`.
942        let bool_match = |t_case: Term, f_case: Term| Term::Match {
943            discriminant: Box::new(v("b")),
944            motive: Box::new(lm("_", g("Bool"), prop())),
945            cases: vec![t_case, f_case],
946        };
947
948        // bool_tf_ne : Not (Eq Bool true false)  — transport True@true along h to False@false.
949        let tf_discr = bool_match(g("True"), g("False"));
950        let tf_motive = lm("b", g("Bool"), lm("_", eqb(g("true"), v("b")), tf_discr));
951        let bool_tf_ne = lm(
952            "h",
953            eqb(g("true"), g("false")),
954            ap(
955                ap(ap(ap(ap(ap(g("Eq_rec_dep"), g("Bool")), g("true")), tf_motive), g("I")), g("false")),
956                v("h"),
957            ),
958        );
959        // bool_ft_ne : Not (Eq Bool false true)  — transport True@false along h to False@true.
960        let ft_discr = bool_match(g("False"), g("True"));
961        let ft_motive = lm("b", g("Bool"), lm("_", eqb(g("false"), v("b")), ft_discr));
962        let bool_ft_ne = lm(
963            "h",
964            eqb(g("false"), g("true")),
965            ap(
966                ap(ap(ap(ap(ap(g("Eq_rec_dep"), g("Bool")), g("false")), ft_motive), g("I")), g("true")),
967                v("h"),
968            ),
969        );
970
971        // decEqBool : Π(a b:Bool). Decidable (Eq Bool a b)
972        let de_ty = Term::Pi {
973            param: "a".to_string(),
974            param_type: Box::new(g("Bool")),
975            body_type: Box::new(Term::Pi {
976                param: "b".to_string(),
977                param_type: Box::new(g("Bool")),
978                body_type: Box::new(dec(eqb(v("a"), v("b")))),
979            }),
980        };
981        // Inner match on `b`, with the outer `a` fixed to a constructor `af`.
982        let inner = |af: Term, both_same: Term, ne_proof: Term, same_first: bool| {
983            let motive = lm("b'", g("Bool"), dec(eqb(af.clone(), v("b'"))));
984            // cases in Bool order [true, false]; `same` is when b matches af.
985            let (t_case, f_case) = if same_first {
986                // af == true
987                (
988                    is_true(eqb(af.clone(), g("true")), both_same),
989                    is_false(eqb(af.clone(), g("false")), ne_proof),
990                )
991            } else {
992                // af == false
993                (
994                    is_false(eqb(af.clone(), g("true")), ne_proof),
995                    is_true(eqb(af.clone(), g("false")), both_same),
996                )
997            };
998            Term::Match {
999                discriminant: Box::new(v("b")),
1000                motive: Box::new(motive),
1001                cases: vec![t_case, f_case],
1002            }
1003        };
1004        let a_true = inner(g("true"), refl(g("true")), bool_tf_ne, true);
1005        let a_false = inner(g("false"), refl(g("false")), bool_ft_ne, false);
1006        let outer_motive = lm("a'", g("Bool"), dec(eqb(v("a'"), v("b"))));
1007        let de_body = lm(
1008            "a",
1009            g("Bool"),
1010            lm(
1011                "b",
1012                g("Bool"),
1013                Term::Match {
1014                    discriminant: Box::new(v("a")),
1015                    motive: Box::new(outer_motive),
1016                    cases: vec![a_true, a_false],
1017                },
1018            ),
1019        );
1020        ctx.add_definition("decEqBool".to_string(), de_ty, de_body);
1021    }
1022
1023    /// Decidable equality of `Nat` (`decEqNat : Π(a b:Nat). Decidable (Eq Nat a b)`) — the
1024    /// flagship: it lets `decide` discharge arithmetic (in)equalities. Built by structural
1025    /// recursion on `a`, using derived Nat no-confusion (`Zero ≠ Succ`), `Succ` congruence
1026    /// (via J), and `Succ` injectivity (via a `pred` congruence). All derived — no axioms.
1027    fn register_dec_eq_nat(ctx: &mut Context) {
1028        let prop = || Term::Sort(Universe::Prop);
1029        let nat = || Term::Global("Nat".to_string());
1030        let g = |s: &str| Term::Global(s.to_string());
1031        let v = |s: &str| Term::Var(s.to_string());
1032        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1033        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
1034            param: p.to_string(),
1035            param_type: Box::new(t),
1036            body: Box::new(b),
1037        };
1038        let pi = |p: &str, t: Term, b: Term| Term::Pi {
1039            param: p.to_string(),
1040            param_type: Box::new(t),
1041            body_type: Box::new(b),
1042        };
1043        let succ = |n: Term| ap(g("Succ"), n);
1044        let eqn = |x: Term, y: Term| ap(ap(ap(g("Eq"), nat()), x), y);
1045        let not = |p: Term| ap(g("Not"), p);
1046        let dec = |p: Term| ap(g("Decidable"), p);
1047        let is_true = |p: Term, h: Term| ap(ap(g("isTrue"), p), h);
1048        let is_false = |p: Term, h: Term| ap(ap(g("isFalse"), p), h);
1049        let refln = |x: Term| ap(ap(g("refl"), nat()), x);
1050        // `match k return (λ_:Nat. R) with { <Zero-case>, <Succ-case> }` (Nat order Zero,Succ).
1051        let nat_match = |ret: Term, zero_case: Term, succ_case: Term| Term::Match {
1052            discriminant: Box::new(v("k")),
1053            motive: Box::new(lm("_", nat(), ret)),
1054            cases: vec![zero_case, succ_case],
1055        };
1056        // Transport `Eq_rec_dep Nat x (λk.λ_:Eq Nat x k. discr) base y h`.
1057        let transport = |x: Term, discr_motive: Term, base: Term, y: Term, h: Term| {
1058            ap(ap(ap(ap(ap(ap(g("Eq_rec_dep"), nat()), x), discr_motive), base), y), h)
1059        };
1060
1061        // nat_zs_ne : Π(n:Nat). Not (Eq Nat Zero (Succ n))  — Zero ≠ Succ n.
1062        let d_zs = nat_match(prop(), g("True"), lm("_", nat(), g("False")));
1063        let zs_motive = lm("k", nat(), lm("_", eqn(g("Zero"), v("k")), d_zs));
1064        let nat_zs_ne = lm(
1065            "n",
1066            nat(),
1067            lm(
1068                "h",
1069                eqn(g("Zero"), succ(v("n"))),
1070                transport(g("Zero"), zs_motive, g("I"), succ(v("n")), v("h")),
1071            ),
1072        );
1073        ctx.add_definition("nat_zs_ne".to_string(), pi("n", nat(), not(eqn(g("Zero"), succ(v("n"))))), nat_zs_ne);
1074
1075        // nat_sz_ne : Π(n:Nat). Not (Eq Nat (Succ n) Zero)  — Succ n ≠ Zero.
1076        let d_sz = nat_match(prop(), g("False"), lm("_", nat(), g("True")));
1077        let sz_motive = lm("k", nat(), lm("_", eqn(succ(v("n")), v("k")), d_sz));
1078        let nat_sz_ne = lm(
1079            "n",
1080            nat(),
1081            lm(
1082                "h",
1083                eqn(succ(v("n")), g("Zero")),
1084                transport(succ(v("n")), sz_motive, g("I"), g("Zero"), v("h")),
1085            ),
1086        );
1087        ctx.add_definition("nat_sz_ne".to_string(), pi("n", nat(), not(eqn(succ(v("n")), g("Zero")))), nat_sz_ne);
1088
1089        // succ_cong : Π(a b:Nat). Eq Nat a b → Eq Nat (Succ a) (Succ b).
1090        let sc_motive = lm("b'", nat(), lm("_", eqn(v("a"), v("b'")), eqn(succ(v("a")), succ(v("b'")))));
1091        let succ_cong_body = lm(
1092            "a",
1093            nat(),
1094            lm(
1095                "b",
1096                nat(),
1097                lm(
1098                    "h",
1099                    eqn(v("a"), v("b")),
1100                    ap(ap(ap(ap(ap(ap(g("Eq_rec_dep"), nat()), v("a")), sc_motive), refln(succ(v("a")))), v("b")), v("h")),
1101                ),
1102            ),
1103        );
1104        let succ_cong_ty = pi("a", nat(), pi("b", nat(), pi("_", eqn(v("a"), v("b")), eqn(succ(v("a")), succ(v("b"))))));
1105        ctx.add_definition("succ_cong".to_string(), succ_cong_ty, succ_cong_body);
1106
1107        // succ_inj : Π(a b:Nat). Eq Nat (Succ a) (Succ b) → Eq Nat a b.
1108        // `pred y := match y with Zero => Zero | Succ p => p`; transport `Eq Nat a (pred y)`
1109        // along `h`, base `refl Nat a` (since `pred (Succ a) ≡ a`), result `Eq Nat a b`.
1110        let pred_y = Term::Match {
1111            discriminant: Box::new(v("y'")),
1112            motive: Box::new(lm("_", nat(), nat())),
1113            cases: vec![g("Zero"), lm("p", nat(), v("p"))],
1114        };
1115        let si_motive = lm("y'", nat(), lm("_", eqn(succ(v("a")), v("y'")), eqn(v("a"), pred_y)));
1116        let succ_inj_body = lm(
1117            "a",
1118            nat(),
1119            lm(
1120                "b",
1121                nat(),
1122                lm(
1123                    "h",
1124                    eqn(succ(v("a")), succ(v("b"))),
1125                    ap(ap(ap(ap(ap(ap(g("Eq_rec_dep"), nat()), succ(v("a"))), si_motive), refln(v("a"))), succ(v("b"))), v("h")),
1126                ),
1127            ),
1128        );
1129        let succ_inj_ty = pi("a", nat(), pi("b", nat(), pi("_", eqn(succ(v("a")), succ(v("b"))), eqn(v("a"), v("b")))));
1130        ctx.add_definition("succ_inj".to_string(), succ_inj_ty, succ_inj_body);
1131
1132        // decEqNat : Π(a b:Nat). Decidable (Eq Nat a b)  — structural recursion on `a`.
1133        // Inner match on the recursive result `rec a' b'`.
1134        let rec_call = ap(ap(v("rec"), v("a'")), v("b'"));
1135        let true_case = lm(
1136            "hp",
1137            eqn(v("a'"), v("b'")),
1138            is_true(eqn(succ(v("a'")), succ(v("b'"))), ap(ap(ap(g("succ_cong"), v("a'")), v("b'")), v("hp"))),
1139        );
1140        let false_case = lm(
1141            "hnp",
1142            not(eqn(v("a'"), v("b'"))),
1143            is_false(
1144                eqn(succ(v("a'")), succ(v("b'"))),
1145                lm(
1146                    "hs",
1147                    eqn(succ(v("a'")), succ(v("b'"))),
1148                    ap(v("hnp"), ap(ap(ap(g("succ_inj"), v("a'")), v("b'")), v("hs"))),
1149                ),
1150            ),
1151        );
1152        let inner_rec_match = Term::Match {
1153            discriminant: Box::new(rec_call),
1154            motive: Box::new(lm("_", dec(eqn(v("a'"), v("b'"))), dec(eqn(succ(v("a'")), succ(v("b'")))))),
1155            cases: vec![true_case, false_case],
1156        };
1157        // b-match in the `Succ a'` branch: motive λb'. Decidable (Eq Nat (Succ a') b').
1158        let succ_b_match = Term::Match {
1159            discriminant: Box::new(v("b")),
1160            motive: Box::new(lm("b'", nat(), dec(eqn(succ(v("a'")), v("b'"))))),
1161            cases: vec![
1162                is_false(eqn(succ(v("a'")), g("Zero")), ap(g("nat_sz_ne"), v("a'"))),
1163                lm("b'", nat(), inner_rec_match),
1164            ],
1165        };
1166        // b-match in the `Zero` branch: motive λb'. Decidable (Eq Nat Zero b').
1167        let zero_b_match = Term::Match {
1168            discriminant: Box::new(v("b")),
1169            motive: Box::new(lm("b'", nat(), dec(eqn(g("Zero"), v("b'"))))),
1170            cases: vec![
1171                is_true(eqn(g("Zero"), g("Zero")), refln(g("Zero"))),
1172                lm("b'", nat(), is_false(eqn(g("Zero"), succ(v("b'"))), ap(g("nat_zs_ne"), v("b'")))),
1173            ],
1174        };
1175        // Outer match on `a`: motive λa'. Decidable (Eq Nat a' b).
1176        let a_match = Term::Match {
1177            discriminant: Box::new(v("a")),
1178            motive: Box::new(lm("a'", nat(), dec(eqn(v("a'"), v("b"))))),
1179            cases: vec![zero_b_match, lm("a'", nat(), succ_b_match)],
1180        };
1181        let deceqnat_body = Term::Fix {
1182            name: "rec".to_string(),
1183            body: Box::new(lm("a", nat(), lm("b", nat(), a_match))),
1184        };
1185        let deceqnat_ty = pi("a", nat(), pi("b", nat(), dec(eqn(v("a"), v("b")))));
1186        ctx.add_definition("decEqNat".to_string(), deceqnat_ty, deceqnat_body);
1187    }
1188
1189    /// The `native_decide` trust boundary (TCB additions, exactly as Lean's `native_decide`
1190    /// adds `ofReduceBool` + its compiler): `reduceBool : Bool → Bool` is the identity, but
1191    /// the KERNEL reduces `reduceBool t` by running the fast [`crate::eval`] evaluator (a
1192    /// reduction hook), and `ofReduceBool` turns `reduceBool a = b` into `a = b`. So a
1193    /// `native_decide` proof discharges `decide p inst = true` by native evaluation instead
1194    /// of the kernel re-normalizing the decision procedure.
1195    fn register_native_decide(ctx: &mut Context) {
1196        let g = |s: &str| Term::Global(s.to_string());
1197        let v = |s: &str| Term::Var(s.to_string());
1198        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1199        let pi = |p: &str, t: Term, b: Term| Term::Pi {
1200            param: p.to_string(),
1201            param_type: Box::new(t),
1202            body_type: Box::new(b),
1203        };
1204        let eqb = |x: Term, y: Term| ap(ap(ap(g("Eq"), g("Bool")), x), y);
1205        // reduceBool : Bool → Bool
1206        ctx.add_declaration("reduceBool", pi("_", g("Bool"), g("Bool")));
1207        // ofReduceBool : Π(a:Bool). Π(b:Bool). Eq Bool (reduceBool a) b → Eq Bool a b
1208        ctx.add_declaration(
1209            "ofReduceBool",
1210            pi(
1211                "a",
1212                g("Bool"),
1213                pi(
1214                    "b",
1215                    g("Bool"),
1216                    pi("_", eqb(ap(g("reduceBool"), v("a")), v("b")), eqb(v("a"), v("b"))),
1217                ),
1218            ),
1219        );
1220    }
1221
1222    /// QUOTIENT TYPES — CIC primitives (as in Lean). `Quot A r` is the quotient of `A` by
1223    /// the relation `r`; `Quot_mk` forms classes; `Quot_lift` lifts a relation-respecting
1224    /// function (with the definitional computation rule `Quot_lift … (Quot_mk a) ≡ f a`
1225    /// implemented in `reduction.rs`); `Quot_ind` is the induction principle; and the
1226    /// `Quot_sound` AXIOM identifies related representatives — the propositional content that
1227    /// makes `Quot` a genuine quotient. `Quot A r` is OPAQUE (not an inductive), so it cannot
1228    /// be pattern-matched — only `Quot_lift`/`Quot_ind` eliminate it, which is what keeps the
1229    /// identification consistent. Unblocks ℤ/ℚ/ℝ, `Multiset`, setoids.
1230    fn register_quot(ctx: &mut Context) {
1231        let type0 = || Term::Sort(Universe::Type(0));
1232        let prop = || Term::Sort(Universe::Prop);
1233        let g = |s: &str| Term::Global(s.to_string());
1234        let v = |s: &str| Term::Var(s.to_string());
1235        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1236        let pi = |p: &str, t: Term, b: Term| Term::Pi {
1237            param: p.to_string(),
1238            param_type: Box::new(t),
1239            body_type: Box::new(b),
1240        };
1241        let arrow = |t: Term, b: Term| pi("_", t, b);
1242        // r : A → A → Prop
1243        let rel = || arrow(v("A"), arrow(v("A"), prop()));
1244        let quot = |a: Term, r: Term| ap(ap(g("Quot"), a), r);
1245        let mk = |a: Term, r: Term, x: Term| ap(ap(ap(g("Quot_mk"), a), r), x);
1246        let eq3 = |t: Term, x: Term, y: Term| ap(ap(ap(g("Eq"), t), x), y);
1247
1248        // Quot : Π(A:Type). (A → A → Prop) → Type
1249        ctx.add_declaration("Quot", pi("A", type0(), arrow(rel(), type0())));
1250        // Quot_mk : Π(A:Type). Π(r:A→A→Prop). A → Quot A r
1251        ctx.add_declaration(
1252            "Quot_mk",
1253            pi("A", type0(), pi("r", rel(), arrow(v("A"), quot(v("A"), v("r"))))),
1254        );
1255        // Quot_lift : Π(A). Π(r). Π(B:Type). Π(f:A→B).
1256        //   Π(h: Π(a b:A). r a b → Eq B (f a) (f b)). Quot A r → B
1257        let resp = pi(
1258            "a",
1259            v("A"),
1260            pi(
1261                "b",
1262                v("A"),
1263                arrow(
1264                    ap(ap(v("r"), v("a")), v("b")),
1265                    eq3(v("B"), ap(v("f"), v("a")), ap(v("f"), v("b"))),
1266                ),
1267            ),
1268        );
1269        ctx.add_declaration(
1270            "Quot_lift",
1271            pi(
1272                "A",
1273                type0(),
1274                pi(
1275                    "r",
1276                    rel(),
1277                    pi(
1278                        "B",
1279                        type0(),
1280                        pi(
1281                            "f",
1282                            arrow(v("A"), v("B")),
1283                            pi("h", resp, arrow(quot(v("A"), v("r")), v("B"))),
1284                        ),
1285                    ),
1286                ),
1287            ),
1288        );
1289        // Quot_sound : Π(A). Π(r). Π(a b:A). r a b → Eq (Quot A r) (Quot_mk A r a) (Quot_mk A r b)
1290        ctx.add_declaration(
1291            "Quot_sound",
1292            pi(
1293                "A",
1294                type0(),
1295                pi(
1296                    "r",
1297                    rel(),
1298                    pi(
1299                        "a",
1300                        v("A"),
1301                        pi(
1302                            "b",
1303                            v("A"),
1304                            arrow(
1305                                ap(ap(v("r"), v("a")), v("b")),
1306                                eq3(
1307                                    quot(v("A"), v("r")),
1308                                    mk(v("A"), v("r"), v("a")),
1309                                    mk(v("A"), v("r"), v("b")),
1310                                ),
1311                            ),
1312                        ),
1313                    ),
1314                ),
1315            ),
1316        );
1317        // Quot_ind : Π(A). Π(r). Π(P: Quot A r → Prop).
1318        //   (Π(a:A). P (Quot_mk A r a)) → Π(q: Quot A r). P q
1319        ctx.add_declaration(
1320            "Quot_ind",
1321            pi(
1322                "A",
1323                type0(),
1324                pi(
1325                    "r",
1326                    rel(),
1327                    pi(
1328                        "P",
1329                        arrow(quot(v("A"), v("r")), prop()),
1330                        arrow(
1331                            pi("a", v("A"), ap(v("P"), mk(v("A"), v("r"), v("a")))),
1332                            pi("q", quot(v("A"), v("r")), ap(v("P"), v("q"))),
1333                        ),
1334                    ),
1335                ),
1336            ),
1337        );
1338    }
1339
1340    /// Eq : Π(A : Type 0). A → A → Prop
1341    /// refl : Π(A : Type 0). Π(x : A). Eq A x x
1342    /// Well-founded recursion. `Acc (A)(R) : A → Prop` is the accessibility
1343    /// predicate — `x` is accessible under `R` when every `R`-predecessor of `x` is
1344    /// accessible — with the single constructor
1345    /// `Acc_intro : Π(A)(R)(x). (Π(y:A). R y x → Acc A R y) → Acc A R x`. Its
1346    /// recursive field is FUNCTIONAL (the accessibility of all predecessors), a
1347    /// strictly-positive occurrence under a `Π`; the auto-derived eliminator
1348    /// `Acc_rec` carries the matching functional induction hypothesis, and recursion
1349    /// over an `Acc` proof terminates by the applied-smaller guard. `WellFounded A R`
1350    /// abbreviates `Π(x:A). Acc A R x`. Together these are the substrate for
1351    /// definitions by well-founded recursion (gcd, division, strong induction) that
1352    /// structural recursion cannot express — the parity item Lean's kernel has and
1353    /// ours did not.
1354    fn register_acc(ctx: &mut Context) {
1355        fn pi(p: &str, t: Term, b: Term) -> Term {
1356            Term::Pi { param: p.to_string(), param_type: Box::new(t), body_type: Box::new(b) }
1357        }
1358        fn arrow(a: Term, b: Term) -> Term {
1359            pi("_", a, b)
1360        }
1361        fn v(n: &str) -> Term {
1362            Term::Var(n.to_string())
1363        }
1364        fn g(n: &str) -> Term {
1365            Term::Global(n.to_string())
1366        }
1367        fn apps(f: Term, xs: &[Term]) -> Term {
1368            xs.iter().fold(f, |a, x| Term::App(Box::new(a), Box::new(x.clone())))
1369        }
1370        let type0 = || Term::Sort(Universe::Type(0));
1371        let prop = || Term::Sort(Universe::Prop);
1372        // R : A → A → Prop
1373        let rel = || arrow(v("A"), arrow(v("A"), prop()));
1374        // Acc A R x
1375        let acc = |x: Term| apps(g("Acc"), &[v("A"), v("R"), x]);
1376
1377        // Acc : Π(A:Type). Π(R:A→A→Prop). A → Prop  — two params (A, R), one index (x).
1378        let acc_ty = pi("A", type0(), pi("R", rel(), arrow(v("A"), prop())));
1379        ctx.add_indexed_inductive("Acc", acc_ty, 2);
1380
1381        // Acc_intro : Π(A)(R)(x). (Π(y:A). R y x → Acc A R y) → Acc A R x
1382        let intro_ty = pi(
1383            "A",
1384            type0(),
1385            pi(
1386                "R",
1387                rel(),
1388                pi(
1389                    "x",
1390                    v("A"),
1391                    arrow(
1392                        pi("y", v("A"), arrow(apps(v("R"), &[v("y"), v("x")]), acc(v("y")))),
1393                        acc(v("x")),
1394                    ),
1395                ),
1396            ),
1397        );
1398        ctx.add_constructor("Acc_intro", "Acc", intro_ty);
1399
1400        // Acc_rec — the AUTO-DERIVED dependent eliminator carrying the functional
1401        // induction hypothesis (two-kernel verified in tests/well_founded.rs). A
1402        // kernel-checked definition, not an axiom.
1403        let (rec_ty, rec_body) = crate::recursor::derive_recursor(ctx, "Acc")
1404            .expect("Acc's dependent eliminator must derive");
1405        ctx.add_definition("Acc_rec".to_string(), rec_ty, rec_body);
1406
1407        // WellFounded A R := Π(x:A). Acc A R x  — `R` is well-founded iff every
1408        // element is accessible. A plain definition (abbreviation), no new inductive.
1409        let wf_ty = pi("A", type0(), arrow(rel(), prop()));
1410        let wf_body = Term::Lambda {
1411            param: "A".to_string(),
1412            param_type: Box::new(type0()),
1413            body: Box::new(Term::Lambda {
1414                param: "R".to_string(),
1415                param_type: Box::new(rel()),
1416                body: Box::new(pi("x", v("A"), acc(v("x")))),
1417            }),
1418        };
1419        ctx.add_definition("WellFounded".to_string(), wf_ty, wf_body);
1420    }
1421
1422    fn register_eq(ctx: &mut Context) {
1423        // Eq : Π(A : Type 0). A → A → Prop
1424        let eq_type = Term::Pi {
1425            param: "A".to_string(),
1426            param_type: Box::new(Term::Sort(Universe::Type(0))),
1427            body_type: Box::new(Term::Pi {
1428                param: "x".to_string(),
1429                param_type: Box::new(Term::Var("A".to_string())),
1430                body_type: Box::new(Term::Pi {
1431                    param: "y".to_string(),
1432                    param_type: Box::new(Term::Var("A".to_string())),
1433                    body_type: Box::new(Term::Sort(Universe::Prop)),
1434                }),
1435            }),
1436        };
1437        // `Eq (A) (x) : A → Prop` — the leading `A` and `x` are uniform parameters
1438        // (`refl`'s result `Eq A x x` repeats them), the trailing slot is the index.
1439        ctx.add_indexed_inductive("Eq", eq_type, 2);
1440
1441        // refl : Π(A : Type 0). Π(x : A). Eq A x x
1442        let refl_type = Term::Pi {
1443            param: "A".to_string(),
1444            param_type: Box::new(Term::Sort(Universe::Type(0))),
1445            body_type: Box::new(Term::Pi {
1446                param: "x".to_string(),
1447                param_type: Box::new(Term::Var("A".to_string())),
1448                body_type: Box::new(Term::App(
1449                    Box::new(Term::App(
1450                        Box::new(Term::App(
1451                            Box::new(Term::Global("Eq".to_string())),
1452                            Box::new(Term::Var("A".to_string())),
1453                        )),
1454                        Box::new(Term::Var("x".to_string())),
1455                    )),
1456                    Box::new(Term::Var("x".to_string())),
1457                )),
1458            }),
1459        };
1460        ctx.add_constructor("refl", "Eq", refl_type);
1461
1462        // `Eq_rec_dep` — the AUTO-DERIVED full dependent eliminator (Paulin-Mohring J):
1463        // `Π(A). Π(x:A). Π(P:Π(y:A). Eq A x y → Type). P x (refl A x) → Π(y). Π(h:Eq A x y).
1464        // P y h`. A kernel-CHECKED definition (two-kernel verified), not an axiom — so the
1465        // equality eliminator and its lemmas below leave the trusted base.
1466        Self::register_eq_recursor(ctx);
1467
1468        // Eq_rec : Π(A:Type). Π(x:A). Π(P:A→Prop). P x → Π(y:A). Eq A x y → P y
1469        // The (proof-irrelevant) substitution eliminator — now DERIVED from J.
1470        Self::register_eq_rec(ctx);
1471
1472        // Eq_sym : Π(A:Type). Π(x:A). Π(y:A). Eq A x y → Eq A y x — DERIVED from J.
1473        Self::register_eq_sym(ctx);
1474
1475        // Eq_trans : Π(A:Type). Π(x:A). Π(y:A). Π(z:A). Eq A x y → Eq A y z → Eq A x z — DERIVED.
1476        Self::register_eq_trans(ctx);
1477    }
1478
1479    /// Register the auto-derived dependent equality eliminator `Eq_rec_dep` (full J) as a
1480    /// kernel-checked definition. `Eq` and `refl` must already be registered.
1481    fn register_eq_recursor(ctx: &mut Context) {
1482        let (ty, body) = crate::recursor::derive_recursor(ctx, "Eq")
1483            .expect("Eq's dependent eliminator (J) must derive");
1484        ctx.add_definition("Eq_rec_dep".to_string(), ty, body);
1485    }
1486
1487    /// Eq_rec : Π(A:Type). Π(x:A). Π(P:A→Prop). P x → Π(y:A). Eq A x y → P y
1488    /// The eliminator for equality - Leibniz's Law / Substitution of Equals
1489    fn register_eq_rec(ctx: &mut Context) {
1490        let a = Term::Var("A".to_string());
1491        let x = Term::Var("x".to_string());
1492        let y = Term::Var("y".to_string());
1493        let p = Term::Var("P".to_string());
1494
1495        // P x = P applied to x
1496        let p_x = Term::App(Box::new(p.clone()), Box::new(x.clone()));
1497
1498        // P y = P applied to y
1499        let p_y = Term::App(Box::new(p.clone()), Box::new(y.clone()));
1500
1501        // Eq A x y
1502        let eq_a_x_y = Term::App(
1503            Box::new(Term::App(
1504                Box::new(Term::App(
1505                    Box::new(Term::Global("Eq".to_string())),
1506                    Box::new(a.clone()),
1507                )),
1508                Box::new(x.clone()),
1509            )),
1510            Box::new(y.clone()),
1511        );
1512
1513        // P : A → Prop
1514        let p_type = Term::Pi {
1515            param: "_".to_string(),
1516            param_type: Box::new(a.clone()),
1517            body_type: Box::new(Term::Sort(Universe::Prop)),
1518        };
1519
1520        // Full type: Π(A:Type). Π(x:A). Π(P:A→Prop). P x → Π(y:A). Eq A x y → P y
1521        let eq_rec_type = Term::Pi {
1522            param: "A".to_string(),
1523            param_type: Box::new(Term::Sort(Universe::Type(0))),
1524            body_type: Box::new(Term::Pi {
1525                param: "x".to_string(),
1526                param_type: Box::new(a.clone()),
1527                body_type: Box::new(Term::Pi {
1528                    param: "P".to_string(),
1529                    param_type: Box::new(p_type),
1530                    body_type: Box::new(Term::Pi {
1531                        param: "_".to_string(),
1532                        param_type: Box::new(p_x),
1533                        body_type: Box::new(Term::Pi {
1534                            param: "y".to_string(),
1535                            param_type: Box::new(a.clone()),
1536                            body_type: Box::new(Term::Pi {
1537                                param: "_".to_string(),
1538                                param_type: Box::new(eq_a_x_y),
1539                                body_type: Box::new(p_y),
1540                            }),
1541                        }),
1542                    }),
1543                }),
1544            }),
1545        };
1546
1547        // Body — the substitution eliminator DERIVED from J by discarding the proof in the
1548        // motive: `λA x P base y h. Eq_rec_dep A x (λy'. λ_:Eq A x y'. P y') base y h`.
1549        let gl = |s: &str| Term::Global(s.to_string());
1550        let vr = |s: &str| Term::Var(s.to_string());
1551        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1552        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
1553            param: p.to_string(),
1554            param_type: Box::new(t),
1555            body: Box::new(b),
1556        };
1557        let eq3 = |a: Term, x: Term, y: Term| ap(ap(ap(gl("Eq"), a), x), y);
1558        let dep_motive = lm(
1559            "y'",
1560            vr("A"),
1561            lm("_", eq3(vr("A"), vr("x"), vr("y'")), ap(vr("P"), vr("y'"))),
1562        );
1563        let call = ap(
1564            ap(
1565                ap(ap(ap(ap(gl("Eq_rec_dep"), vr("A")), vr("x")), dep_motive), vr("base")),
1566                vr("y"),
1567            ),
1568            vr("h"),
1569        );
1570        let eq_rec_body = lm(
1571            "A",
1572            Term::Sort(Universe::Type(0)),
1573            lm(
1574                "x",
1575                vr("A"),
1576                lm(
1577                    "P",
1578                    Term::Pi {
1579                        param: "_".to_string(),
1580                        param_type: Box::new(vr("A")),
1581                        body_type: Box::new(Term::Sort(Universe::Prop)),
1582                    },
1583                    lm(
1584                        "base",
1585                        ap(vr("P"), vr("x")),
1586                        lm("y", vr("A"), lm("h", eq3(vr("A"), vr("x"), vr("y")), call)),
1587                    ),
1588                ),
1589            ),
1590        );
1591
1592        ctx.add_definition("Eq_rec".to_string(), eq_rec_type, eq_rec_body);
1593    }
1594
1595    /// Eq_sym : Π(A:Type). Π(x:A). Π(y:A). Eq A x y → Eq A y x
1596    /// Symmetry of equality
1597    fn register_eq_sym(ctx: &mut Context) {
1598        let a = Term::Var("A".to_string());
1599        let x = Term::Var("x".to_string());
1600        let y = Term::Var("y".to_string());
1601
1602        // Eq A x y
1603        let eq_a_x_y = Term::App(
1604            Box::new(Term::App(
1605                Box::new(Term::App(
1606                    Box::new(Term::Global("Eq".to_string())),
1607                    Box::new(a.clone()),
1608                )),
1609                Box::new(x.clone()),
1610            )),
1611            Box::new(y.clone()),
1612        );
1613
1614        // Eq A y x
1615        let eq_a_y_x = Term::App(
1616            Box::new(Term::App(
1617                Box::new(Term::App(
1618                    Box::new(Term::Global("Eq".to_string())),
1619                    Box::new(a.clone()),
1620                )),
1621                Box::new(y.clone()),
1622            )),
1623            Box::new(x.clone()),
1624        );
1625
1626        // Π(A:Type). Π(x:A). Π(y:A). Eq A x y → Eq A y x
1627        let eq_sym_type = Term::Pi {
1628            param: "A".to_string(),
1629            param_type: Box::new(Term::Sort(Universe::Type(0))),
1630            body_type: Box::new(Term::Pi {
1631                param: "x".to_string(),
1632                param_type: Box::new(a.clone()),
1633                body_type: Box::new(Term::Pi {
1634                    param: "y".to_string(),
1635                    param_type: Box::new(a),
1636                    body_type: Box::new(Term::Pi {
1637                        param: "_".to_string(),
1638                        param_type: Box::new(eq_a_x_y),
1639                        body_type: Box::new(eq_a_y_x),
1640                    }),
1641                }),
1642            }),
1643        };
1644
1645        // Body — symmetry DERIVED from J: transport `Eq A x _` along `h` with base `refl`.
1646        // `λA x y h. Eq_rec_dep A x (λy'. λ_:Eq A x y'. Eq A y' x) (refl A x) y h`.
1647        let gl = |s: &str| Term::Global(s.to_string());
1648        let vr = |s: &str| Term::Var(s.to_string());
1649        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1650        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
1651            param: p.to_string(),
1652            param_type: Box::new(t),
1653            body: Box::new(b),
1654        };
1655        let eq3 = |a: Term, x: Term, y: Term| ap(ap(ap(gl("Eq"), a), x), y);
1656        let motive = lm(
1657            "y'",
1658            vr("A"),
1659            lm("_", eq3(vr("A"), vr("x"), vr("y'")), eq3(vr("A"), vr("y'"), vr("x"))),
1660        );
1661        let refl_a_x = ap(ap(gl("refl"), vr("A")), vr("x"));
1662        let call = ap(
1663            ap(
1664                ap(ap(ap(ap(gl("Eq_rec_dep"), vr("A")), vr("x")), motive), refl_a_x),
1665                vr("y"),
1666            ),
1667            vr("h"),
1668        );
1669        let eq_sym_body = lm(
1670            "A",
1671            Term::Sort(Universe::Type(0)),
1672            lm(
1673                "x",
1674                vr("A"),
1675                lm("y", vr("A"), lm("h", eq3(vr("A"), vr("x"), vr("y")), call)),
1676            ),
1677        );
1678
1679        ctx.add_definition("Eq_sym".to_string(), eq_sym_type, eq_sym_body);
1680    }
1681
1682    /// Eq_trans : Π(A:Type). Π(x:A). Π(y:A). Π(z:A). Eq A x y → Eq A y z → Eq A x z
1683    /// Transitivity of equality
1684    fn register_eq_trans(ctx: &mut Context) {
1685        let a = Term::Var("A".to_string());
1686        let x = Term::Var("x".to_string());
1687        let y = Term::Var("y".to_string());
1688        let z = Term::Var("z".to_string());
1689
1690        // Eq A x y
1691        let eq_a_x_y = Term::App(
1692            Box::new(Term::App(
1693                Box::new(Term::App(
1694                    Box::new(Term::Global("Eq".to_string())),
1695                    Box::new(a.clone()),
1696                )),
1697                Box::new(x.clone()),
1698            )),
1699            Box::new(y.clone()),
1700        );
1701
1702        // Eq A y z
1703        let eq_a_y_z = Term::App(
1704            Box::new(Term::App(
1705                Box::new(Term::App(
1706                    Box::new(Term::Global("Eq".to_string())),
1707                    Box::new(a.clone()),
1708                )),
1709                Box::new(y.clone()),
1710            )),
1711            Box::new(z.clone()),
1712        );
1713
1714        // Eq A x z
1715        let eq_a_x_z = Term::App(
1716            Box::new(Term::App(
1717                Box::new(Term::App(
1718                    Box::new(Term::Global("Eq".to_string())),
1719                    Box::new(a.clone()),
1720                )),
1721                Box::new(x.clone()),
1722            )),
1723            Box::new(z.clone()),
1724        );
1725
1726        // Π(A:Type). Π(x:A). Π(y:A). Π(z:A). Eq A x y → Eq A y z → Eq A x z
1727        let eq_trans_type = Term::Pi {
1728            param: "A".to_string(),
1729            param_type: Box::new(Term::Sort(Universe::Type(0))),
1730            body_type: Box::new(Term::Pi {
1731                param: "x".to_string(),
1732                param_type: Box::new(a.clone()),
1733                body_type: Box::new(Term::Pi {
1734                    param: "y".to_string(),
1735                    param_type: Box::new(a.clone()),
1736                    body_type: Box::new(Term::Pi {
1737                        param: "z".to_string(),
1738                        param_type: Box::new(a),
1739                        body_type: Box::new(Term::Pi {
1740                            param: "_".to_string(),
1741                            param_type: Box::new(eq_a_x_y),
1742                            body_type: Box::new(Term::Pi {
1743                                param: "_".to_string(),
1744                                param_type: Box::new(eq_a_y_z),
1745                                body_type: Box::new(eq_a_x_z),
1746                            }),
1747                        }),
1748                    }),
1749                }),
1750            }),
1751        };
1752
1753        // Body — transitivity DERIVED from J: transport `Eq A x _` along `hyz` (relating
1754        // `y` and `z`) with base `hxy`.
1755        // `λA x y z hxy hyz. Eq_rec_dep A y (λz'. λ_:Eq A y z'. Eq A x z') hxy z hyz`.
1756        let gl = |s: &str| Term::Global(s.to_string());
1757        let vr = |s: &str| Term::Var(s.to_string());
1758        let ap = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
1759        let lm = |p: &str, t: Term, b: Term| Term::Lambda {
1760            param: p.to_string(),
1761            param_type: Box::new(t),
1762            body: Box::new(b),
1763        };
1764        let eq3 = |a: Term, x: Term, y: Term| ap(ap(ap(gl("Eq"), a), x), y);
1765        let motive = lm(
1766            "z'",
1767            vr("A"),
1768            lm("_", eq3(vr("A"), vr("y"), vr("z'")), eq3(vr("A"), vr("x"), vr("z'"))),
1769        );
1770        let call = ap(
1771            ap(
1772                ap(ap(ap(ap(gl("Eq_rec_dep"), vr("A")), vr("y")), motive), vr("hxy")),
1773                vr("z"),
1774            ),
1775            vr("hyz"),
1776        );
1777        let eq_trans_body = lm(
1778            "A",
1779            Term::Sort(Universe::Type(0)),
1780            lm(
1781                "x",
1782                vr("A"),
1783                lm(
1784                    "y",
1785                    vr("A"),
1786                    lm(
1787                        "z",
1788                        vr("A"),
1789                        lm(
1790                            "hxy",
1791                            eq3(vr("A"), vr("x"), vr("y")),
1792                            lm("hyz", eq3(vr("A"), vr("y"), vr("z")), call),
1793                        ),
1794                    ),
1795                ),
1796            ),
1797        );
1798
1799        ctx.add_definition("Eq_trans".to_string(), eq_trans_type, eq_trans_body);
1800    }
1801
1802    /// And : Prop → Prop → Prop
1803    /// conj : Π(P : Prop). Π(Q : Prop). P → Q → And P Q
1804    fn register_and(ctx: &mut Context) {
1805        // And : Prop → Prop → Prop
1806        let and_type = Term::Pi {
1807            param: "P".to_string(),
1808            param_type: Box::new(Term::Sort(Universe::Prop)),
1809            body_type: Box::new(Term::Pi {
1810                param: "Q".to_string(),
1811                param_type: Box::new(Term::Sort(Universe::Prop)),
1812                body_type: Box::new(Term::Sort(Universe::Prop)),
1813            }),
1814        };
1815        ctx.add_inductive("And", and_type);
1816
1817        // conj : Π(P : Prop). Π(Q : Prop). P → Q → And P Q
1818        let conj_type = Term::Pi {
1819            param: "P".to_string(),
1820            param_type: Box::new(Term::Sort(Universe::Prop)),
1821            body_type: Box::new(Term::Pi {
1822                param: "Q".to_string(),
1823                param_type: Box::new(Term::Sort(Universe::Prop)),
1824                body_type: Box::new(Term::Pi {
1825                    param: "p".to_string(),
1826                    param_type: Box::new(Term::Var("P".to_string())),
1827                    body_type: Box::new(Term::Pi {
1828                        param: "q".to_string(),
1829                        param_type: Box::new(Term::Var("Q".to_string())),
1830                        body_type: Box::new(Term::App(
1831                            Box::new(Term::App(
1832                                Box::new(Term::Global("And".to_string())),
1833                                Box::new(Term::Var("P".to_string())),
1834                            )),
1835                            Box::new(Term::Var("Q".to_string())),
1836                        )),
1837                    }),
1838                }),
1839            }),
1840        };
1841        ctx.add_constructor("conj", "And", conj_type);
1842    }
1843
1844    /// Or : Prop → Prop → Prop
1845    /// left : Π(P : Prop). Π(Q : Prop). P → Or P Q
1846    /// right : Π(P : Prop). Π(Q : Prop). Q → Or P Q
1847    fn register_or(ctx: &mut Context) {
1848        // Or : Prop → Prop → Prop
1849        let or_type = Term::Pi {
1850            param: "P".to_string(),
1851            param_type: Box::new(Term::Sort(Universe::Prop)),
1852            body_type: Box::new(Term::Pi {
1853                param: "Q".to_string(),
1854                param_type: Box::new(Term::Sort(Universe::Prop)),
1855                body_type: Box::new(Term::Sort(Universe::Prop)),
1856            }),
1857        };
1858        ctx.add_inductive("Or", or_type);
1859
1860        // left : Π(P : Prop). Π(Q : Prop). P → Or P Q
1861        let left_type = Term::Pi {
1862            param: "P".to_string(),
1863            param_type: Box::new(Term::Sort(Universe::Prop)),
1864            body_type: Box::new(Term::Pi {
1865                param: "Q".to_string(),
1866                param_type: Box::new(Term::Sort(Universe::Prop)),
1867                body_type: Box::new(Term::Pi {
1868                    param: "p".to_string(),
1869                    param_type: Box::new(Term::Var("P".to_string())),
1870                    body_type: Box::new(Term::App(
1871                        Box::new(Term::App(
1872                            Box::new(Term::Global("Or".to_string())),
1873                            Box::new(Term::Var("P".to_string())),
1874                        )),
1875                        Box::new(Term::Var("Q".to_string())),
1876                    )),
1877                }),
1878            }),
1879        };
1880        ctx.add_constructor("left", "Or", left_type);
1881
1882        // right : Π(P : Prop). Π(Q : Prop). Q → Or P Q
1883        let right_type = Term::Pi {
1884            param: "P".to_string(),
1885            param_type: Box::new(Term::Sort(Universe::Prop)),
1886            body_type: Box::new(Term::Pi {
1887                param: "Q".to_string(),
1888                param_type: Box::new(Term::Sort(Universe::Prop)),
1889                body_type: Box::new(Term::Pi {
1890                    param: "q".to_string(),
1891                    param_type: Box::new(Term::Var("Q".to_string())),
1892                    body_type: Box::new(Term::App(
1893                        Box::new(Term::App(
1894                            Box::new(Term::Global("Or".to_string())),
1895                            Box::new(Term::Var("P".to_string())),
1896                        )),
1897                        Box::new(Term::Var("Q".to_string())),
1898                    )),
1899                }),
1900            }),
1901        };
1902        ctx.add_constructor("right", "Or", right_type);
1903    }
1904
1905    /// Ex : Π(A : Type 0). (A → Prop) → Prop
1906    /// witness : Π(A : Type 0). Π(P : A → Prop). Π(x : A). P x → Ex A P
1907    ///
1908    /// Ex is the existential type for propositions.
1909    /// Ex A P means "there exists an x:A such that P(x)"
1910    fn register_ex(ctx: &mut Context) {
1911        // Ex : Π(A : Type 0). (A → Prop) → Prop
1912        let ex_type = Term::Pi {
1913            param: "A".to_string(),
1914            param_type: Box::new(Term::Sort(Universe::Type(0))),
1915            body_type: Box::new(Term::Pi {
1916                param: "P".to_string(),
1917                param_type: Box::new(Term::Pi {
1918                    param: "_".to_string(),
1919                    param_type: Box::new(Term::Var("A".to_string())),
1920                    body_type: Box::new(Term::Sort(Universe::Prop)),
1921                }),
1922                body_type: Box::new(Term::Sort(Universe::Prop)),
1923            }),
1924        };
1925        ctx.add_inductive("Ex", ex_type);
1926
1927        // witness : Π(A : Type 0). Π(P : A → Prop). Π(x : A). P x → Ex A P
1928        //
1929        // To construct an existential proof, provide:
1930        // - A: the type being quantified over
1931        // - P: the predicate
1932        // - x: the witness (an element of A)
1933        // - proof: evidence that P(x) holds
1934        let a = Term::Var("A".to_string());
1935        let p = Term::Var("P".to_string());
1936        let x = Term::Var("x".to_string());
1937
1938        // P x = P applied to x
1939        let p_x = Term::App(Box::new(p.clone()), Box::new(x.clone()));
1940
1941        // Ex A P = Ex applied to A, then to P
1942        let ex_a_p = Term::App(
1943            Box::new(Term::App(
1944                Box::new(Term::Global("Ex".to_string())),
1945                Box::new(a.clone()),
1946            )),
1947            Box::new(p.clone()),
1948        );
1949
1950        let witness_type = Term::Pi {
1951            param: "A".to_string(),
1952            param_type: Box::new(Term::Sort(Universe::Type(0))),
1953            body_type: Box::new(Term::Pi {
1954                param: "P".to_string(),
1955                param_type: Box::new(Term::Pi {
1956                    param: "_".to_string(),
1957                    param_type: Box::new(a.clone()),
1958                    body_type: Box::new(Term::Sort(Universe::Prop)),
1959                }),
1960                body_type: Box::new(Term::Pi {
1961                    param: "x".to_string(),
1962                    param_type: Box::new(a),
1963                    body_type: Box::new(Term::Pi {
1964                        param: "_".to_string(),
1965                        param_type: Box::new(p_x),
1966                        body_type: Box::new(ex_a_p),
1967                    }),
1968                }),
1969            }),
1970        };
1971        ctx.add_constructor("witness", "Ex", witness_type);
1972    }
1973
1974    // -------------------------------------------------------------------------
1975    // Reflection System (Deep Embedding)
1976    // -------------------------------------------------------------------------
1977
1978    /// Reflection types for deep embedding.
1979    ///
1980    /// Univ : Type 0 (representation of universes)
1981    /// Syntax : Type 0 (representation of terms with De Bruijn indices)
1982    /// syn_size : Syntax -> Int (compute size of syntax tree)
1983    /// syn_max_var : Syntax -> Int (compute max free variable index)
1984    fn register_reflection(ctx: &mut Context) {
1985        Self::register_univ(ctx);
1986        Self::register_syntax(ctx);
1987        Self::register_syn_size(ctx);
1988        Self::register_syn_max_var(ctx);
1989        Self::register_syn_lift(ctx);
1990        Self::register_syn_subst(ctx);
1991        Self::register_syn_beta(ctx);
1992        Self::register_syn_step(ctx);
1993        Self::register_syn_eval(ctx);
1994        Self::register_syn_quote(ctx);
1995        Self::register_syn_diag(ctx);
1996        Self::register_derivation(ctx);
1997        Self::register_concludes(ctx);
1998        Self::register_try_refl(ctx);
1999        Self::register_try_compute(ctx);
2000        Self::register_try_cong(ctx);
2001        Self::register_tact_fail(ctx);
2002        Self::register_tact_orelse(ctx);
2003        Self::register_tact_try(ctx);
2004        Self::register_tact_repeat(ctx);
2005        Self::register_tact_then(ctx);
2006        Self::register_tact_first(ctx);
2007        Self::register_tact_solve(ctx);
2008        Self::register_try_ring(ctx);
2009        Self::register_try_lia(ctx);
2010        Self::register_try_cc(ctx);
2011        Self::register_try_simp(ctx);
2012        Self::register_try_omega(ctx);
2013        Self::register_try_auto(ctx);
2014        Self::register_try_induction(ctx);
2015        Self::register_induction_helpers(ctx);
2016        Self::register_try_inversion_tactic(ctx);
2017        Self::register_operator_tactics(ctx);
2018        Self::register_hw_tactics(ctx);
2019    }
2020
2021    /// Univ : Type 0 (representation of universes)
2022    /// UProp : Univ
2023    /// UType : Int -> Univ
2024    fn register_univ(ctx: &mut Context) {
2025        let univ = Term::Global("Univ".to_string());
2026        let int = Term::Global("Int".to_string());
2027
2028        // Univ : Type 0
2029        ctx.add_inductive("Univ", Term::Sort(Universe::Type(0)));
2030
2031        // UProp : Univ
2032        ctx.add_constructor("UProp", "Univ", univ.clone());
2033
2034        // UType : Int -> Univ
2035        ctx.add_constructor(
2036            "UType",
2037            "Univ",
2038            Term::Pi {
2039                param: "_".to_string(),
2040                param_type: Box::new(int),
2041                body_type: Box::new(univ),
2042            },
2043        );
2044    }
2045
2046    /// Syntax : Type 0 (representation of terms with De Bruijn indices)
2047    ///
2048    /// SVar : Int -> Syntax (De Bruijn variable index)
2049    /// SGlobal : Int -> Syntax (global reference by ID)
2050    /// SSort : Univ -> Syntax (universe)
2051    /// SApp : Syntax -> Syntax -> Syntax (application)
2052    /// SLam : Syntax -> Syntax -> Syntax (lambda: param_type, body)
2053    /// SPi : Syntax -> Syntax -> Syntax (pi: param_type, body_type)
2054    fn register_syntax(ctx: &mut Context) {
2055        let syntax = Term::Global("Syntax".to_string());
2056        let int = Term::Global("Int".to_string());
2057        let univ = Term::Global("Univ".to_string());
2058
2059        // Syntax : Type 0
2060        ctx.add_inductive("Syntax", Term::Sort(Universe::Type(0)));
2061
2062        // SVar : Int -> Syntax (De Bruijn index)
2063        ctx.add_constructor(
2064            "SVar",
2065            "Syntax",
2066            Term::Pi {
2067                param: "_".to_string(),
2068                param_type: Box::new(int.clone()),
2069                body_type: Box::new(syntax.clone()),
2070            },
2071        );
2072
2073        // SGlobal : Int -> Syntax (global reference by ID)
2074        ctx.add_constructor(
2075            "SGlobal",
2076            "Syntax",
2077            Term::Pi {
2078                param: "_".to_string(),
2079                param_type: Box::new(int.clone()),
2080                body_type: Box::new(syntax.clone()),
2081            },
2082        );
2083
2084        // SSort : Univ -> Syntax
2085        ctx.add_constructor(
2086            "SSort",
2087            "Syntax",
2088            Term::Pi {
2089                param: "_".to_string(),
2090                param_type: Box::new(univ),
2091                body_type: Box::new(syntax.clone()),
2092            },
2093        );
2094
2095        // SApp : Syntax -> Syntax -> Syntax
2096        ctx.add_constructor(
2097            "SApp",
2098            "Syntax",
2099            Term::Pi {
2100                param: "_".to_string(),
2101                param_type: Box::new(syntax.clone()),
2102                body_type: Box::new(Term::Pi {
2103                    param: "_".to_string(),
2104                    param_type: Box::new(syntax.clone()),
2105                    body_type: Box::new(syntax.clone()),
2106                }),
2107            },
2108        );
2109
2110        // SLam : Syntax -> Syntax -> Syntax (param_type, body)
2111        ctx.add_constructor(
2112            "SLam",
2113            "Syntax",
2114            Term::Pi {
2115                param: "_".to_string(),
2116                param_type: Box::new(syntax.clone()),
2117                body_type: Box::new(Term::Pi {
2118                    param: "_".to_string(),
2119                    param_type: Box::new(syntax.clone()),
2120                    body_type: Box::new(syntax.clone()),
2121                }),
2122            },
2123        );
2124
2125        // SPi : Syntax -> Syntax -> Syntax (param_type, body_type)
2126        ctx.add_constructor(
2127            "SPi",
2128            "Syntax",
2129            Term::Pi {
2130                param: "_".to_string(),
2131                param_type: Box::new(syntax.clone()),
2132                body_type: Box::new(Term::Pi {
2133                    param: "_".to_string(),
2134                    param_type: Box::new(syntax.clone()),
2135                    body_type: Box::new(syntax.clone()),
2136                }),
2137            },
2138        );
2139
2140        // SLit : Int -> Syntax (integer literal in quoted syntax)
2141        ctx.add_constructor(
2142            "SLit",
2143            "Syntax",
2144            Term::Pi {
2145                param: "_".to_string(),
2146                param_type: Box::new(int),
2147                body_type: Box::new(syntax.clone()),
2148            },
2149        );
2150
2151        // SName : Text -> Syntax (named global reference)
2152        let text = Term::Global("Text".to_string());
2153        ctx.add_constructor(
2154            "SName",
2155            "Syntax",
2156            Term::Pi {
2157                param: "_".to_string(),
2158                param_type: Box::new(text),
2159                body_type: Box::new(syntax),
2160            },
2161        );
2162    }
2163
2164    /// syn_size : Syntax -> Int
2165    ///
2166    /// Computes the number of nodes in a syntax tree.
2167    /// Computational behavior defined in reduction.rs.
2168    fn register_syn_size(ctx: &mut Context) {
2169        let syntax = Term::Global("Syntax".to_string());
2170        let int = Term::Global("Int".to_string());
2171
2172        // syn_size : Syntax -> Int
2173        let syn_size_type = Term::Pi {
2174            param: "_".to_string(),
2175            param_type: Box::new(syntax),
2176            body_type: Box::new(int),
2177        };
2178
2179        ctx.add_declaration("syn_size", syn_size_type);
2180    }
2181
2182    /// syn_max_var : Syntax -> Int
2183    ///
2184    /// Returns the maximum free variable index in a syntax term.
2185    /// Returns -1 if the term is closed (all variables bound).
2186    /// Computational behavior defined in reduction.rs.
2187    fn register_syn_max_var(ctx: &mut Context) {
2188        let syntax = Term::Global("Syntax".to_string());
2189        let int = Term::Global("Int".to_string());
2190
2191        let syn_max_var_type = Term::Pi {
2192            param: "_".to_string(),
2193            param_type: Box::new(syntax),
2194            body_type: Box::new(int),
2195        };
2196
2197        ctx.add_declaration("syn_max_var", syn_max_var_type);
2198    }
2199
2200    // -------------------------------------------------------------------------
2201    // De Bruijn Operations (Substitution)
2202    // -------------------------------------------------------------------------
2203
2204    /// syn_lift : Int -> Int -> Syntax -> Syntax
2205    ///
2206    /// Shifts free variables (index >= cutoff) by amount.
2207    /// - syn_lift amount cutoff term
2208    /// - Variables with index < cutoff are bound -> unchanged
2209    /// - Variables with index >= cutoff are free -> add amount
2210    /// Computational behavior defined in reduction.rs.
2211    fn register_syn_lift(ctx: &mut Context) {
2212        let syntax = Term::Global("Syntax".to_string());
2213        let int = Term::Global("Int".to_string());
2214
2215        // syn_lift : Int -> Int -> Syntax -> Syntax
2216        let syn_lift_type = Term::Pi {
2217            param: "_".to_string(),
2218            param_type: Box::new(int.clone()),
2219            body_type: Box::new(Term::Pi {
2220                param: "_".to_string(),
2221                param_type: Box::new(int),
2222                body_type: Box::new(Term::Pi {
2223                    param: "_".to_string(),
2224                    param_type: Box::new(syntax.clone()),
2225                    body_type: Box::new(syntax),
2226                }),
2227            }),
2228        };
2229
2230        ctx.add_declaration("syn_lift", syn_lift_type);
2231    }
2232
2233    /// syn_subst : Syntax -> Int -> Syntax -> Syntax
2234    ///
2235    /// Substitutes replacement for variable at index in term.
2236    /// - syn_subst replacement index term
2237    /// - If term is SVar k and k == index, return replacement
2238    /// - If term is SVar k and k != index, return term unchanged
2239    /// - For binders, increment index and lift replacement
2240    /// Computational behavior defined in reduction.rs.
2241    fn register_syn_subst(ctx: &mut Context) {
2242        let syntax = Term::Global("Syntax".to_string());
2243        let int = Term::Global("Int".to_string());
2244
2245        // syn_subst : Syntax -> Int -> Syntax -> Syntax
2246        let syn_subst_type = Term::Pi {
2247            param: "_".to_string(),
2248            param_type: Box::new(syntax.clone()),
2249            body_type: Box::new(Term::Pi {
2250                param: "_".to_string(),
2251                param_type: Box::new(int),
2252                body_type: Box::new(Term::Pi {
2253                    param: "_".to_string(),
2254                    param_type: Box::new(syntax.clone()),
2255                    body_type: Box::new(syntax),
2256                }),
2257            }),
2258        };
2259
2260        ctx.add_declaration("syn_subst", syn_subst_type);
2261    }
2262
2263    // -------------------------------------------------------------------------
2264    // Beta Reduction (Computation)
2265    // -------------------------------------------------------------------------
2266
2267    /// syn_beta : Syntax -> Syntax -> Syntax
2268    ///
2269    /// Beta reduction: substitute arg for variable 0 in body, then decrement the
2270    /// surviving free variables (the λ binder is removed, so references past it
2271    /// drop one de Bruijn level). `syn_beta body arg = syn_subst arg 0 body`
2272    /// composed with that shift — a FAITHFUL reflection of real beta (unlike the
2273    /// raw, non-shifting `syn_subst` primitive).
2274    /// Computational behavior defined in reduction.rs.
2275    fn register_syn_beta(ctx: &mut Context) {
2276        let syntax = Term::Global("Syntax".to_string());
2277
2278        // syn_beta : Syntax -> Syntax -> Syntax
2279        let syn_beta_type = Term::Pi {
2280            param: "_".to_string(),
2281            param_type: Box::new(syntax.clone()),
2282            body_type: Box::new(Term::Pi {
2283                param: "_".to_string(),
2284                param_type: Box::new(syntax.clone()),
2285                body_type: Box::new(syntax),
2286            }),
2287        };
2288
2289        ctx.add_declaration("syn_beta", syn_beta_type);
2290    }
2291
2292    /// syn_step : Syntax -> Syntax
2293    ///
2294    /// Single-step head reduction. Finds first beta redex and reduces.
2295    /// - If SApp (SLam T body) arg: perform beta reduction
2296    /// - If SApp f x where f is reducible: reduce f
2297    /// - Otherwise: return unchanged (stuck or value)
2298    /// Computational behavior defined in reduction.rs.
2299    fn register_syn_step(ctx: &mut Context) {
2300        let syntax = Term::Global("Syntax".to_string());
2301
2302        // syn_step : Syntax -> Syntax
2303        let syn_step_type = Term::Pi {
2304            param: "_".to_string(),
2305            param_type: Box::new(syntax.clone()),
2306            body_type: Box::new(syntax),
2307        };
2308
2309        ctx.add_declaration("syn_step", syn_step_type);
2310    }
2311
2312    // -------------------------------------------------------------------------
2313    // Bounded Evaluation
2314    // -------------------------------------------------------------------------
2315
2316    /// syn_eval : Int -> Syntax -> Syntax
2317    ///
2318    /// Bounded evaluation: reduce for up to N steps.
2319    /// - syn_eval fuel term
2320    /// - If fuel <= 0: return term unchanged
2321    /// - Otherwise: step and repeat until normal form or fuel exhausted
2322    /// Computational behavior defined in reduction.rs.
2323    fn register_syn_eval(ctx: &mut Context) {
2324        let syntax = Term::Global("Syntax".to_string());
2325        let int = Term::Global("Int".to_string());
2326
2327        // syn_eval : Int -> Syntax -> Syntax
2328        let syn_eval_type = Term::Pi {
2329            param: "_".to_string(),
2330            param_type: Box::new(int),
2331            body_type: Box::new(Term::Pi {
2332                param: "_".to_string(),
2333                param_type: Box::new(syntax.clone()),
2334                body_type: Box::new(syntax),
2335            }),
2336        };
2337
2338        ctx.add_declaration("syn_eval", syn_eval_type);
2339    }
2340
2341    // -------------------------------------------------------------------------
2342    // Reification (Quote)
2343    // -------------------------------------------------------------------------
2344
2345    /// syn_quote : Syntax -> Syntax
2346    ///
2347    /// Converts a Syntax value to Syntax code that constructs it.
2348    /// Computational behavior defined in reduction.rs.
2349    fn register_syn_quote(ctx: &mut Context) {
2350        let syntax = Term::Global("Syntax".to_string());
2351
2352        // syn_quote : Syntax -> Syntax
2353        let syn_quote_type = Term::Pi {
2354            param: "_".to_string(),
2355            param_type: Box::new(syntax.clone()),
2356            body_type: Box::new(syntax),
2357        };
2358
2359        ctx.add_declaration("syn_quote", syn_quote_type);
2360    }
2361
2362    // -------------------------------------------------------------------------
2363    // Diagonalization (Self-Reference)
2364    // -------------------------------------------------------------------------
2365
2366    /// syn_diag : Syntax -> Syntax
2367    ///
2368    /// The diagonal function: syn_diag x = syn_subst (syn_quote x) 0 x
2369    /// This is the key construction for self-reference and the diagonal lemma.
2370    fn register_syn_diag(ctx: &mut Context) {
2371        let syntax = Term::Global("Syntax".to_string());
2372
2373        let syn_diag_type = Term::Pi {
2374            param: "_".to_string(),
2375            param_type: Box::new(syntax.clone()),
2376            body_type: Box::new(syntax),
2377        };
2378
2379        ctx.add_declaration("syn_diag", syn_diag_type);
2380    }
2381
2382    // -------------------------------------------------------------------------
2383    // Inference Rules (Proof Trees)
2384    // -------------------------------------------------------------------------
2385
2386    /// Derivation : Type 0 (deep embedding of proof trees)
2387    ///
2388    /// DAxiom : Syntax -> Derivation (introduce an axiom)
2389    /// DModusPonens : Derivation -> Derivation -> Derivation (A->B, A |- B)
2390    /// DUnivIntro : Derivation -> Derivation (P(x) |- forall x. P(x))
2391    /// DUnivElim : Derivation -> Syntax -> Derivation (forall x. P(x), t |- P(t))
2392    fn register_derivation(ctx: &mut Context) {
2393        let syntax = Term::Global("Syntax".to_string());
2394        let derivation = Term::Global("Derivation".to_string());
2395
2396        // Derivation : Type 0
2397        ctx.add_inductive("Derivation", Term::Sort(Universe::Type(0)));
2398
2399        // DAxiom : Syntax -> Derivation
2400        ctx.add_constructor(
2401            "DAxiom",
2402            "Derivation",
2403            Term::Pi {
2404                param: "_".to_string(),
2405                param_type: Box::new(syntax.clone()),
2406                body_type: Box::new(derivation.clone()),
2407            },
2408        );
2409
2410        // DModusPonens : Derivation -> Derivation -> Derivation
2411        ctx.add_constructor(
2412            "DModusPonens",
2413            "Derivation",
2414            Term::Pi {
2415                param: "_".to_string(),
2416                param_type: Box::new(derivation.clone()),
2417                body_type: Box::new(Term::Pi {
2418                    param: "_".to_string(),
2419                    param_type: Box::new(derivation.clone()),
2420                    body_type: Box::new(derivation.clone()),
2421                }),
2422            },
2423        );
2424
2425        // DUnivIntro : Derivation -> Derivation
2426        ctx.add_constructor(
2427            "DUnivIntro",
2428            "Derivation",
2429            Term::Pi {
2430                param: "_".to_string(),
2431                param_type: Box::new(derivation.clone()),
2432                body_type: Box::new(derivation.clone()),
2433            },
2434        );
2435
2436        // DUnivElim : Derivation -> Syntax -> Derivation
2437        ctx.add_constructor(
2438            "DUnivElim",
2439            "Derivation",
2440            Term::Pi {
2441                param: "_".to_string(),
2442                param_type: Box::new(derivation.clone()),
2443                body_type: Box::new(Term::Pi {
2444                    param: "_".to_string(),
2445                    param_type: Box::new(syntax.clone()),
2446                    body_type: Box::new(derivation.clone()),
2447                }),
2448            },
2449        );
2450
2451        // DRefl : Syntax -> Syntax -> Derivation
2452        // DRefl T a proves (Eq T a a)
2453        ctx.add_constructor(
2454            "DRefl",
2455            "Derivation",
2456            Term::Pi {
2457                param: "_".to_string(),
2458                param_type: Box::new(syntax.clone()),
2459                body_type: Box::new(Term::Pi {
2460                    param: "_".to_string(),
2461                    param_type: Box::new(syntax.clone()),
2462                    body_type: Box::new(derivation.clone()),
2463                }),
2464            },
2465        );
2466
2467        // DInduction : Syntax -> Derivation -> Derivation -> Derivation
2468        // DInduction motive base step proves (∀n:Nat. motive n) via induction
2469        // - motive: λn:Nat. P(n) as Syntax
2470        // - base: proof of P(Zero)
2471        // - step: proof of ∀k:Nat. P(k) → P(Succ k)
2472        ctx.add_constructor(
2473            "DInduction",
2474            "Derivation",
2475            Term::Pi {
2476                param: "_".to_string(),
2477                param_type: Box::new(syntax.clone()), // motive
2478                body_type: Box::new(Term::Pi {
2479                    param: "_".to_string(),
2480                    param_type: Box::new(derivation.clone()), // base proof
2481                    body_type: Box::new(Term::Pi {
2482                        param: "_".to_string(),
2483                        param_type: Box::new(derivation.clone()), // step proof
2484                        body_type: Box::new(derivation.clone()),
2485                    }),
2486                }),
2487            },
2488        );
2489
2490        // DCompute : Syntax -> Derivation
2491        // DCompute goal proves goal by computation
2492        // - Validates that goal is (Eq T A B) and eval(A) == eval(B)
2493        ctx.add_constructor(
2494            "DCompute",
2495            "Derivation",
2496            Term::Pi {
2497                param: "_".to_string(),
2498                param_type: Box::new(syntax.clone()),
2499                body_type: Box::new(derivation.clone()),
2500            },
2501        );
2502
2503        // DCong : Syntax -> Derivation -> Derivation
2504        // DCong context eq_proof proves congruence
2505        // - context: SLam T body (function with hole at SVar 0)
2506        // - eq_proof: proof of (Eq T a b)
2507        // - Result: proof of (Eq T (body[0:=a]) (body[0:=b]))
2508        ctx.add_constructor(
2509            "DCong",
2510            "Derivation",
2511            Term::Pi {
2512                param: "_".to_string(),
2513                param_type: Box::new(syntax.clone()), // context
2514                body_type: Box::new(Term::Pi {
2515                    param: "_".to_string(),
2516                    param_type: Box::new(derivation.clone()), // eq_proof
2517                    body_type: Box::new(derivation.clone()),
2518                }),
2519            },
2520        );
2521
2522        // DCase : Derivation -> Derivation -> Derivation
2523        // Cons cell for case proofs in DElim
2524        ctx.add_constructor(
2525            "DCase",
2526            "Derivation",
2527            Term::Pi {
2528                param: "_".to_string(),
2529                param_type: Box::new(derivation.clone()), // head (case proof)
2530                body_type: Box::new(Term::Pi {
2531                    param: "_".to_string(),
2532                    param_type: Box::new(derivation.clone()), // tail (rest of cases)
2533                    body_type: Box::new(derivation.clone()),
2534                }),
2535            },
2536        );
2537
2538        // DCaseEnd : Derivation
2539        // Nil for case proofs in DElim
2540        ctx.add_constructor("DCaseEnd", "Derivation", derivation.clone());
2541
2542        // DElim : Syntax -> Syntax -> Derivation -> Derivation
2543        // Generic elimination for any inductive type
2544        // - ind_type: the inductive type (e.g., SName "Nat" or SApp (SName "List") A)
2545        // - motive: SLam param_type body (the property to prove)
2546        // - cases: DCase chain with one proof per constructor
2547        ctx.add_constructor(
2548            "DElim",
2549            "Derivation",
2550            Term::Pi {
2551                param: "_".to_string(),
2552                param_type: Box::new(syntax.clone()), // ind_type
2553                body_type: Box::new(Term::Pi {
2554                    param: "_".to_string(),
2555                    param_type: Box::new(syntax.clone()), // motive
2556                    body_type: Box::new(Term::Pi {
2557                        param: "_".to_string(),
2558                        param_type: Box::new(derivation.clone()), // cases
2559                        body_type: Box::new(derivation.clone()),
2560                    }),
2561                }),
2562            },
2563        );
2564
2565        // DInversion : Syntax -> Derivation
2566        // Proves False when no constructor can build the given hypothesis.
2567        // - hyp_type: the Syntax representation of the hypothesis (e.g., SApp (SName "Even") three)
2568        // - Returns proof of False if verified that all constructors are impossible
2569        ctx.add_constructor(
2570            "DInversion",
2571            "Derivation",
2572            Term::Pi {
2573                param: "_".to_string(),
2574                param_type: Box::new(syntax.clone()),
2575                body_type: Box::new(derivation.clone()),
2576            },
2577        );
2578
2579        // DRewrite : Derivation -> Syntax -> Syntax -> Derivation
2580        // Stores: eq_proof, original_goal, new_goal
2581        // Given eq_proof : Eq A x y, rewrites goal by replacing x with y
2582        ctx.add_constructor(
2583            "DRewrite",
2584            "Derivation",
2585            Term::Pi {
2586                param: "_".to_string(),
2587                param_type: Box::new(derivation.clone()), // eq_proof
2588                body_type: Box::new(Term::Pi {
2589                    param: "_".to_string(),
2590                    param_type: Box::new(syntax.clone()), // original_goal
2591                    body_type: Box::new(Term::Pi {
2592                        param: "_".to_string(),
2593                        param_type: Box::new(syntax.clone()), // new_goal
2594                        body_type: Box::new(derivation.clone()),
2595                    }),
2596                }),
2597            },
2598        );
2599
2600        // DDestruct : Syntax -> Syntax -> Derivation -> Derivation
2601        // Case analysis without induction hypotheses
2602        // - ind_type: the inductive type
2603        // - motive: the property to prove
2604        // - cases: DCase chain with proofs for each constructor
2605        ctx.add_constructor(
2606            "DDestruct",
2607            "Derivation",
2608            Term::Pi {
2609                param: "_".to_string(),
2610                param_type: Box::new(syntax.clone()), // ind_type
2611                body_type: Box::new(Term::Pi {
2612                    param: "_".to_string(),
2613                    param_type: Box::new(syntax.clone()), // motive
2614                    body_type: Box::new(Term::Pi {
2615                        param: "_".to_string(),
2616                        param_type: Box::new(derivation.clone()), // cases
2617                        body_type: Box::new(derivation.clone()),
2618                    }),
2619                }),
2620            },
2621        );
2622
2623        // DApply : Syntax -> Derivation -> Syntax -> Syntax -> Derivation
2624        // Manual backward chaining
2625        // - hyp_name: name of hypothesis
2626        // - hyp_proof: proof of the hypothesis
2627        // - original_goal: the goal we started with
2628        // - new_goal: the antecedent we need to prove
2629        ctx.add_constructor(
2630            "DApply",
2631            "Derivation",
2632            Term::Pi {
2633                param: "_".to_string(),
2634                param_type: Box::new(syntax.clone()), // hyp_name
2635                body_type: Box::new(Term::Pi {
2636                    param: "_".to_string(),
2637                    param_type: Box::new(derivation.clone()), // hyp_proof
2638                    body_type: Box::new(Term::Pi {
2639                        param: "_".to_string(),
2640                        param_type: Box::new(syntax.clone()), // original_goal
2641                        body_type: Box::new(Term::Pi {
2642                            param: "_".to_string(),
2643                            param_type: Box::new(syntax), // new_goal
2644                            body_type: Box::new(derivation),
2645                        }),
2646                    }),
2647                }),
2648            },
2649        );
2650    }
2651
2652    /// concludes : Derivation -> Syntax
2653    ///
2654    /// Extracts the conclusion from a derivation.
2655    /// Computational behavior defined in reduction.rs.
2656    fn register_concludes(ctx: &mut Context) {
2657        let derivation = Term::Global("Derivation".to_string());
2658        let syntax = Term::Global("Syntax".to_string());
2659
2660        // concludes : Derivation -> Syntax
2661        let concludes_type = Term::Pi {
2662            param: "_".to_string(),
2663            param_type: Box::new(derivation),
2664            body_type: Box::new(syntax),
2665        };
2666
2667        ctx.add_declaration("concludes", concludes_type);
2668    }
2669
2670    // -------------------------------------------------------------------------
2671    // Core Tactics
2672    // -------------------------------------------------------------------------
2673
2674    /// try_refl : Syntax -> Derivation
2675    ///
2676    /// Reflexivity tactic: given a goal, try to prove it by reflexivity.
2677    /// - If goal matches (Eq T a b) and a == b, returns DRefl T a
2678    /// - Otherwise returns DAxiom (SName "Error")
2679    /// Computational behavior defined in reduction.rs.
2680    fn register_try_refl(ctx: &mut Context) {
2681        let syntax = Term::Global("Syntax".to_string());
2682        let derivation = Term::Global("Derivation".to_string());
2683
2684        // try_refl : Syntax -> Derivation
2685        let try_refl_type = Term::Pi {
2686            param: "_".to_string(),
2687            param_type: Box::new(syntax),
2688            body_type: Box::new(derivation),
2689        };
2690
2691        ctx.add_declaration("try_refl", try_refl_type);
2692    }
2693
2694    /// try_compute : Syntax -> Derivation
2695    ///
2696    /// Computation tactic: given a goal (Eq T A B), proves it by evaluating
2697    /// both sides and checking equality.
2698    /// Returns DCompute goal.
2699    /// Computational behavior defined in reduction.rs.
2700    fn register_try_compute(ctx: &mut Context) {
2701        let syntax = Term::Global("Syntax".to_string());
2702        let derivation = Term::Global("Derivation".to_string());
2703
2704        // try_compute : Syntax -> Derivation
2705        let try_compute_type = Term::Pi {
2706            param: "_".to_string(),
2707            param_type: Box::new(syntax),
2708            body_type: Box::new(derivation),
2709        };
2710
2711        ctx.add_declaration("try_compute", try_compute_type);
2712    }
2713
2714    /// try_cong : Syntax -> Derivation -> Derivation
2715    ///
2716    /// Congruence tactic: given a context (SLam T body) and proof of (Eq T a b),
2717    /// produces proof of (Eq T (body[0:=a]) (body[0:=b])).
2718    /// Returns DCong context eq_proof.
2719    /// Computational behavior defined in reduction.rs.
2720    fn register_try_cong(ctx: &mut Context) {
2721        let syntax = Term::Global("Syntax".to_string());
2722        let derivation = Term::Global("Derivation".to_string());
2723
2724        // try_cong : Syntax -> Derivation -> Derivation
2725        let try_cong_type = Term::Pi {
2726            param: "_".to_string(),
2727            param_type: Box::new(syntax), // context
2728            body_type: Box::new(Term::Pi {
2729                param: "_".to_string(),
2730                param_type: Box::new(derivation.clone()), // eq_proof
2731                body_type: Box::new(derivation),
2732            }),
2733        };
2734
2735        ctx.add_declaration("try_cong", try_cong_type);
2736    }
2737
2738    // -------------------------------------------------------------------------
2739    // Tactic Combinators
2740    // -------------------------------------------------------------------------
2741
2742    /// tact_fail : Syntax -> Derivation
2743    ///
2744    /// A tactic that always fails by returning DAxiom (SName "Error").
2745    /// Useful for testing combinators and as a base case.
2746    /// Computational behavior defined in reduction.rs.
2747    fn register_tact_fail(ctx: &mut Context) {
2748        let syntax = Term::Global("Syntax".to_string());
2749        let derivation = Term::Global("Derivation".to_string());
2750
2751        // tact_fail : Syntax -> Derivation
2752        let tact_fail_type = Term::Pi {
2753            param: "_".to_string(),
2754            param_type: Box::new(syntax),
2755            body_type: Box::new(derivation),
2756        };
2757
2758        ctx.add_declaration("tact_fail", tact_fail_type);
2759    }
2760
2761    /// tact_orelse : (Syntax -> Derivation) -> (Syntax -> Derivation) -> Syntax -> Derivation
2762    ///
2763    /// Tactic combinator: try first tactic, if it fails (concludes to Error) try second.
2764    /// Enables composition of tactics with fallback behavior.
2765    /// Computational behavior defined in reduction.rs.
2766    fn register_tact_orelse(ctx: &mut Context) {
2767        let syntax = Term::Global("Syntax".to_string());
2768        let derivation = Term::Global("Derivation".to_string());
2769
2770        // Tactic type: Syntax -> Derivation
2771        let tactic_type = Term::Pi {
2772            param: "_".to_string(),
2773            param_type: Box::new(syntax.clone()),
2774            body_type: Box::new(derivation.clone()),
2775        };
2776
2777        // tact_orelse : (Syntax -> Derivation) -> (Syntax -> Derivation) -> Syntax -> Derivation
2778        let tact_orelse_type = Term::Pi {
2779            param: "_".to_string(),
2780            param_type: Box::new(tactic_type.clone()), // t1
2781            body_type: Box::new(Term::Pi {
2782                param: "_".to_string(),
2783                param_type: Box::new(tactic_type), // t2
2784                body_type: Box::new(Term::Pi {
2785                    param: "_".to_string(),
2786                    param_type: Box::new(syntax), // goal
2787                    body_type: Box::new(derivation),
2788                }),
2789            }),
2790        };
2791
2792        ctx.add_declaration("tact_orelse", tact_orelse_type);
2793    }
2794
2795    /// tact_try : (Syntax -> Derivation) -> Syntax -> Derivation
2796    ///
2797    /// Tactic combinator: try the tactic, but never fail.
2798    /// If the tactic fails (concludes to Error), return identity (DAxiom goal).
2799    /// Computational behavior defined in reduction.rs.
2800    fn register_tact_try(ctx: &mut Context) {
2801        let syntax = Term::Global("Syntax".to_string());
2802        let derivation = Term::Global("Derivation".to_string());
2803
2804        // Tactic type: Syntax -> Derivation
2805        let tactic_type = Term::Pi {
2806            param: "_".to_string(),
2807            param_type: Box::new(syntax.clone()),
2808            body_type: Box::new(derivation.clone()),
2809        };
2810
2811        // tact_try : (Syntax -> Derivation) -> Syntax -> Derivation
2812        let tact_try_type = Term::Pi {
2813            param: "_".to_string(),
2814            param_type: Box::new(tactic_type), // t
2815            body_type: Box::new(Term::Pi {
2816                param: "_".to_string(),
2817                param_type: Box::new(syntax),
2818                body_type: Box::new(derivation),
2819            }),
2820        };
2821
2822        ctx.add_declaration("tact_try", tact_try_type);
2823    }
2824
2825    /// tact_repeat : (Syntax -> Derivation) -> Syntax -> Derivation
2826    ///
2827    /// Tactic combinator: apply tactic repeatedly until it fails or makes no progress.
2828    /// Returns identity if tactic fails immediately.
2829    /// Computational behavior defined in reduction.rs.
2830    fn register_tact_repeat(ctx: &mut Context) {
2831        let syntax = Term::Global("Syntax".to_string());
2832        let derivation = Term::Global("Derivation".to_string());
2833
2834        // Tactic type: Syntax -> Derivation
2835        let tactic_type = Term::Pi {
2836            param: "_".to_string(),
2837            param_type: Box::new(syntax.clone()),
2838            body_type: Box::new(derivation.clone()),
2839        };
2840
2841        // tact_repeat : (Syntax -> Derivation) -> Syntax -> Derivation
2842        let tact_repeat_type = Term::Pi {
2843            param: "_".to_string(),
2844            param_type: Box::new(tactic_type), // t
2845            body_type: Box::new(Term::Pi {
2846                param: "_".to_string(),
2847                param_type: Box::new(syntax),
2848                body_type: Box::new(derivation),
2849            }),
2850        };
2851
2852        ctx.add_declaration("tact_repeat", tact_repeat_type);
2853    }
2854
2855    /// tact_then : (Syntax -> Derivation) -> (Syntax -> Derivation) -> Syntax -> Derivation
2856    ///
2857    /// Tactic combinator: sequence two tactics (the ";" operator).
2858    /// Apply t1 to goal, then apply t2 to the result.
2859    /// Fails if either tactic fails.
2860    /// Computational behavior defined in reduction.rs.
2861    fn register_tact_then(ctx: &mut Context) {
2862        let syntax = Term::Global("Syntax".to_string());
2863        let derivation = Term::Global("Derivation".to_string());
2864
2865        // Tactic type: Syntax -> Derivation
2866        let tactic_type = Term::Pi {
2867            param: "_".to_string(),
2868            param_type: Box::new(syntax.clone()),
2869            body_type: Box::new(derivation.clone()),
2870        };
2871
2872        // tact_then : (Syntax -> Derivation) -> (Syntax -> Derivation) -> Syntax -> Derivation
2873        let tact_then_type = Term::Pi {
2874            param: "_".to_string(),
2875            param_type: Box::new(tactic_type.clone()), // t1
2876            body_type: Box::new(Term::Pi {
2877                param: "_".to_string(),
2878                param_type: Box::new(tactic_type), // t2
2879                body_type: Box::new(Term::Pi {
2880                    param: "_".to_string(),
2881                    param_type: Box::new(syntax),
2882                    body_type: Box::new(derivation),
2883                }),
2884            }),
2885        };
2886
2887        ctx.add_declaration("tact_then", tact_then_type);
2888    }
2889
2890    /// tact_first : TList (Syntax -> Derivation) -> Syntax -> Derivation
2891    ///
2892    /// Tactic combinator: try tactics from a list until one succeeds.
2893    /// Returns Error if all tactics fail or list is empty.
2894    /// Computational behavior defined in reduction.rs.
2895    fn register_tact_first(ctx: &mut Context) {
2896        let syntax = Term::Global("Syntax".to_string());
2897        let derivation = Term::Global("Derivation".to_string());
2898
2899        // Tactic type: Syntax -> Derivation
2900        let tactic_type = Term::Pi {
2901            param: "_".to_string(),
2902            param_type: Box::new(syntax.clone()),
2903            body_type: Box::new(derivation.clone()),
2904        };
2905
2906        // TList (Syntax -> Derivation)
2907        let tactic_list_type = Term::App(
2908            Box::new(Term::Global("TList".to_string())),
2909            Box::new(tactic_type),
2910        );
2911
2912        // tact_first : TList (Syntax -> Derivation) -> Syntax -> Derivation
2913        let tact_first_type = Term::Pi {
2914            param: "_".to_string(),
2915            param_type: Box::new(tactic_list_type), // tactics
2916            body_type: Box::new(Term::Pi {
2917                param: "_".to_string(),
2918                param_type: Box::new(syntax),
2919                body_type: Box::new(derivation),
2920            }),
2921        };
2922
2923        ctx.add_declaration("tact_first", tact_first_type);
2924    }
2925
2926    /// tact_solve : (Syntax -> Derivation) -> Syntax -> Derivation
2927    ///
2928    /// Tactic combinator: tactic MUST completely solve the goal.
2929    /// If tactic returns Error, returns Error.
2930    /// If tactic returns a proof, returns that proof.
2931    /// Computational behavior defined in reduction.rs.
2932    fn register_tact_solve(ctx: &mut Context) {
2933        let syntax = Term::Global("Syntax".to_string());
2934        let derivation = Term::Global("Derivation".to_string());
2935
2936        // Tactic type: Syntax -> Derivation
2937        let tactic_type = Term::Pi {
2938            param: "_".to_string(),
2939            param_type: Box::new(syntax.clone()),
2940            body_type: Box::new(derivation.clone()),
2941        };
2942
2943        // tact_solve : (Syntax -> Derivation) -> Syntax -> Derivation
2944        let tact_solve_type = Term::Pi {
2945            param: "_".to_string(),
2946            param_type: Box::new(tactic_type), // t
2947            body_type: Box::new(Term::Pi {
2948                param: "_".to_string(),
2949                param_type: Box::new(syntax),
2950                body_type: Box::new(derivation),
2951            }),
2952        };
2953
2954        ctx.add_declaration("tact_solve", tact_solve_type);
2955    }
2956
2957    // -------------------------------------------------------------------------
2958    // Ring Tactic (Polynomial Equality)
2959    // -------------------------------------------------------------------------
2960
2961    /// DRingSolve : Syntax -> Derivation
2962    /// try_ring : Syntax -> Derivation
2963    ///
2964    /// Ring tactic: proves polynomial equalities by normalization.
2965    /// Computational behavior defined in reduction.rs.
2966    fn register_try_ring(ctx: &mut Context) {
2967        let syntax = Term::Global("Syntax".to_string());
2968        let derivation = Term::Global("Derivation".to_string());
2969
2970        // DRingSolve : Syntax -> Derivation
2971        // Proof constructor for ring-solved equalities
2972        ctx.add_constructor(
2973            "DRingSolve",
2974            "Derivation",
2975            Term::Pi {
2976                param: "_".to_string(),
2977                param_type: Box::new(syntax.clone()),
2978                body_type: Box::new(derivation.clone()),
2979            },
2980        );
2981
2982        // try_ring : Syntax -> Derivation
2983        // Ring tactic: given a goal, try to prove it by polynomial normalization
2984        let try_ring_type = Term::Pi {
2985            param: "_".to_string(),
2986            param_type: Box::new(syntax),
2987            body_type: Box::new(derivation),
2988        };
2989
2990        ctx.add_declaration("try_ring", try_ring_type);
2991    }
2992
2993    // -------------------------------------------------------------------------
2994    // LIA Tactic (Linear Integer Arithmetic)
2995    // -------------------------------------------------------------------------
2996
2997    /// DLiaSolve : Syntax -> Derivation
2998    /// try_lia : Syntax -> Derivation
2999    ///
3000    /// LIA tactic: proves linear inequalities by Fourier-Motzkin elimination.
3001    /// Computational behavior defined in reduction.rs.
3002    fn register_try_lia(ctx: &mut Context) {
3003        let syntax = Term::Global("Syntax".to_string());
3004        let derivation = Term::Global("Derivation".to_string());
3005
3006        // DLiaSolve : Syntax -> Derivation
3007        // Proof constructor for LIA-solved inequalities
3008        ctx.add_constructor(
3009            "DLiaSolve",
3010            "Derivation",
3011            Term::Pi {
3012                param: "_".to_string(),
3013                param_type: Box::new(syntax.clone()),
3014                body_type: Box::new(derivation.clone()),
3015            },
3016        );
3017
3018        // try_lia : Syntax -> Derivation
3019        // LIA tactic: given a goal, try to prove it by linear arithmetic
3020        let try_lia_type = Term::Pi {
3021            param: "_".to_string(),
3022            param_type: Box::new(syntax),
3023            body_type: Box::new(derivation),
3024        };
3025
3026        ctx.add_declaration("try_lia", try_lia_type);
3027    }
3028
3029    /// DccSolve : Syntax -> Derivation
3030    /// try_cc : Syntax -> Derivation
3031    ///
3032    /// Congruence Closure tactic: proves equalities over uninterpreted functions.
3033    /// Handles hypotheses via implications: (implies (Eq x y) (Eq (f x) (f y)))
3034    /// Computational behavior defined in reduction.rs.
3035    fn register_try_cc(ctx: &mut Context) {
3036        let syntax = Term::Global("Syntax".to_string());
3037        let derivation = Term::Global("Derivation".to_string());
3038
3039        // DccSolve : Syntax -> Derivation
3040        // Proof constructor for congruence closure proofs
3041        ctx.add_constructor(
3042            "DccSolve",
3043            "Derivation",
3044            Term::Pi {
3045                param: "_".to_string(),
3046                param_type: Box::new(syntax.clone()),
3047                body_type: Box::new(derivation.clone()),
3048            },
3049        );
3050
3051        // try_cc : Syntax -> Derivation
3052        // CC tactic: given a goal, try to prove it by congruence closure
3053        let try_cc_type = Term::Pi {
3054            param: "_".to_string(),
3055            param_type: Box::new(syntax),
3056            body_type: Box::new(derivation),
3057        };
3058
3059        ctx.add_declaration("try_cc", try_cc_type);
3060    }
3061
3062    /// DSimpSolve : Syntax -> Derivation
3063    /// try_simp : Syntax -> Derivation
3064    ///
3065    /// Simplifier tactic: proves equalities by term rewriting.
3066    /// Uses bottom-up rewriting with arithmetic evaluation and hypothesis substitution.
3067    /// Handles: reflexivity, constant folding (2+3=5), and hypothesis-based substitution.
3068    /// Computational behavior defined in reduction.rs.
3069    fn register_try_simp(ctx: &mut Context) {
3070        let syntax = Term::Global("Syntax".to_string());
3071        let derivation = Term::Global("Derivation".to_string());
3072
3073        // DSimpSolve : Syntax -> Derivation
3074        // Proof constructor for simplifier proofs
3075        ctx.add_constructor(
3076            "DSimpSolve",
3077            "Derivation",
3078            Term::Pi {
3079                param: "_".to_string(),
3080                param_type: Box::new(syntax.clone()),
3081                body_type: Box::new(derivation.clone()),
3082            },
3083        );
3084
3085        // try_simp : Syntax -> Derivation
3086        // Simp tactic: given a goal, try to prove it by simplification
3087        let try_simp_type = Term::Pi {
3088            param: "_".to_string(),
3089            param_type: Box::new(syntax),
3090            body_type: Box::new(derivation),
3091        };
3092
3093        ctx.add_declaration("try_simp", try_simp_type);
3094    }
3095
3096    /// DOmegaSolve : Syntax -> Derivation
3097    /// try_omega : Syntax -> Derivation
3098    ///
3099    /// Omega tactic: proves linear integer arithmetic with proper floor/ceil rounding.
3100    /// Unlike lia (which uses rationals), omega handles integers correctly:
3101    /// - x > 1 means x >= 2 (strict-to-nonstrict conversion)
3102    /// - 3x <= 10 means x <= 3 (floor division)
3103    /// Computational behavior defined in reduction.rs.
3104    fn register_try_omega(ctx: &mut Context) {
3105        let syntax = Term::Global("Syntax".to_string());
3106        let derivation = Term::Global("Derivation".to_string());
3107
3108        // DOmegaSolve : Syntax -> Derivation
3109        // Proof constructor for omega-solved inequalities
3110        ctx.add_constructor(
3111            "DOmegaSolve",
3112            "Derivation",
3113            Term::Pi {
3114                param: "_".to_string(),
3115                param_type: Box::new(syntax.clone()),
3116                body_type: Box::new(derivation.clone()),
3117            },
3118        );
3119
3120        // try_omega : Syntax -> Derivation
3121        // Omega tactic: given a goal, try to prove it by integer arithmetic
3122        let try_omega_type = Term::Pi {
3123            param: "_".to_string(),
3124            param_type: Box::new(syntax),
3125            body_type: Box::new(derivation),
3126        };
3127
3128        ctx.add_declaration("try_omega", try_omega_type);
3129    }
3130
3131    /// DAutoSolve : Syntax -> Derivation
3132    /// try_auto : Syntax -> Derivation
3133    ///
3134    /// Auto tactic: tries all decision procedures in sequence.
3135    /// Order: simp → ring → cc → omega → lia
3136    /// Returns the first successful derivation, or error if all fail.
3137    /// Computational behavior defined in reduction.rs.
3138    fn register_try_auto(ctx: &mut Context) {
3139        let syntax = Term::Global("Syntax".to_string());
3140        let derivation = Term::Global("Derivation".to_string());
3141
3142        // DAutoSolve : Syntax -> Derivation
3143        // Proof constructor for auto-solved goals
3144        ctx.add_constructor(
3145            "DAutoSolve",
3146            "Derivation",
3147            Term::Pi {
3148                param: "_".to_string(),
3149                param_type: Box::new(syntax.clone()),
3150                body_type: Box::new(derivation.clone()),
3151            },
3152        );
3153
3154        // try_auto : Syntax -> Derivation
3155        // Auto tactic: given a goal, try all tactics in sequence
3156        let try_auto_type = Term::Pi {
3157            param: "_".to_string(),
3158            param_type: Box::new(syntax),
3159            body_type: Box::new(derivation),
3160        };
3161
3162        ctx.add_declaration("try_auto", try_auto_type);
3163    }
3164
3165    /// try_induction : Syntax -> Syntax -> Derivation -> Derivation
3166    ///
3167    /// Generic induction tactic for any inductive type.
3168    /// Arguments:
3169    /// - ind_type: The inductive type (SName "Nat" or SApp (SName "List") A)
3170    /// - motive: The property to prove (SLam param_type body)
3171    /// - cases: DCase chain with one derivation per constructor
3172    ///
3173    /// Returns a DElim derivation if verification passes.
3174    fn register_try_induction(ctx: &mut Context) {
3175        let syntax = Term::Global("Syntax".to_string());
3176        let derivation = Term::Global("Derivation".to_string());
3177
3178        // try_induction : Syntax -> Syntax -> Derivation -> Derivation
3179        let try_induction_type = Term::Pi {
3180            param: "_".to_string(),
3181            param_type: Box::new(syntax.clone()), // ind_type
3182            body_type: Box::new(Term::Pi {
3183                param: "_".to_string(),
3184                param_type: Box::new(syntax.clone()), // motive
3185                body_type: Box::new(Term::Pi {
3186                    param: "_".to_string(),
3187                    param_type: Box::new(derivation.clone()), // cases
3188                    body_type: Box::new(derivation),
3189                }),
3190            }),
3191        };
3192
3193        ctx.add_declaration("try_induction", try_induction_type);
3194    }
3195
3196    /// Helper functions for building induction goals.
3197    ///
3198    /// These functions help construct the subgoals for induction:
3199    /// - induction_base_goal: Computes the base case goal
3200    /// - induction_step_goal: Computes the step case goal for a constructor
3201    /// - induction_num_cases: Returns number of constructors for an inductive
3202    fn register_induction_helpers(ctx: &mut Context) {
3203        let syntax = Term::Global("Syntax".to_string());
3204        let nat = Term::Global("Nat".to_string());
3205
3206        // induction_base_goal : Syntax -> Syntax -> Syntax
3207        // Given ind_type and motive, returns the base case goal (first constructor)
3208        ctx.add_declaration(
3209            "induction_base_goal",
3210            Term::Pi {
3211                param: "_".to_string(),
3212                param_type: Box::new(syntax.clone()),
3213                body_type: Box::new(Term::Pi {
3214                    param: "_".to_string(),
3215                    param_type: Box::new(syntax.clone()),
3216                    body_type: Box::new(syntax.clone()),
3217                }),
3218            },
3219        );
3220
3221        // induction_step_goal : Syntax -> Syntax -> Nat -> Syntax
3222        // Given ind_type, motive, constructor index, returns the case goal
3223        ctx.add_declaration(
3224            "induction_step_goal",
3225            Term::Pi {
3226                param: "_".to_string(),
3227                param_type: Box::new(syntax.clone()),
3228                body_type: Box::new(Term::Pi {
3229                    param: "_".to_string(),
3230                    param_type: Box::new(syntax.clone()),
3231                    body_type: Box::new(Term::Pi {
3232                        param: "_".to_string(),
3233                        param_type: Box::new(nat),
3234                        body_type: Box::new(syntax.clone()),
3235                    }),
3236                }),
3237            },
3238        );
3239
3240        // induction_num_cases : Syntax -> Nat
3241        // Returns number of constructors for an inductive type
3242        ctx.add_declaration(
3243            "induction_num_cases",
3244            Term::Pi {
3245                param: "_".to_string(),
3246                param_type: Box::new(syntax),
3247                body_type: Box::new(Term::Global("Nat".to_string())),
3248            },
3249        );
3250    }
3251
3252    // -------------------------------------------------------------------------
3253    // Inversion Tactic
3254    // -------------------------------------------------------------------------
3255
3256    /// try_inversion : Syntax -> Derivation
3257    ///
3258    /// Inversion tactic: given a hypothesis type, derives False if no constructor
3259    /// can possibly build that type.
3260    ///
3261    /// Example: try_inversion (SApp (SName "Even") three) proves False because
3262    /// neither even_zero (requires 0) nor even_succ (requires Even 1) can build Even 3.
3263    ///
3264    /// Computational behavior defined in reduction.rs.
3265    fn register_try_inversion_tactic(ctx: &mut Context) {
3266        let syntax = Term::Global("Syntax".to_string());
3267        let derivation = Term::Global("Derivation".to_string());
3268
3269        // try_inversion : Syntax -> Derivation
3270        ctx.add_declaration(
3271            "try_inversion",
3272            Term::Pi {
3273                param: "_".to_string(),
3274                param_type: Box::new(syntax),
3275                body_type: Box::new(derivation),
3276            },
3277        );
3278    }
3279
3280    // -------------------------------------------------------------------------
3281    // Operator Tactics (rewrite, destruct, apply)
3282    // -------------------------------------------------------------------------
3283
3284    /// Operator tactics for manual proof control.
3285    ///
3286    /// try_rewrite : Derivation -> Syntax -> Derivation
3287    /// try_rewrite_rev : Derivation -> Syntax -> Derivation
3288    /// try_destruct : Syntax -> Syntax -> Derivation -> Derivation
3289    /// try_apply : Syntax -> Derivation -> Syntax -> Derivation
3290    fn register_operator_tactics(ctx: &mut Context) {
3291        let syntax = Term::Global("Syntax".to_string());
3292        let derivation = Term::Global("Derivation".to_string());
3293
3294        // try_rewrite : Derivation -> Syntax -> Derivation
3295        // Given eq_proof (concluding Eq A x y) and goal, replaces x with y in goal
3296        ctx.add_declaration(
3297            "try_rewrite",
3298            Term::Pi {
3299                param: "_".to_string(),
3300                param_type: Box::new(derivation.clone()), // eq_proof
3301                body_type: Box::new(Term::Pi {
3302                    param: "_".to_string(),
3303                    param_type: Box::new(syntax.clone()), // goal
3304                    body_type: Box::new(derivation.clone()),
3305                }),
3306            },
3307        );
3308
3309        // try_rewrite_rev : Derivation -> Syntax -> Derivation
3310        // Given eq_proof (concluding Eq A x y) and goal, replaces y with x in goal (reverse direction)
3311        ctx.add_declaration(
3312            "try_rewrite_rev",
3313            Term::Pi {
3314                param: "_".to_string(),
3315                param_type: Box::new(derivation.clone()), // eq_proof
3316                body_type: Box::new(Term::Pi {
3317                    param: "_".to_string(),
3318                    param_type: Box::new(syntax.clone()), // goal
3319                    body_type: Box::new(derivation.clone()),
3320                }),
3321            },
3322        );
3323
3324        // try_destruct : Syntax -> Syntax -> Derivation -> Derivation
3325        // Case analysis without induction hypotheses
3326        ctx.add_declaration(
3327            "try_destruct",
3328            Term::Pi {
3329                param: "_".to_string(),
3330                param_type: Box::new(syntax.clone()), // ind_type
3331                body_type: Box::new(Term::Pi {
3332                    param: "_".to_string(),
3333                    param_type: Box::new(syntax.clone()), // motive
3334                    body_type: Box::new(Term::Pi {
3335                        param: "_".to_string(),
3336                        param_type: Box::new(derivation.clone()), // cases
3337                        body_type: Box::new(derivation.clone()),
3338                    }),
3339                }),
3340            },
3341        );
3342
3343        // try_apply : Syntax -> Derivation -> Syntax -> Derivation
3344        // Manual backward chaining - applies hypothesis to transform goal
3345        ctx.add_declaration(
3346            "try_apply",
3347            Term::Pi {
3348                param: "_".to_string(),
3349                param_type: Box::new(syntax.clone()), // hyp_name
3350                body_type: Box::new(Term::Pi {
3351                    param: "_".to_string(),
3352                    param_type: Box::new(derivation.clone()), // hyp_proof
3353                    body_type: Box::new(Term::Pi {
3354                        param: "_".to_string(),
3355                        param_type: Box::new(syntax), // goal
3356                        body_type: Box::new(derivation),
3357                    }),
3358                }),
3359            },
3360        );
3361    }
3362
3363    // =========================================================================
3364    // HARDWARE TACTICS: try_bitblast, try_tabulate, try_hw_auto
3365    // =========================================================================
3366
3367    fn register_hw_tactics(ctx: &mut Context) {
3368        let syntax = Term::Global("Syntax".to_string());
3369        let derivation = Term::Global("Derivation".to_string());
3370
3371        // DBitblastSolve : Syntax -> Derivation
3372        ctx.add_constructor(
3373            "DBitblastSolve",
3374            "Derivation",
3375            Term::Pi {
3376                param: "_".to_string(),
3377                param_type: Box::new(syntax.clone()),
3378                body_type: Box::new(derivation.clone()),
3379            },
3380        );
3381
3382        // try_bitblast : Syntax -> Derivation
3383        ctx.add_declaration(
3384            "try_bitblast",
3385            Term::Pi {
3386                param: "_".to_string(),
3387                param_type: Box::new(syntax.clone()),
3388                body_type: Box::new(derivation.clone()),
3389            },
3390        );
3391
3392        // DTabulateSolve : Syntax -> Derivation
3393        ctx.add_constructor(
3394            "DTabulateSolve",
3395            "Derivation",
3396            Term::Pi {
3397                param: "_".to_string(),
3398                param_type: Box::new(syntax.clone()),
3399                body_type: Box::new(derivation.clone()),
3400            },
3401        );
3402
3403        // try_tabulate : Syntax -> Derivation
3404        ctx.add_declaration(
3405            "try_tabulate",
3406            Term::Pi {
3407                param: "_".to_string(),
3408                param_type: Box::new(syntax.clone()),
3409                body_type: Box::new(derivation.clone()),
3410            },
3411        );
3412
3413        // DHwAutoSolve : Syntax -> Derivation
3414        ctx.add_constructor(
3415            "DHwAutoSolve",
3416            "Derivation",
3417            Term::Pi {
3418                param: "_".to_string(),
3419                param_type: Box::new(syntax.clone()),
3420                body_type: Box::new(derivation.clone()),
3421            },
3422        );
3423
3424        // try_hw_auto : Syntax -> Derivation
3425        ctx.add_declaration(
3426            "try_hw_auto",
3427            Term::Pi {
3428                param: "_".to_string(),
3429                param_type: Box::new(syntax.clone()),
3430                body_type: Box::new(derivation),
3431            },
3432        );
3433    }
3434
3435    // =========================================================================
3436    // HARDWARE TYPES: Bit, Unit, BVec, gate operations, Circuit, BVec ops
3437    // =========================================================================
3438
3439    /// Register all hardware-related types and operations.
3440    fn register_hardware(ctx: &mut Context) {
3441        Self::register_bit(ctx);
3442        Self::register_hw_unit(ctx);
3443        Self::register_bvec(ctx);
3444        Self::register_gate_ops(ctx);
3445        Self::register_circuit(ctx);
3446        Self::register_bvec_ops(ctx);
3447    }
3448
3449    /// Bit : Type 0 — 1-bit logic value
3450    /// B0 : Bit — logic low
3451    /// B1 : Bit — logic high
3452    fn register_bit(ctx: &mut Context) {
3453        let bit = Term::Global("Bit".to_string());
3454        ctx.add_inductive("Bit", Term::Sort(Universe::Type(0)));
3455        ctx.add_constructor("B0", "Bit", bit.clone());
3456        ctx.add_constructor("B1", "Bit", bit);
3457    }
3458
3459    /// Unit : Type 0 — trivial type for stateless circuits
3460    /// Tt : Unit — sole inhabitant
3461    fn register_hw_unit(ctx: &mut Context) {
3462        let unit = Term::Global("Unit".to_string());
3463        ctx.add_inductive("Unit", Term::Sort(Universe::Type(0)));
3464        ctx.add_constructor("Tt", "Unit", unit);
3465    }
3466
3467    /// BVec : Nat -> Type 0 — length-indexed bitvector
3468    /// BVNil : BVec Zero
3469    /// BVCons : Bit -> Π(n:Nat). BVec n -> BVec (Succ n)
3470    fn register_bvec(ctx: &mut Context) {
3471        let type0 = Term::Sort(Universe::Type(0));
3472        let nat = Term::Global("Nat".to_string());
3473        let n = Term::Var("n".to_string());
3474
3475        // BVec : Nat -> Type 0
3476        let bvec_type = Term::Pi {
3477            param: "n".to_string(),
3478            param_type: Box::new(nat.clone()),
3479            body_type: Box::new(type0),
3480        };
3481        ctx.add_inductive("BVec", bvec_type);
3482
3483        // BVNil : BVec Zero
3484        let bvec_zero = Term::App(
3485            Box::new(Term::Global("BVec".to_string())),
3486            Box::new(Term::Global("Zero".to_string())),
3487        );
3488        ctx.add_constructor("BVNil", "BVec", bvec_zero);
3489
3490        // BVCons : Bit -> Π(n:Nat). BVec n -> BVec (Succ n)
3491        let bit = Term::Global("Bit".to_string());
3492        let bvec_n = Term::App(
3493            Box::new(Term::Global("BVec".to_string())),
3494            Box::new(n.clone()),
3495        );
3496        let bvec_succ_n = Term::App(
3497            Box::new(Term::Global("BVec".to_string())),
3498            Box::new(Term::App(
3499                Box::new(Term::Global("Succ".to_string())),
3500                Box::new(n.clone()),
3501            )),
3502        );
3503        let bvcons_type = Term::Pi {
3504            param: "_".to_string(),
3505            param_type: Box::new(bit),
3506            body_type: Box::new(Term::Pi {
3507                param: "n".to_string(),
3508                param_type: Box::new(nat),
3509                body_type: Box::new(Term::Pi {
3510                    param: "_".to_string(),
3511                    param_type: Box::new(bvec_n),
3512                    body_type: Box::new(bvec_succ_n),
3513                }),
3514            }),
3515        };
3516        ctx.add_constructor("BVCons", "BVec", bvcons_type);
3517    }
3518
3519    /// Gate operations as transparent definitions (unfold via iota reduction).
3520    ///
3521    /// bit_and : Bit -> Bit -> Bit — match a: B0 -> B0, B1 -> b
3522    /// bit_or  : Bit -> Bit -> Bit — match a: B0 -> b,  B1 -> B1
3523    /// bit_not : Bit -> Bit        — match a: B0 -> B1, B1 -> B0
3524    /// bit_xor : Bit -> Bit -> Bit — match a: B0 -> bit_not b, B1 -> b
3525    /// bit_mux : Bit -> Bit -> Bit -> Bit — match sel: B0 -> else, B1 -> then
3526    fn register_gate_ops(ctx: &mut Context) {
3527        let bit = Term::Global("Bit".to_string());
3528
3529        // Bit -> Bit -> Bit
3530        let bit2_type = Term::Pi {
3531            param: "_".to_string(),
3532            param_type: Box::new(bit.clone()),
3533            body_type: Box::new(Term::Pi {
3534                param: "_".to_string(),
3535                param_type: Box::new(bit.clone()),
3536                body_type: Box::new(bit.clone()),
3537            }),
3538        };
3539
3540        // Bit -> Bit
3541        let bit1_type = Term::Pi {
3542            param: "_".to_string(),
3543            param_type: Box::new(bit.clone()),
3544            body_type: Box::new(bit.clone()),
3545        };
3546
3547        // Bit -> Bit -> Bit -> Bit
3548        let bit3_type = Term::Pi {
3549            param: "_".to_string(),
3550            param_type: Box::new(bit.clone()),
3551            body_type: Box::new(Term::Pi {
3552                param: "_".to_string(),
3553                param_type: Box::new(bit.clone()),
3554                body_type: Box::new(Term::Pi {
3555                    param: "_".to_string(),
3556                    param_type: Box::new(bit.clone()),
3557                    body_type: Box::new(bit.clone()),
3558                }),
3559            }),
3560        };
3561
3562        // Motive for match on Bit: λ(_:Bit). Bit
3563        let motive = Term::Lambda {
3564            param: "_".to_string(),
3565            param_type: Box::new(bit.clone()),
3566            body: Box::new(bit.clone()),
3567        };
3568
3569        // bit_and := λ(a:Bit). λ(b:Bit). match a return (λ_:Bit. Bit) with [B0, b]
3570        let bit_and_body = Term::Lambda {
3571            param: "a".to_string(),
3572            param_type: Box::new(bit.clone()),
3573            body: Box::new(Term::Lambda {
3574                param: "b".to_string(),
3575                param_type: Box::new(bit.clone()),
3576                body: Box::new(Term::Match {
3577                    discriminant: Box::new(Term::Var("a".to_string())),
3578                    motive: Box::new(motive.clone()),
3579                    cases: vec![
3580                        Term::Global("B0".to_string()), // B0 case: return B0
3581                        Term::Var("b".to_string()),      // B1 case: return b
3582                    ],
3583                }),
3584            }),
3585        };
3586        ctx.add_definition("bit_and".to_string(), bit2_type.clone(), bit_and_body);
3587
3588        // bit_or := λ(a:Bit). λ(b:Bit). match a return (λ_:Bit. Bit) with [b, B1]
3589        let bit_or_body = Term::Lambda {
3590            param: "a".to_string(),
3591            param_type: Box::new(bit.clone()),
3592            body: Box::new(Term::Lambda {
3593                param: "b".to_string(),
3594                param_type: Box::new(bit.clone()),
3595                body: Box::new(Term::Match {
3596                    discriminant: Box::new(Term::Var("a".to_string())),
3597                    motive: Box::new(motive.clone()),
3598                    cases: vec![
3599                        Term::Var("b".to_string()),      // B0 case: return b
3600                        Term::Global("B1".to_string()), // B1 case: return B1
3601                    ],
3602                }),
3603            }),
3604        };
3605        ctx.add_definition("bit_or".to_string(), bit2_type.clone(), bit_or_body);
3606
3607        // bit_not := λ(a:Bit). match a return (λ_:Bit. Bit) with [B1, B0]
3608        let bit_not_body = Term::Lambda {
3609            param: "a".to_string(),
3610            param_type: Box::new(bit.clone()),
3611            body: Box::new(Term::Match {
3612                discriminant: Box::new(Term::Var("a".to_string())),
3613                motive: Box::new(motive.clone()),
3614                cases: vec![
3615                    Term::Global("B1".to_string()), // B0 case: return B1
3616                    Term::Global("B0".to_string()), // B1 case: return B0
3617                ],
3618            }),
3619        };
3620        ctx.add_definition("bit_not".to_string(), bit1_type, bit_not_body);
3621
3622        // bit_xor := λ(a:Bit). λ(b:Bit). match a return (λ_:Bit. Bit) with [b, bit_not b]
3623        // XOR truth table: 0^0=0, 0^1=1, 1^0=1, 1^1=0
3624        // When a=B0: result = b (0^0=0, 0^1=1)
3625        // When a=B1: result = bit_not b (1^0=1, 1^1=0)
3626        let bit_xor_body = Term::Lambda {
3627            param: "a".to_string(),
3628            param_type: Box::new(bit.clone()),
3629            body: Box::new(Term::Lambda {
3630                param: "b".to_string(),
3631                param_type: Box::new(bit.clone()),
3632                body: Box::new(Term::Match {
3633                    discriminant: Box::new(Term::Var("a".to_string())),
3634                    motive: Box::new(motive.clone()),
3635                    cases: vec![
3636                        // B0 case: b
3637                        Term::Var("b".to_string()),
3638                        // B1 case: bit_not b
3639                        Term::App(
3640                            Box::new(Term::Global("bit_not".to_string())),
3641                            Box::new(Term::Var("b".to_string())),
3642                        ),
3643                    ],
3644                }),
3645            }),
3646        };
3647        ctx.add_definition("bit_xor".to_string(), bit2_type, bit_xor_body);
3648
3649        // bit_mux := λ(sel:Bit). λ(then_v:Bit). λ(else_v:Bit).
3650        //            match sel return (λ_:Bit. Bit) with [else_v, then_v]
3651        let bit_mux_body = Term::Lambda {
3652            param: "sel".to_string(),
3653            param_type: Box::new(bit.clone()),
3654            body: Box::new(Term::Lambda {
3655                param: "then_v".to_string(),
3656                param_type: Box::new(bit.clone()),
3657                body: Box::new(Term::Lambda {
3658                    param: "else_v".to_string(),
3659                    param_type: Box::new(bit.clone()),
3660                    body: Box::new(Term::Match {
3661                        discriminant: Box::new(Term::Var("sel".to_string())),
3662                        motive: Box::new(motive),
3663                        cases: vec![
3664                            Term::Var("else_v".to_string()), // B0 case: else
3665                            Term::Var("then_v".to_string()), // B1 case: then
3666                        ],
3667                    }),
3668                }),
3669            }),
3670        };
3671        ctx.add_definition("bit_mux".to_string(), bit3_type, bit_mux_body);
3672    }
3673
3674    /// Circuit : Type 0 -> Type 0 -> Type 0 -> Type 0
3675    /// MkCircuit : Π(S:Type 0). Π(I:Type 0). Π(O:Type 0).
3676    ///             (S -> I -> S) -> (S -> I -> O) -> S -> Circuit S I O
3677    fn register_circuit(ctx: &mut Context) {
3678        let type0 = Term::Sort(Universe::Type(0));
3679        let s = Term::Var("S".to_string());
3680        let i = Term::Var("I".to_string());
3681        let o = Term::Var("O".to_string());
3682
3683        // Circuit : Type 0 -> Type 0 -> Type 0 -> Type 0
3684        let circuit_type = Term::Pi {
3685            param: "S".to_string(),
3686            param_type: Box::new(type0.clone()),
3687            body_type: Box::new(Term::Pi {
3688                param: "I".to_string(),
3689                param_type: Box::new(type0.clone()),
3690                body_type: Box::new(Term::Pi {
3691                    param: "O".to_string(),
3692                    param_type: Box::new(type0.clone()),
3693                    body_type: Box::new(type0.clone()),
3694                }),
3695            }),
3696        };
3697        ctx.add_inductive("Circuit", circuit_type);
3698
3699        // Circuit S I O
3700        let circuit_s_i_o = Term::App(
3701            Box::new(Term::App(
3702                Box::new(Term::App(
3703                    Box::new(Term::Global("Circuit".to_string())),
3704                    Box::new(s.clone()),
3705                )),
3706                Box::new(i.clone()),
3707            )),
3708            Box::new(o.clone()),
3709        );
3710
3711        // S -> I -> S (transition function type)
3712        let trans_type = Term::Pi {
3713            param: "_".to_string(),
3714            param_type: Box::new(s.clone()),
3715            body_type: Box::new(Term::Pi {
3716                param: "_".to_string(),
3717                param_type: Box::new(i.clone()),
3718                body_type: Box::new(s.clone()),
3719            }),
3720        };
3721
3722        // S -> I -> O (output function type)
3723        let out_type = Term::Pi {
3724            param: "_".to_string(),
3725            param_type: Box::new(s.clone()),
3726            body_type: Box::new(Term::Pi {
3727                param: "_".to_string(),
3728                param_type: Box::new(i.clone()),
3729                body_type: Box::new(o.clone()),
3730            }),
3731        };
3732
3733        // MkCircuit : Π(S:Type0). Π(I:Type0). Π(O:Type0).
3734        //             (S->I->S) -> (S->I->O) -> S -> Circuit S I O
3735        let mkcircuit_type = Term::Pi {
3736            param: "S".to_string(),
3737            param_type: Box::new(type0.clone()),
3738            body_type: Box::new(Term::Pi {
3739                param: "I".to_string(),
3740                param_type: Box::new(type0.clone()),
3741                body_type: Box::new(Term::Pi {
3742                    param: "O".to_string(),
3743                    param_type: Box::new(type0),
3744                    body_type: Box::new(Term::Pi {
3745                        param: "_".to_string(),
3746                        param_type: Box::new(trans_type),
3747                        body_type: Box::new(Term::Pi {
3748                            param: "_".to_string(),
3749                            param_type: Box::new(out_type),
3750                            body_type: Box::new(Term::Pi {
3751                                param: "_".to_string(),
3752                                param_type: Box::new(s),
3753                                body_type: Box::new(circuit_s_i_o),
3754                            }),
3755                        }),
3756                    }),
3757                }),
3758            }),
3759        };
3760        ctx.add_constructor("MkCircuit", "Circuit", mkcircuit_type);
3761    }
3762
3763    /// Bitvector operations as recursive Fix definitions.
3764    /// bv_and, bv_or, bv_not, bv_xor : Π(n:Nat). BVec n -> BVec n -> BVec n
3765    fn register_bvec_ops(ctx: &mut Context) {
3766        let nat = Term::Global("Nat".to_string());
3767        let n = Term::Var("n".to_string());
3768        let bvec_n = Term::App(
3769            Box::new(Term::Global("BVec".to_string())),
3770            Box::new(n.clone()),
3771        );
3772
3773        // Π(n:Nat). BVec n -> BVec n -> BVec n
3774        let bv_binop_type = Term::Pi {
3775            param: "n".to_string(),
3776            param_type: Box::new(nat.clone()),
3777            body_type: Box::new(Term::Pi {
3778                param: "_".to_string(),
3779                param_type: Box::new(bvec_n.clone()),
3780                body_type: Box::new(Term::Pi {
3781                    param: "_".to_string(),
3782                    param_type: Box::new(bvec_n.clone()),
3783                    body_type: Box::new(bvec_n.clone()),
3784                }),
3785            }),
3786        };
3787
3788        // Π(n:Nat). BVec n -> BVec n
3789        let bv_unop_type = Term::Pi {
3790            param: "n".to_string(),
3791            param_type: Box::new(nat.clone()),
3792            body_type: Box::new(Term::Pi {
3793                param: "_".to_string(),
3794                param_type: Box::new(bvec_n.clone()),
3795                body_type: Box::new(bvec_n.clone()),
3796            }),
3797        };
3798
3799        // Helper: build BVec m
3800        let bvec_of = |m: Term| -> Term {
3801            Term::App(Box::new(Term::Global("BVec".to_string())), Box::new(m))
3802        };
3803
3804        // Motive for Match on BVec n: λ(_:BVec n). BVec n
3805        // (In practice, the motive parameter is unused, so a simple identity type works)
3806        let motive_n = Term::Lambda {
3807            param: "_".to_string(),
3808            param_type: Box::new(bvec_n.clone()),
3809            body: Box::new(bvec_n.clone()),
3810        };
3811
3812        // --- bv_and ---
3813        // fix bv_and_rec. λ(n:Nat). λ(v1:BVec n). λ(v2:BVec n).
3814        //   match v1 with
3815        //   | BVNil => BVNil
3816        //   | BVCons => λ(b1:Bit). λ(m:Nat). λ(tail1:BVec m).
3817        //       match v2 with
3818        //       | BVNil => BVNil
3819        //       | BVCons => λ(b2:Bit). λ(_:Nat). λ(tail2:BVec m).
3820        //           BVCons (bit_and b1 b2) m (bv_and_rec m tail1 tail2)
3821        let bit = Term::Global("Bit".to_string());
3822
3823        let bv_and_body = Self::make_bvec_binop_fix(
3824            "bv_and_rec", "bit_and", &nat, &bvec_n, &bit, &motive_n,
3825        );
3826        ctx.add_definition("bv_and".to_string(), bv_binop_type.clone(), bv_and_body);
3827
3828        // --- bv_or ---
3829        let bv_or_body = Self::make_bvec_binop_fix(
3830            "bv_or_rec", "bit_or", &nat, &bvec_n, &bit, &motive_n,
3831        );
3832        ctx.add_definition("bv_or".to_string(), bv_binop_type.clone(), bv_or_body);
3833
3834        // --- bv_xor ---
3835        let bv_xor_body = Self::make_bvec_binop_fix(
3836            "bv_xor_rec", "bit_xor", &nat, &bvec_n, &bit, &motive_n,
3837        );
3838        ctx.add_definition("bv_xor".to_string(), bv_binop_type, bv_xor_body);
3839
3840        // --- bv_not ---
3841        // fix bv_not_rec. λ(n:Nat). λ(v:BVec n).
3842        //   match v with
3843        //   | BVNil => BVNil
3844        //   | BVCons => λ(b:Bit). λ(m:Nat). λ(tail:BVec m).
3845        //       BVCons (bit_not b) m (bv_not_rec m tail)
3846        let bv_not_body = Self::make_bvec_unop_fix(
3847            "bv_not_rec", "bit_not", &nat, &bvec_n, &bit, &motive_n,
3848        );
3849        ctx.add_definition("bv_not".to_string(), bv_unop_type, bv_not_body);
3850    }
3851
3852    /// Build a Fix term for a binary BVec operation (bv_and, bv_or, bv_xor).
3853    ///
3854    /// Pattern: fix rec. λn. λv1. λv2. match v1 { BVNil => BVNil, BVCons b1 m t1 => match v2 { BVNil => BVNil, BVCons b2 _ t2 => BVCons (bit_op b1 b2) m (rec m t1 t2) } }
3855    fn make_bvec_binop_fix(
3856        rec_name: &str,
3857        bit_op: &str,
3858        nat: &Term,
3859        bvec_n: &Term,
3860        bit: &Term,
3861        motive: &Term,
3862    ) -> Term {
3863        let m_var = Term::Var("m".to_string());
3864        let bvec_m = Term::App(Box::new(Term::Global("BVec".to_string())), Box::new(m_var.clone()));
3865        let motive_m = Term::Lambda {
3866            param: "_".to_string(),
3867            param_type: Box::new(bvec_m.clone()),
3868            body: Box::new(bvec_m.clone()),
3869        };
3870
3871        // Innermost: BVCons (bit_op b1 b2) m (rec m tail1 tail2)
3872        let bit_op_applied = Term::App(
3873            Box::new(Term::App(
3874                Box::new(Term::Global(bit_op.to_string())),
3875                Box::new(Term::Var("b1".to_string())),
3876            )),
3877            Box::new(Term::Var("b2".to_string())),
3878        );
3879        let rec_call = Term::App(
3880            Box::new(Term::App(
3881                Box::new(Term::App(
3882                    Box::new(Term::Var(rec_name.to_string())),
3883                    Box::new(m_var.clone()),
3884                )),
3885                Box::new(Term::Var("tail1".to_string())),
3886            )),
3887            Box::new(Term::Var("tail2".to_string())),
3888        );
3889        let bvcons_result = Term::App(
3890            Box::new(Term::App(
3891                Box::new(Term::App(
3892                    Box::new(Term::Global("BVCons".to_string())),
3893                    Box::new(bit_op_applied),
3894                )),
3895                Box::new(m_var.clone()),
3896            )),
3897            Box::new(rec_call),
3898        );
3899
3900        // Inner match on v2 (BVCons case for v1)
3901        let inner_bvcons_case = Term::Lambda {
3902            param: "b2".to_string(),
3903            param_type: Box::new(bit.clone()),
3904            body: Box::new(Term::Lambda {
3905                param: "_m2".to_string(),
3906                param_type: Box::new(nat.clone()),
3907                body: Box::new(Term::Lambda {
3908                    param: "tail2".to_string(),
3909                    param_type: Box::new(bvec_m.clone()),
3910                    body: Box::new(bvcons_result),
3911                }),
3912            }),
3913        };
3914
3915        let inner_match = Term::Match {
3916            discriminant: Box::new(Term::Var("v2".to_string())),
3917            motive: Box::new(motive_m.clone()),
3918            cases: vec![
3919                Term::Global("BVNil".to_string()), // BVNil case
3920                inner_bvcons_case,                  // BVCons case
3921            ],
3922        };
3923
3924        // Outer BVCons case for v1
3925        let outer_bvcons_case = Term::Lambda {
3926            param: "b1".to_string(),
3927            param_type: Box::new(bit.clone()),
3928            body: Box::new(Term::Lambda {
3929                param: "m".to_string(),
3930                param_type: Box::new(nat.clone()),
3931                body: Box::new(Term::Lambda {
3932                    param: "tail1".to_string(),
3933                    param_type: Box::new(bvec_m.clone()),
3934                    body: Box::new(inner_match),
3935                }),
3936            }),
3937        };
3938
3939        // Outer match on v1
3940        let outer_match = Term::Match {
3941            discriminant: Box::new(Term::Var("v1".to_string())),
3942            motive: Box::new(motive.clone()),
3943            cases: vec![
3944                Term::Global("BVNil".to_string()), // BVNil case
3945                outer_bvcons_case,                  // BVCons case
3946            ],
3947        };
3948
3949        // Fix + lambdas
3950        Term::Fix {
3951            name: rec_name.to_string(),
3952            body: Box::new(Term::Lambda {
3953                param: "n".to_string(),
3954                param_type: Box::new(nat.clone()),
3955                body: Box::new(Term::Lambda {
3956                    param: "v1".to_string(),
3957                    param_type: Box::new(bvec_n.clone()),
3958                    body: Box::new(Term::Lambda {
3959                        param: "v2".to_string(),
3960                        param_type: Box::new(bvec_n.clone()),
3961                        body: Box::new(outer_match),
3962                    }),
3963                }),
3964            }),
3965        }
3966    }
3967
3968    /// Build a Fix term for a unary BVec operation (bv_not).
3969    ///
3970    /// Pattern: fix rec. λn. λv. match v { BVNil => BVNil, BVCons b m t => BVCons (bit_op b) m (rec m t) }
3971    fn make_bvec_unop_fix(
3972        rec_name: &str,
3973        bit_op: &str,
3974        nat: &Term,
3975        bvec_n: &Term,
3976        bit: &Term,
3977        motive: &Term,
3978    ) -> Term {
3979        let m_var = Term::Var("m".to_string());
3980        let bvec_m = Term::App(Box::new(Term::Global("BVec".to_string())), Box::new(m_var.clone()));
3981
3982        // BVCons (bit_op b) m (rec m tail)
3983        let bit_op_applied = Term::App(
3984            Box::new(Term::Global(bit_op.to_string())),
3985            Box::new(Term::Var("b".to_string())),
3986        );
3987        let rec_call = Term::App(
3988            Box::new(Term::App(
3989                Box::new(Term::Var(rec_name.to_string())),
3990                Box::new(m_var.clone()),
3991            )),
3992            Box::new(Term::Var("tail".to_string())),
3993        );
3994        let bvcons_result = Term::App(
3995            Box::new(Term::App(
3996                Box::new(Term::App(
3997                    Box::new(Term::Global("BVCons".to_string())),
3998                    Box::new(bit_op_applied),
3999                )),
4000                Box::new(m_var.clone()),
4001            )),
4002            Box::new(rec_call),
4003        );
4004
4005        // BVCons case
4006        let bvcons_case = Term::Lambda {
4007            param: "b".to_string(),
4008            param_type: Box::new(bit.clone()),
4009            body: Box::new(Term::Lambda {
4010                param: "m".to_string(),
4011                param_type: Box::new(nat.clone()),
4012                body: Box::new(Term::Lambda {
4013                    param: "tail".to_string(),
4014                    param_type: Box::new(bvec_m),
4015                    body: Box::new(bvcons_result),
4016                }),
4017            }),
4018        };
4019
4020        // Match on v
4021        let match_expr = Term::Match {
4022            discriminant: Box::new(Term::Var("v".to_string())),
4023            motive: Box::new(motive.clone()),
4024            cases: vec![
4025                Term::Global("BVNil".to_string()), // BVNil case
4026                bvcons_case,                        // BVCons case
4027            ],
4028        };
4029
4030        // Fix + lambdas
4031        Term::Fix {
4032            name: rec_name.to_string(),
4033            body: Box::new(Term::Lambda {
4034                param: "n".to_string(),
4035                param_type: Box::new(nat.clone()),
4036                body: Box::new(Term::Lambda {
4037                    param: "v".to_string(),
4038                    param_type: Box::new(bvec_n.clone()),
4039                    body: Box::new(match_expr),
4040                }),
4041            }),
4042        }
4043    }
4044}