Skip to main content

logicaffeine_kernel/
int_inductive.rs

1//! `Int` as a REAL INDUCTIVE — Lean's `ofNat | negSucc`, with its arithmetic DEFINED.
2//!
3//! The standard prelude represents integers with checked constructors and definitions.
4//! Ring and order facts still need separate proof terms; installing a carrier does not
5//! turn an assumed law into a theorem.
6//! It is a block Lean does not pay: `Init/Data/Int/Basic.lean:40`–`45` declares
7//!
8//! ```text
9//! inductive Int : Type where
10//!   | ofNat   : Nat → Int
11//!   | negSucc : Nat → Int
12//! ```
13//!
14//! and then DEFINES `negOfNat`, `neg`, `subNatNat`, `add`, `mul` and `sub` by matching on those
15//! two constructors (`Init/Data/Int/Basic.lean:78`–`166`). Every arithmetic identity Lean states
16//! about `Int` is a theorem over that definition.
17//!
18//! # How Lean keeps it fast anyway
19//!
20//! The obvious objection to constructors is cost: `ofNat n` looks like a unary Peano spine, so
21//! `2 + 3` looks linear and `2 * 3` quadratic. Lean's answer — read off its own source rather
22//! than guessed — is that `Int`'s arithmetic BOTTOMS OUT IN `Nat`'s. Look at the `ofNat/ofNat`
23//! arm of `Int.add`: it is `ofNat (m + n)`, ONE `Nat` addition, and `Nat` literals are
24//! GMP-accelerated in Lean's kernel. The `@[extern "lean_int_add"]` attribute on the same
25//! definition (`Init/Data/Int/Basic.lean:126`) is the COMPILER's story, not the kernel's:
26//! `Int.ofNat` itself carries `@[extern "lean_nat_to_int"]` (line 47) so a runtime `Int` is
27//! never a spine either.
28//!
29//! LOGOS keeps compact arbitrary-precision literals and canonical constructor bridges.
30//! Public addition and multiplication have checked definition bodies. Their literal
31//! accelerations attest the complete program dependency chain and preserve each kernel's
32//! independent arithmetic implementation. Rebinding any dependency disables the shortcut.
33//!
34//! # What this module is for
35//!
36//! The standard prelude calls this module to install checked constructors and arithmetic.
37//! Constructors pass positivity/universe checks; arithmetic remains ordinary definitions
38//! visible to both kernels. The literal bridge below activates only for the canonical
39//! declaration signatures. The standard prelude also installs the public add/mul aliases.
40//! Other primitive operations and the ring/order axioms remain separately auditable until
41//! their derived replacements are installed.
42
43use crate::context::Context;
44use crate::error::{KernelError, KernelResult};
45use crate::term::Term;
46
47fn g(n: &str) -> Term {
48    Term::Global(n.to_string())
49}
50fn v(n: &str) -> Term {
51    Term::Var(n.to_string())
52}
53fn app(f: Term, x: Term) -> Term {
54    Term::App(Box::new(f), Box::new(x))
55}
56fn apps(f: Term, xs: &[Term]) -> Term {
57    xs.iter().fold(f, |a, x| app(a, x.clone()))
58}
59fn lam(p: &str, t: Term, b: Term) -> Term {
60    Term::Lambda {
61        param: p.to_string(),
62        param_type: Box::new(t),
63        body: Box::new(b),
64    }
65}
66fn pi(p: &str, t: Term, b: Term) -> Term {
67    Term::Pi {
68        param: p.to_string(),
69        param_type: Box::new(t),
70        body_type: Box::new(b),
71    }
72}
73fn arrow(a: Term, b: Term) -> Term {
74    pi("_", a, b)
75}
76fn fix(name: &str, body: Term) -> Term {
77    Term::Fix {
78        name: name.to_string(),
79        body: Box::new(body),
80    }
81}
82fn mat(d: Term, motive: Term, cases: Vec<Term>) -> Term {
83    Term::Match {
84        discriminant: Box::new(d),
85        motive: Box::new(motive),
86        cases,
87    }
88}
89fn nat() -> Term {
90    g("Nat")
91}
92fn int() -> Term {
93    g("Int")
94}
95fn succ(t: Term) -> Term {
96    app(g("Succ"), t)
97}
98/// `λ_:Nat. Nat` — the motive every `Nat` recursion below returns at.
99fn nat_motive() -> Term {
100    lam("_", nat(), nat())
101}
102/// `λ_:Int. Int`.
103fn int_motive() -> Term {
104    lam("_", int(), int())
105}
106fn nat_binop() -> Term {
107    arrow(nat(), arrow(nat(), nat()))
108}
109fn int_binop() -> Term {
110    arrow(int(), arrow(int(), int()))
111}
112fn of_nat(n: Term) -> Term {
113    app(g("Int_ofNat"), n)
114}
115fn neg_succ(n: Term) -> Term {
116    app(g("Int_negSucc"), n)
117}
118
119/// Register `name` unless something is already bound to it — a caller that has its own `Nat_add`
120/// keeps it, and this module never silently swaps one program for another.
121fn define_if_absent(ctx: &mut Context, name: &str, ty: Term, body: Term) {
122    if ctx.get_global(name).is_none() {
123        ctx.add_definition(name.to_string(), ty, body);
124    }
125}
126
127/// The `Nat` operations `Int`'s arithmetic bottoms out in, recursing on the SECOND argument
128/// exactly as Lean's `Nat.add`/`Nat.sub`/`Nat.mul` do (`Init/Prelude.lean`'s `Nat.add` is
129/// `| n, .succ m => .succ (Nat.add n m)`).
130///
131/// Recursing on the second argument is not a detail: it is what makes `n + 0 ≡ n` and
132/// `n * 0 ≡ 0` hold by ι for an OPEN `n`, which is what every ring-law proof over `Int` starts
133/// from.
134type ArithmeticDefinition = (&'static str, Term, Term);
135
136fn nat_arithmetic_definitions() -> &'static [ArithmeticDefinition] {
137    static DEFINITIONS: std::sync::OnceLock<Vec<ArithmeticDefinition>> = std::sync::OnceLock::new();
138    DEFINITIONS.get_or_init(|| {
139        let mut definitions = Vec::new();
140
141        // Nat_add m 0 = m ; Nat_add m (k+1) = Succ (Nat_add m k)
142        definitions.push((
143            "Nat_add",
144            nat_binop(),
145            fix(
146                "f✧",
147                lam(
148                    "m",
149                    nat(),
150                    lam(
151                        "n",
152                        nat(),
153                        mat(
154                            v("n"),
155                            nat_motive(),
156                            vec![
157                                v("m"),
158                                lam("k", nat(), succ(apps(v("f✧"), &[v("m"), v("k")]))),
159                            ],
160                        ),
161                    ),
162                ),
163            ),
164        ));
165        // Nat_pred 0 = 0 ; Nat_pred (k+1) = k
166        definitions.push((
167            "Nat_pred",
168            arrow(nat(), nat()),
169            lam(
170                "x",
171                nat(),
172                mat(
173                    v("x"),
174                    nat_motive(),
175                    vec![g("Zero"), lam("p", nat(), v("p"))],
176                ),
177            ),
178        ));
179        // Nat_sub m 0 = m ; Nat_sub m (k+1) = Nat_pred (Nat_sub m k) — TRUNCATED, as Lean's is.
180        definitions.push((
181            "Nat_sub",
182            nat_binop(),
183            fix(
184                "f✧",
185                lam(
186                    "m",
187                    nat(),
188                    lam(
189                        "n",
190                        nat(),
191                        mat(
192                            v("n"),
193                            nat_motive(),
194                            vec![
195                                v("m"),
196                                lam(
197                                    "k",
198                                    nat(),
199                                    app(g("Nat_pred"), apps(v("f✧"), &[v("m"), v("k")])),
200                                ),
201                            ],
202                        ),
203                    ),
204                ),
205            ),
206        ));
207        // Nat_mul m 0 = 0 ; Nat_mul m (k+1) = Nat_add (Nat_mul m k) m
208        definitions.push((
209            "Nat_mul",
210            nat_binop(),
211            fix(
212                "f✧",
213                lam(
214                    "m",
215                    nat(),
216                    lam(
217                        "n",
218                        nat(),
219                        mat(
220                            v("n"),
221                            nat_motive(),
222                            vec![
223                                g("Zero"),
224                                lam(
225                                    "k",
226                                    nat(),
227                                    apps(g("Nat_add"), &[apps(v("f✧"), &[v("m"), v("k")]), v("m")]),
228                                ),
229                            ],
230                        ),
231                    ),
232                ),
233            ),
234        ));
235
236        definitions
237    })
238}
239
240pub fn register_nat_arithmetic(ctx: &mut Context) {
241    for (name, ty, body) in nat_arithmetic_definitions() {
242        define_if_absent(ctx, name, ty.clone(), body.clone());
243    }
244}
245
246/// `Int`'s two constructors, through the CHECKED door.
247///
248/// `ofNat : Nat → Int` and `negSucc : Nat → Int`, in Lean's declaration order
249/// (`Init/Data/Int/Basic.lean:42`, `:45`) — order is the whole content of a declaration here,
250/// because `Term::Match` is positional.
251pub fn register_int_constructors(ctx: &mut Context) -> KernelResult<()> {
252    if !ctx.is_inductive("Int") {
253        return Err(KernelError::CertificationError(
254            "`Int` must already be a registered inductive — this adds its constructors, it does \
255             not declare the carrier"
256                .to_string(),
257        ));
258    }
259    if !ctx.get_constructors("Int").is_empty() {
260        return Ok(());
261    }
262    ctx.add_constructor_checked("Int_ofNat", "Int", arrow(nat(), int()))?;
263    ctx.add_constructor_checked("Int_negSucc", "Int", arrow(nat(), int()))?;
264    Ok(())
265}
266
267/// `Int` negation, addition, subtraction and multiplication as DEFINITIONS, transcribed from
268/// `Init/Data/Int/Basic.lean:78`–`166` arm for arm.
269///
270/// ```text
271/// negOfNat 0        = ofNat 0          negOfNat (succ m) = negSucc m
272/// neg (ofNat n)     = negOfNat n       neg (negSucc n)   = ofNat (succ n)
273/// subNatNat m n     = match n - m with | 0 => ofNat (m - n) | succ k => negSucc k
274/// add  (ofNat m)   (ofNat n)   = ofNat (m + n)
275/// add  (ofNat m)   (negSucc n) = subNatNat m (succ n)
276/// add  (negSucc m) (ofNat n)   = subNatNat n (succ m)
277/// add  (negSucc m) (negSucc n) = negSucc (succ (m + n))
278/// mul  (ofNat m)   (ofNat n)   = ofNat (m * n)
279/// mul  (ofNat m)   (negSucc n) = negOfNat (m * succ n)
280/// mul  (negSucc m) (ofNat n)   = negOfNat (succ m * n)
281/// mul  (negSucc m) (negSucc n) = ofNat (succ m * succ n)
282/// sub  m n                     = m + (- n)
283/// ```
284///
285/// `negOfNat` is the ONE place `-0 = 0` is decided, which is why it is a named definition here
286/// and not inlined into `neg` and `mul`: an off-by-one there is invisible on positive operands.
287fn int_arithmetic_definitions() -> &'static [ArithmeticDefinition] {
288    static DEFINITIONS: std::sync::OnceLock<Vec<ArithmeticDefinition>> = std::sync::OnceLock::new();
289    DEFINITIONS.get_or_init(|| {
290        let mut definitions = Vec::new();
291        let nadd = |a: Term, b: Term| apps(g("Nat_add"), &[a, b]);
292        let nmul = |a: Term, b: Term| apps(g("Nat_mul"), &[a, b]);
293
294        // Int_negOfNat : Nat → Int
295        definitions.push((
296            "Int_negOfNat",
297            arrow(nat(), int()),
298            lam(
299                "n",
300                nat(),
301                mat(
302                    v("n"),
303                    lam("_", nat(), int()),
304                    vec![of_nat(g("Zero")), lam("k", nat(), neg_succ(v("k")))],
305                ),
306            ),
307        ));
308        // Int_subNatNat : Nat → Nat → Int
309        definitions.push((
310            "Int_subNatNat",
311            arrow(nat(), arrow(nat(), int())),
312            lam(
313                "m",
314                nat(),
315                lam(
316                    "n",
317                    nat(),
318                    mat(
319                        apps(g("Nat_sub"), &[v("n"), v("m")]),
320                        lam("_", nat(), int()),
321                        vec![
322                            of_nat(apps(g("Nat_sub"), &[v("m"), v("n")])),
323                            lam("k", nat(), neg_succ(v("k"))),
324                        ],
325                    ),
326                ),
327            ),
328        ));
329        // Int_neg : Int → Int
330        definitions.push((
331            "Int_neg",
332            arrow(int(), int()),
333            lam(
334                "a",
335                int(),
336                mat(
337                    v("a"),
338                    int_motive(),
339                    vec![
340                        lam("m", nat(), app(g("Int_negOfNat"), v("m"))),
341                        lam("m", nat(), of_nat(succ(v("m")))),
342                    ],
343                ),
344            ),
345        ));
346        // Int_add : Int → Int → Int
347        definitions.push((
348            "Int_add",
349            int_binop(),
350            lam(
351                "a",
352                int(),
353                lam(
354                    "b",
355                    int(),
356                    mat(
357                        v("a"),
358                        int_motive(),
359                        vec![
360                            lam(
361                                "m",
362                                nat(),
363                                mat(
364                                    v("b"),
365                                    int_motive(),
366                                    vec![
367                                        lam("n", nat(), of_nat(nadd(v("m"), v("n")))),
368                                        lam(
369                                            "n",
370                                            nat(),
371                                            apps(g("Int_subNatNat"), &[v("m"), succ(v("n"))]),
372                                        ),
373                                    ],
374                                ),
375                            ),
376                            lam(
377                                "m",
378                                nat(),
379                                mat(
380                                    v("b"),
381                                    int_motive(),
382                                    vec![
383                                        lam(
384                                            "n",
385                                            nat(),
386                                            apps(g("Int_subNatNat"), &[v("n"), succ(v("m"))]),
387                                        ),
388                                        lam("n", nat(), neg_succ(succ(nadd(v("m"), v("n"))))),
389                                    ],
390                                ),
391                            ),
392                        ],
393                    ),
394                ),
395            ),
396        ));
397        // Int_sub a b := Int_add a (Int_neg b). Public sub_elim additionally
398        // rewrites Int_neg b to multiplication by -1 using a checked constructor proof.
399        definitions.push((
400            "Int_sub",
401            int_binop(),
402            lam(
403                "a",
404                int(),
405                lam(
406                    "b",
407                    int(),
408                    apps(g("Int_add"), &[v("a"), app(g("Int_neg"), v("b"))]),
409                ),
410            ),
411        ));
412        // Int_mul : Int → Int → Int
413        definitions.push((
414            "Int_mul",
415            int_binop(),
416            lam(
417                "a",
418                int(),
419                lam(
420                    "b",
421                    int(),
422                    mat(
423                        v("a"),
424                        int_motive(),
425                        vec![
426                            lam(
427                                "m",
428                                nat(),
429                                mat(
430                                    v("b"),
431                                    int_motive(),
432                                    vec![
433                                        lam("n", nat(), of_nat(nmul(v("m"), v("n")))),
434                                        lam(
435                                            "n",
436                                            nat(),
437                                            app(g("Int_negOfNat"), nmul(v("m"), succ(v("n")))),
438                                        ),
439                                    ],
440                                ),
441                            ),
442                            lam(
443                                "m",
444                                nat(),
445                                mat(
446                                    v("b"),
447                                    int_motive(),
448                                    vec![
449                                        lam(
450                                            "n",
451                                            nat(),
452                                            app(g("Int_negOfNat"), nmul(succ(v("m")), v("n"))),
453                                        ),
454                                        lam("n", nat(), of_nat(nmul(succ(v("m")), succ(v("n"))))),
455                                    ],
456                                ),
457                            ),
458                        ],
459                    ),
460                ),
461            ),
462        ));
463
464        definitions
465    })
466}
467
468pub fn register_int_arithmetic(ctx: &mut Context) -> KernelResult<()> {
469    if ctx.get_constructors("Int").len() != 2 {
470        return Err(KernelError::CertificationError(
471            "`Int`'s arithmetic is defined BY CASES on `ofNat`/`negSucc` — register the \
472             constructors first"
473                .to_string(),
474        ));
475    }
476    register_nat_arithmetic(ctx);
477    for (name, ty, body) in int_arithmetic_definitions() {
478        define_if_absent(ctx, name, ty.clone(), body.clone());
479    }
480    Ok(())
481}
482
483/// The whole adoption, as one call: `Int` gains Lean's two constructors and Lean's arithmetic.
484///
485/// `Nat` must already be registered (`Int_ofNat` stores a `Nat`); `StandardLibrary::register`
486/// calls this once `Nat` and its arithmetic exist. It defines `Int_add`/`Int_sub`/`Int_mul`/
487/// `Int_le` and leaves the public `add`/`sub`/`mul` names alone — binding those to these
488/// definitions is `register_public_arithmetic_aliases`, a separate step with its own check.
489///
490/// ```
491/// use logicaffeine_kernel::int_inductive::canonical_int;
492/// use logicaffeine_kernel::prelude::StandardLibrary;
493/// use logicaffeine_kernel::{normalize, Term};
494///
495/// let ctx = StandardLibrary::context();
496/// let constructors: Vec<&str> = ctx.get_constructors("Int").into_iter().map(|(name, _)| name).collect();
497/// assert_eq!(constructors, ["Int_ofNat", "Int_negSucc"]);
498///
499/// let app = |f: Term, x: Term| Term::App(Box::new(f), Box::new(x));
500/// let sum = app(app(Term::Global("Int_add".into()), canonical_int(2)), canonical_int(-5));
501/// assert_eq!(normalize(ctx, &sum), canonical_int(-3));
502/// ```
503pub fn register_inductive_int(ctx: &mut Context) -> KernelResult<()> {
504    register_int_constructors(ctx)?;
505    register_int_arithmetic(ctx)
506}
507
508/// The canonical `Int` term for a machine integer, in Lean's representation: `ofNat n` for
509/// `n ≥ 0`, `negSucc (-n-1)` below. The `Nat` argument is a `Literal::Nat`, not a `Succ` spine,
510/// so a large integer costs one node — which is the whole point of the `Nat` literal bridge.
511pub fn canonical_int(n: i64) -> Term {
512    canonical_bigint(&crate::BigInt::from_i64(n))
513}
514
515/// The literal bridge is valid only for the canonical monomorphic Nat/Int declarations.
516/// A same-spelled user definition or a replaced constructor must never inherit this rule.
517pub(crate) fn canonical_environment(ctx: &Context) -> bool {
518    ctx.canonical_numerals().int
519}
520
521/// [`canonical_environment`] for `Nat` alone: the rules that read a `Nat` literal as `Zero` or
522/// `Succ` of the literal below it.
523pub(crate) fn canonical_nat_environment(ctx: &Context) -> bool {
524    ctx.canonical_numerals().nat
525}
526
527/// Whether a `Nat` literal of value `n` reads as a constructor: `0` (and a malformed negative) as
528/// `Zero` while `Zero : Nat` is `Nat`'s constructor, `n + 1` as `Succ n` while `Succ : Nat → Nat`
529/// is too. A reading names its constructors, and holds exactly while they are `Nat`'s.
530pub(crate) fn reads_nat_literal(ctx: &Context, n: &crate::BigInt) -> bool {
531    let numerals = ctx.canonical_numerals();
532    if *n <= crate::BigInt::from_i64(0) {
533        numerals.zero
534    } else {
535        numerals.succ
536    }
537}
538
539/// Decides [`crate::context::CanonicalNumerals`] from the registries
540/// ([`Context::canonical_numerals`] keeps the answer for their generation).
541pub(crate) fn canonical_numerals_of(ctx: &Context) -> crate::context::CanonicalNumerals {
542    let nat = canonical_family_environment(ctx, "Nat", ["Zero", "Succ"]);
543    let zero = nat || nat_constructor_environment(ctx, "Zero", &|ty| *ty == g("Nat"));
544    let unary = |ty: &Term| {
545        matches!(ty, Term::Pi { param_type, body_type, .. }
546            if **param_type == g("Nat") && **body_type == g("Nat"))
547    };
548    crate::context::CanonicalNumerals {
549        zero,
550        succ: nat || (zero && nat_constructor_environment(ctx, "Succ", &unary)),
551        nat,
552        int: nat && canonical_family_environment(ctx, "Int", ["Int_ofNat", "Int_negSucc"]),
553    }
554}
555
556/// `Nat` a monomorphic family in `Type 0` with a constructor `name` whose type `fits`, whatever
557/// the others are: what reading a literal as `name` needs.
558fn nat_constructor_environment(ctx: &Context, name: &str, fits: &dyn Fn(&Term) -> bool) -> bool {
559    use crate::Universe;
560    ctx.is_inductive("Nat")
561        && ctx.get_global("Nat") == Some(&Term::Sort(Universe::Type(0)))
562        && ctx.inductive_num_params("Nat") == 0
563        && ctx.poly_declaration("Nat").is_none()
564        && ctx.get_definition_body("Nat").is_none()
565        && ctx.constructor_inductive(name) == Some("Nat")
566        && ctx.get_global(name).is_some_and(fits)
567        && ctx.poly_declaration(name).is_none()
568        && ctx.get_definition_body(name).is_none()
569}
570
571fn canonical_family_environment(ctx: &Context, family: &str, names: [&str; 2]) -> bool {
572    use crate::{Term, Universe};
573    if !ctx.is_inductive(family)
574        || ctx.get_global(family) != Some(&Term::Sort(Universe::Type(0)))
575        || ctx.inductive_num_params(family) != 0
576        || ctx.poly_declaration(family).is_some()
577        || ctx.get_definition_body(family).is_some()
578    {
579        return false;
580    }
581    let constructors = ctx.get_constructors(family);
582    if constructors.len() != 2 {
583        return false;
584    }
585    for ((actual, ty), expected) in constructors.iter().zip(names) {
586        if *actual != expected
587            || ctx.constructor_inductive(expected) != Some(family)
588            || ctx.get_global(expected) != Some(*ty)
589            || ctx.poly_declaration(expected).is_some()
590            || ctx.get_definition_body(expected).is_some()
591        {
592            return false;
593        }
594        let right_type = if expected == "Zero" {
595            **ty == g("Nat")
596        } else {
597            matches!(ty, Term::Pi { param_type, body_type, .. }
598                if **param_type == g("Nat") && **body_type == g(family))
599        };
600        if !right_type {
601            return false;
602        }
603    }
604    true
605}
606
607/// One constructor layer, preserving arbitrary precision and Int.negSucc's offset.
608pub(crate) fn literal_constructor(ctx: &Context, t: &Term) -> Option<Term> {
609    let Term::Lit(literal) = t else { return None };
610    let n = crate::term::lit_bigint(literal)?;
611    if !canonical_environment(ctx) {
612        return None;
613    }
614    Some(canonical_bigint(&n))
615}
616
617fn canonical_bigint(n: &crate::BigInt) -> Term {
618    use crate::{BigInt, Literal};
619    if n.is_negative() {
620        neg_succ(Term::Lit(Literal::Nat(n.abs().sub(&BigInt::from_i64(1)))))
621    } else {
622        of_nat(Term::Lit(Literal::Nat(n.clone())))
623    }
624}
625
626/// Read a canonical constructor value for the existing primitive arithmetic evaluator.
627/// The field is inspected iteratively: a compact literal and a finite Succ spine agree.
628pub(crate) fn constructor_literal(ctx: &Context, t: &Term) -> Option<crate::Literal> {
629    let Term::App(head, field) = t else {
630        return None;
631    };
632    let negative = match head.as_ref() {
633        Term::Global(n) if n == "Int_ofNat" => false,
634        Term::Global(n) if n == "Int_negSucc" => true,
635        _ => return None,
636    };
637    if !canonical_environment(ctx) {
638        return None;
639    }
640    let mut field = field.as_ref();
641    let mut count = crate::BigInt::from_i64(0);
642    let one = crate::BigInt::from_i64(1);
643    loop {
644        match field {
645            Term::Lit(crate::Literal::Nat(n)) if !n.is_negative() => {
646                count = count.add(n);
647                break;
648            }
649            Term::Global(n) if n == "Zero" => break,
650            Term::App(f, a) if matches!(f.as_ref(), Term::Global(n) if n == "Succ") => {
651                count = count.add(&one);
652                field = a;
653            }
654            _ => return None,
655        }
656    }
657    if negative {
658        count = count.add(&one).negated();
659    }
660    Some(crate::term::int_lit(count))
661}
662
663/// Checked Boolean order on the existing signed constructors. Natural order
664/// remains the prelude's canonical natLe; this module does not rebuild it.
665fn integer_order_definition() -> &'static ArithmeticDefinition {
666    static DEFINITION: std::sync::OnceLock<ArithmeticDefinition> = std::sync::OnceLock::new();
667    DEFINITION.get_or_init(|| {
668        let bool_motive = || lam("_", int(), g("Bool"));
669        let body = lam(
670            "a",
671            int(),
672            lam(
673                "b",
674                int(),
675                mat(
676                    v("a"),
677                    bool_motive(),
678                    vec![
679                        lam(
680                            "m",
681                            nat(),
682                            mat(
683                                v("b"),
684                                bool_motive(),
685                                vec![
686                                    lam("n", nat(), apps(g("natLe"), &[v("m"), v("n")])),
687                                    lam("n", nat(), g("false")),
688                                ],
689                            ),
690                        ),
691                        lam(
692                            "m",
693                            nat(),
694                            mat(
695                                v("b"),
696                                bool_motive(),
697                                vec![
698                                    lam("n", nat(), g("true")),
699                                    lam("n", nat(), apps(g("natLe"), &[v("n"), v("m")])),
700                                ],
701                            ),
702                        ),
703                    ],
704                ),
705            ),
706        );
707        ("Int_le", arrow(int(), arrow(int(), g("Bool"))), body)
708    })
709}
710
711/// Install order only after the standard prelude has supplied Bool and natLe.
712/// Standalone structural integer arithmetic remains independent of those types.
713pub(crate) fn register_public_order_alias(ctx: &mut Context) -> KernelResult<()> {
714    let (name, ty, body) = integer_order_definition();
715    ctx.declare_checked_auxiliary(name, ty.clone(), body.clone())?;
716    let public_type = ctx.get_global("le").cloned().unwrap_or_else(|| ty.clone());
717    ctx.declare_checked_auxiliary("le", public_type, g(name))?;
718    Ok(())
719}
720
721// The public header is exactly Int -> Int -> Int. Parameter spelling has
722// no semantic role because neither domain nor result contains a bound variable.
723// Inspect only this shape: no unfolding, conversion, or inferred equivalence.
724fn has_integer_binop_signature(ty: &Term) -> bool {
725    has_integer_binary_result(ty, "Int")
726}
727
728fn has_integer_binary_result(ty: &Term, result_name: &str) -> bool {
729    let Term::Pi {
730        param_type: first,
731        body_type: rest,
732        ..
733    } = ty
734    else {
735        return false;
736    };
737    let Term::Pi {
738        param_type: second,
739        body_type: result,
740        ..
741    } = rest.as_ref()
742    else {
743        return false;
744    };
745    [first.as_ref(), second.as_ref()]
746        .into_iter()
747        .all(|part| matches!(part, Term::Global(name) if name == "Int"))
748        && matches!(result.as_ref(), Term::Global(name) if name == result_name)
749}
750
751fn integer_division_definitions() -> &'static [ArithmeticDefinition] {
752    static DEFINITIONS: std::sync::OnceLock<Vec<ArithmeticDefinition>> = std::sync::OnceLock::new();
753    DEFINITIONS.get_or_init(|| {
754        let pair_ty = || apps(g("Prod"), &[int(), int()]);
755        let pair = |q: Term, r: Term| apps(g("Prod_mk"), &[int(), int(), q, r]);
756        let natural_pair = || apps(g("Prod"), &[nat(), nat()]);
757        let nonzero = |negative_divisor: bool| {
758            let positive = mat(
759                apps(g("Nat_divMod"), &[v("m"), succ(v("k"))]),
760                lam("_", natural_pair(), pair_ty()),
761                vec![lam(
762                    "q",
763                    nat(),
764                    lam(
765                        "r",
766                        nat(),
767                        pair(
768                            if negative_divisor {
769                                app(g("Int_negOfNat"), v("q"))
770                            } else {
771                                of_nat(v("q"))
772                            },
773                            of_nat(v("r")),
774                        ),
775                    ),
776                )],
777            );
778            let negative = mat(
779                apps(g("Nat_divMod"), &[succ(v("m")), succ(v("k"))]),
780                lam("_", natural_pair(), pair_ty()),
781                vec![lam(
782                    "q",
783                    nat(),
784                    lam(
785                        "r",
786                        nat(),
787                        mat(
788                            v("r"),
789                            lam("_", nat(), pair_ty()),
790                            vec![
791                                pair(
792                                    if negative_divisor {
793                                        of_nat(v("q"))
794                                    } else {
795                                        app(g("Int_negOfNat"), v("q"))
796                                    },
797                                    of_nat(g("Zero")),
798                                ),
799                                lam(
800                                    "s",
801                                    nat(),
802                                    pair(
803                                        if negative_divisor {
804                                            of_nat(succ(v("q")))
805                                        } else {
806                                            neg_succ(v("q"))
807                                        },
808                                        of_nat(apps(g("Nat_sub"), &[succ(v("k")), succ(v("s"))])),
809                                    ),
810                                ),
811                            ],
812                        ),
813                    ),
814                )],
815            );
816            mat(
817                v("a"),
818                lam("_", int(), pair_ty()),
819                vec![lam("m", nat(), positive), lam("m", nat(), negative)],
820            )
821        };
822        let body = lam(
823            "a",
824            int(),
825            lam(
826                "b",
827                int(),
828                mat(
829                    v("b"),
830                    lam("_", int(), pair_ty()),
831                    vec![
832                        lam(
833                            "n",
834                            nat(),
835                            mat(
836                                v("n"),
837                                lam("_", nat(), pair_ty()),
838                                vec![
839                                    pair(of_nat(g("Zero")), v("a")),
840                                    lam("k", nat(), nonzero(false)),
841                                ],
842                            ),
843                        ),
844                        lam("k", nat(), nonzero(true)),
845                    ],
846                ),
847            ),
848        );
849        let mut definitions = vec![("Int_divMod", arrow(int(), arrow(int(), pair_ty())), body)];
850        for (name, projection) in [("Int_div", "Prod_fst"), ("Int_mod", "Prod_snd")] {
851            definitions.push((
852                name,
853                int_binop(),
854                lam(
855                    "a",
856                    int(),
857                    lam(
858                        "b",
859                        int(),
860                        apps(
861                            g(projection),
862                            &[int(), int(), apps(g("Int_divMod"), &[v("a"), v("b")])],
863                        ),
864                    ),
865                ),
866            ));
867        }
868        definitions
869    })
870}
871
872pub(crate) fn register_public_division_aliases(ctx: &mut Context) -> KernelResult<()> {
873    for (name, ty, body) in integer_division_definitions() {
874        ctx.declare_checked_auxiliary(name, ty.clone(), body.clone())?;
875    }
876    for (name, target) in [("div", "Int_div"), ("mod", "Int_mod")] {
877        let ty = ctx.get_global(name).cloned().unwrap_or_else(int_binop);
878        ctx.declare_checked_auxiliary(name, ty, g(target))?;
879    }
880    Ok(())
881}
882
883/// The generated product projections and positional Bool inventory are part of
884/// division's meaning, so compare with the same builders used by the prelude.
885fn canonical_division_products(ctx: &Context) -> bool {
886    let reference = crate::prelude::StandardLibrary::division_carrier_reference();
887    for family in ["Bool", "Prod"] {
888        if !ctx.is_inductive(family)
889            || ctx.get_global(family) != reference.get_global(family)
890            || ctx.inductive_num_params(family) != reference.inductive_num_params(family)
891            || ctx.poly_declaration(family).is_some()
892            || ctx.get_definition_body(family).is_some()
893        {
894            return false;
895        }
896        let actual = ctx.get_constructors(family);
897        let expected = reference.get_constructors(family);
898        if actual != expected {
899            return false;
900        }
901        for (name, ty) in expected {
902            if ctx.constructor_inductive(name) != Some(family)
903                || ctx.get_global(name) != Some(ty)
904                || ctx.poly_declaration(name).is_some()
905                || ctx.get_definition_body(name).is_some()
906            {
907                return false;
908            }
909        }
910    }
911    ["Prod_fst", "Prod_snd"].into_iter().all(|name| {
912        ctx.poly_declaration(name).is_none()
913            && !ctx.is_constructor(name)
914            && !ctx.is_inductive(name)
915            && ctx.get_global(name) == reference.get_global(name)
916            && ctx.get_definition_type(name) == reference.get_definition_type(name)
917            && ctx.get_definition_body(name) == reference.get_definition_body(name)
918    })
919}
920
921/// Definitions whose delta steps a closed public arithmetic acceleration skips.
922pub(crate) fn integer_alias_dependencies(operation: &str) -> Option<&'static [&'static str]> {
923    match operation {
924        "add" => Some(&[
925            "add",
926            "Int_add",
927            "Int_subNatNat",
928            "Nat_sub",
929            "Nat_pred",
930            "Nat_add",
931        ]),
932        "mul" => Some(&["mul", "Int_mul", "Int_negOfNat", "Nat_mul", "Nat_add"]),
933        "sub" => Some(&[
934            "sub",
935            "Int_sub",
936            "Int_neg",
937            "Int_negOfNat",
938            "Int_add",
939            "Int_subNatNat",
940            "Nat_sub",
941            "Nat_pred",
942            "Nat_add",
943        ]),
944        "le" => Some(&["le", "Int_le", "natLe"]),
945        "div" => Some(&[
946            "div",
947            "Int_div",
948            "Int_divMod",
949            "Prod_fst",
950            "Prod_snd",
951            "Nat_divMod",
952            "Nat_divModSucc",
953            "Nat_sub",
954            "Nat_pred",
955            "Int_negOfNat",
956            "natLt",
957            "natLe",
958        ]),
959        "mod" => Some(&[
960            "mod",
961            "Int_mod",
962            "Int_divMod",
963            "Prod_fst",
964            "Prod_snd",
965            "Nat_divMod",
966            "Nat_divModSucc",
967            "Nat_sub",
968            "Nat_pred",
969            "Int_negOfNat",
970            "natLt",
971            "natLe",
972        ]),
973        _ => None,
974    }
975}
976
977/// A definition may use the existing exact arithmetic implementation only when
978/// its complete program and canonical carriers match the checked constructors.
979/// Rebinding any dependency changes the registry generation and invalidates the
980/// context-owned attestation. Rewrite and transparency policy are not cached.
981pub(crate) fn accelerates_integer_alias(ctx: &Context, operation: &str) -> bool {
982    if !ctx.simp_rules().is_empty() {
983        return false;
984    }
985    let Some(dependencies) = integer_alias_dependencies(operation) else {
986        return false;
987    };
988    ctx.cached_integer_arithmetic(operation, || {
989        if !canonical_environment(ctx)
990            || !canonical_nat_environment(ctx)
991            || ctx.inductive_num_params("Nat") != 0
992            || ctx.inductive_num_params("Int") != 0
993        {
994            return false;
995        }
996        for name in ["Nat", "Zero", "Succ", "Int", "Int_ofNat", "Int_negSucc"] {
997            if ctx.poly_declaration(name).is_some() {
998                return false;
999            }
1000        }
1001        let target = match operation {
1002            "add" => "Int_add",
1003            "mul" => "Int_mul",
1004            "sub" => "Int_sub",
1005            "le" => "Int_le",
1006            "div" => "Int_div",
1007            "mod" => "Int_mod",
1008            _ => return false,
1009        };
1010        let has_signature = |ty: &Term| {
1011            if operation == "le" {
1012                has_integer_binary_result(ty, "Bool")
1013            } else {
1014                has_integer_binop_signature(ty)
1015            }
1016        };
1017        if ctx.poly_declaration(operation).is_some()
1018            || ctx.is_constructor(operation)
1019            || ctx.is_inductive(operation)
1020            || !ctx.get_global(operation).is_some_and(has_signature)
1021            || ctx.get_definition_body(operation) != Some(&g(target))
1022            || !ctx
1023                .get_definition_type(operation)
1024                .is_some_and(has_signature)
1025        {
1026            return false;
1027        }
1028        if operation == "le" {
1029            let (_, ty, body) = integer_order_definition();
1030            return ctx.poly_declaration("Int_le").is_none()
1031                && !ctx.is_constructor("Int_le")
1032                && !ctx.is_inductive("Int_le")
1033                && ctx.get_global("Int_le") == Some(ty)
1034                && ctx.get_definition_type("Int_le") == Some(ty)
1035                && ctx.get_definition_body("Int_le") == Some(body)
1036                && ctx.poly_declaration("natLe").is_none()
1037                && !ctx.is_constructor("natLe")
1038                && !ctx.is_inductive("natLe")
1039                && ctx.get_global("natLe") == ctx.get_definition_type("natLe")
1040                && crate::reduction::accelerates_nat_order(ctx, "natLe");
1041        }
1042        let division = matches!(operation, "div" | "mod");
1043        if division && !canonical_division_products(ctx) {
1044            return false;
1045        }
1046        dependencies[1..].iter().all(|name| {
1047            if division && matches!(*name, "Prod_fst" | "Prod_snd") {
1048                return true; // compared against their canonical generated bodies above
1049            }
1050            if division && matches!(*name, "natLt" | "natLe") {
1051                return ctx.poly_declaration(name).is_none()
1052                    && !ctx.is_constructor(name)
1053                    && !ctx.is_inductive(name)
1054                    && ctx.get_global(name) == ctx.get_definition_type(name)
1055                    && crate::reduction::accelerates_nat_order(ctx, name);
1056            }
1057            let Some((_, ty, body)) = nat_arithmetic_definitions()
1058                .iter()
1059                .chain(int_arithmetic_definitions())
1060                .chain(integer_division_definitions())
1061                .chain(crate::prelude::natural_arithmetic::division_definitions())
1062                .find(|(candidate, _, _)| candidate == name)
1063            else {
1064                return false;
1065            };
1066            ctx.poly_declaration(name).is_none()
1067                && !ctx.is_constructor(name)
1068                && !ctx.is_inductive(name)
1069                && ctx.get_global(name) == Some(ty)
1070                && ctx.get_definition_type(name) == Some(ty)
1071                && ctx.get_definition_body(name) == Some(body)
1072        })
1073    })
1074}
1075
1076/// Whether the public add/mul/sub vocabulary has the standard integer meaning.
1077///
1078/// Polynomial proof producers may preserve these operation names while exposing
1079/// surrounding definitions. Reuse the evaluator's complete checked-alias
1080/// attestation, including mutable dependencies and registered rewrite policy;
1081/// arbitrary same-spelled definitions must not receive a ring interpretation.
1082/// This does not certify a generated proof, which still requires kernel checking.
1083pub fn supports_integer_polynomial_operations(ctx: &Context) -> bool {
1084    accelerates_integer_alias(ctx, "add")
1085        && accelerates_integer_alias(ctx, "mul")
1086        && accelerates_integer_alias(ctx, "sub")
1087}
1088
1089/// Public arithmetic has a checked meaning on symbolic operands as well as
1090/// literals. Exact literal evaluation is only an acceleration of these bodies.
1091pub(crate) fn register_public_arithmetic_aliases(ctx: &mut Context) -> KernelResult<()> {
1092    for (name, target) in [("add", "Int_add"), ("mul", "Int_mul"), ("sub", "Int_sub")] {
1093        ctx.declare(name, int_binop(), g(target))?;
1094    }
1095    Ok(())
1096}
1097
1098#[cfg(test)]
1099#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/checked_alias_attestation_controls.rs"]
1100mod checked_alias_attestation_controls;
1101
1102#[cfg(test)]
1103#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/alias_visible_header_control.rs"]
1104mod alias_visible_header_control;
1105
1106#[cfg(test)]
1107#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/subtraction_alias_adversaries.rs"]
1108mod subtraction_alias_adversaries;
1109
1110#[cfg(test)]
1111#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/integer_alias_signature_controls.rs"]
1112mod integer_alias_signature_controls;
1113
1114#[cfg(test)]
1115#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/checked_order_alias_adversaries.rs"]
1116mod checked_order_alias_adversaries;
1117
1118#[cfg(test)]
1119#[path = "../../logicaffeine_tests/unit/logicaffeine_kernel/int_inductive/checked_division_attestation_controls.rs"]
1120mod checked_division_attestation_controls;