/- Topolei.Cubical.Eval ==================== Environment-based evaluator for the cubical calculus (cells-spec §5.4, Phase 1 Week 2). `eval env t` reduces `t` to weak-head normal form in environment `env`. Three mutually-recursive pieces: · `eval` : `CEnv → CTerm → CVal` — main evaluator · `vApp` : `CVal → CVal → CVal` — function application at the value level · `vPApp` : `CVal → DimExpr → CVal` — path (dimension) application at value level Coverage: the *λ-calculus fragment* — `var`/`lam`/`app`/`plam`/`papp`. `transp`/`comp` produce stuck neutrals (`ntransp`/`ncomp`); their real reduction rules come with `Transport.lean` and `Comp.lean` (Weeks 3–4). These are `partial def`s. Termination isn't statically checked because the `vPApp` β-step substitutes inside the closure body and re-evaluates; the result is the same `CTerm` size, but Lean's structural recursion can't see through that. A future total version will measure on a subject-reduction metric. For now, `partial def` is the honest choice. -/ import CubicalTransport.Value import CubicalTransport.Transport -- ── Rust FFI declarations (Phase C.2) ────────────────────────────────────── -- `@[extern "topolei_cubical_*"] opaque *Rust ...` declares the Rust -- entry point. `@[implemented_by]` on each partial def routes runtime -- calls to Rust (kernel-level proof reasoning still uses the axioms). @[extern "topolei_cubical_eval"] opaque evalRust (env : CEnv) : CTerm → CVal @[extern "topolei_cubical_vapp"] opaque vAppRust : CVal → CVal → CVal @[extern "topolei_cubical_vpapp"] opaque vPAppRust : CVal → DimExpr → CVal @[extern "topolei_cubical_vhcomp"] opaque vHCompValueRust (A : CType) (φ : FaceFormula) (tube base : CVal) : CVal @[extern "topolei_cubical_vcomp_term"] opaque vCompAtTermRust (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (u t : CTerm) : CVal @[extern "topolei_cubical_vcompn_term"] opaque vCompNAtTermRust (env : CEnv) (i : DimVar) (A : CType) (clauses : List (FaceFormula × CTerm)) (t : CTerm) : CVal @[extern "topolei_cubical_vfst"] opaque vFstRust : CVal → CVal @[extern "topolei_cubical_vsnd"] opaque vSndRust : CVal → CVal mutual /-- Evaluate a term in an environment. λ-fragment + dimension abstraction are reduced immediately; transport dispatches through `vTransp`; heterogeneous composition dispatches through `vCompAtTerm` (which has access to the raw CTerms so it can substitute at the term level for `comp_full`). This is a `partial def` because the β-step in `vPApp` substitutes inside a closure body (via `CTerm.substDim`) and re-evaluates — the resulting term is the same CTerm size, but Lean's structural-recursion checker can't see through that, and indeed an untyped Ω-like term genuinely diverges. A well-typed total evaluator arrives in a later phase. -/ @[implemented_by evalRust] partial def eval (env : CEnv) : CTerm → CVal | .var x => (env.lookup x).getD (.vneu (.nvar x)) | .lam x body => .vlam env x body | .app f a => vApp (eval env f) (eval env a) | .plam i body => .vplam env i body | .papp t r => vPApp (eval env t) r | .transp i A φ t => -- Priority order: (1) full face, (2) constant line, (3) path -- (needs env), (4) glue (dispatched via dedicated axioms in -- `Glue.lean`; the partial def produces a stuck neutral as a -- runtime placeholder), (5) other via value-level `vTransp`. match φ with | .top => eval env t -- (1) T1 | _ => if CType.dimAbsent i A then eval env t -- (2) T2 else match A with | .path A₀ a b => .vPathTransp env i A₀ a b φ t -- (3) | .glue _ _ _ _ _ _ _ _ => -- (4) Glue -- Real reduction rules live in `Glue.lean` as dedicated -- axioms keyed on specific sub-cases (e.g. constant -- equivalence components). The partial def produces -- `ntransp` as a placeholder — the same shape vTransp -- would produce on stuck glue — so that kernel-level -- proofs go exclusively through the Glue axioms. .vneu (.ntransp i A φ (eval env t)) | _ => vTransp i A φ (eval env t) -- (5) | .comp i A φ u t => vCompAtTerm env i A φ u t | .compN i A clauses t => vCompNAtTerm env i A clauses t | .glueIn φ t a => -- Face-priority dispatch: φ = .top → t-side, φ = .bot → a-side, -- else stuck `.nglueIn` preserving both face-on / face-off values. match φ with | .top => eval env t | .bot => eval env a | _ => .vneu (.nglueIn φ (eval env t) (eval env a)) | .unglue φ f g => -- Face-priority dispatch: -- φ = .top: apply the forward map; the argument is a T-value. -- φ = .bot: identity — on the empty face, the glued term is the -- A-value `g` itself (consistent with `glueIn .bot t a → a`). -- Else: stuck `.nunglue` preserving `f` and `g`. match φ with | .top => vApp (eval env f) (eval env g) | .bot => eval env g | _ => .vneu (.nunglue φ (eval env f) (eval env g)) | .pair a b => .vpair (eval env a) (eval env b) | .fst t => vFst (eval env t) | .snd t => vSnd (eval env t) -- REL1 inductive-type constructors. | .dimExpr r => .vdimExpr r | .ctor S c params args => -- Produce a canonical constructor value with all args evaluated. -- (Boundary firing for path ctors lands in a follow-up — REL1 -- design doc §4: when a `.dim`-typed arg evaluates to .zero/.one -- and matches a clause in `S.ctors[c].boundary`, we should fire -- that clause body instead. TODO REL1.1.) .vctor S c params (args.map (eval env)) | .indElim S params motive branches target => -- β-reduce on a canonical `vctor`; otherwise build a stuck -- `.nIndElim` that preserves env-evaluated motive and branches -- so it can unstick when target later resolves. match eval env target with | .vctor _ c _ argsV => match List.lookup c branches with | some body => -- Apply branch body to constructor's args via repeated -- `vApp` — the body is curried: λ a₁ … aₙ. result. -- Recursive-arg hypotheses (the IH passed alongside each -- `.self` arg in the design doc §5) are not yet wired -- here; an `indElim` of a non-recursive ctor is fully -- handled, but recursive ctors land their branch body -- with only the constructor args (no IH) — TODO REL1.1. argsV.foldl (fun f a => vApp f a) (eval env body) | none => .vneu (.nvar s!"") | .vneu n => .vneu (.nIndElim S params (eval env motive) (branches.map (fun (nm, b) => (nm, eval env b))) n) | _ => .vneu (.nvar "") /-- First projection at the value level. β-reduces `vpair`; pushes a stuck neutral into `nfst`. Projecting any other value shape is a type error (marker neutral). -/ @[implemented_by vFstRust] partial def vFst : CVal → CVal | .vpair a _ => a | .vneu n => .vneu (.nfst n) | _ => .vneu (.nvar "") /-- Second projection at the value level. Mirror of `vFst`. -/ @[implemented_by vSndRust] partial def vSnd : CVal → CVal | .vpair _ b => b | .vneu n => .vneu (.nsnd n) | _ => .vneu (.nvar "") /-- Apply one value to another. β-reduces `vlam` closures; pushes stuck neutrals into `napp`. `vTranspFun` unfolds per the *full* CCHM Π rule. `vHCompFun` unfolds per the CCHM Π hcomp rule: apply pointwise through the tube, then recursively hcomp on the codomain. Applying a `vplam` or `vTubeApp` as a function is a type error. -/ @[implemented_by vAppRust] partial def vApp : CVal → CVal → CVal | .vlam env x body, arg => eval (env.extend x arg) body | .vneu n, arg => .vneu (.napp n arg) | .vTranspFun i domA codA φ f, arg => vTransp i codA φ (vApp f (vTranspInv i domA φ arg)) | .vHCompFun codA φ tube base, arg => vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg) -- Full CCHM Π hetero comp β-rule. See file-level comment below for -- the fill construction. | .vCompFun env i domA codA φ u t, arg => let yName := "$y" let fj : DimVar := ⟨"$fj"⟩ let newEnv := env.extend yName arg -- `filled_y_at_i`: a line in the *outer* binder `i` giving y_at_i:A(i). -- Build as `transp^{fj} (domA[i := (inv fj) ∨ i]) φ ($y)`: -- at fj=0: line slice is A(1) (source: y : A(1)) -- at fj=1: line slice is A(i) (target we want) let filled_y_at_i : CTerm := .transp fj (domA.substDimExpr i (.join (.inv (.var fj)) (.var i))) φ (.var yName) -- `filled_y_at_0`: substitute i := 0 in the above — the reversed -- line is `A[i := inv fj]`, taking A(1) → A(0). let filled_y_at_0 : CTerm := .transp fj (domA.substDimExpr i (.inv (.var fj))) φ (.var yName) eval newEnv (.comp i codA φ (.app u filled_y_at_i) (.app t filled_y_at_0)) | .vplam _ _ _, _ => .vneu (.nvar "") | .vTubeApp _ _, _ => .vneu (.nvar "") | .vPathTransp _ _ _ _ _ _ _, _ => .vneu (.nvar "") | .vpair _ _, _ => .vneu (.nvar "") | .vctor _ _ _ _, _ => .vneu (.nvar "") | .vdimExpr _, _ => .vneu (.nvar "") /-- Apply a value to a dimension expression. β-reduces `vplam` closures by substituting the dim in the body and re-evaluating; pushes stuck neutrals into `npapp`. `vTubeApp tube arg` applied to dim `r` reduces to `vApp (vPApp tube r) arg` — the semantic meaning of `λj. (tube @ j) arg`. Marker neutrals for ill-typed applications. -/ @[implemented_by vPAppRust] partial def vPApp : CVal → DimExpr → CVal | .vplam env i body, r => eval env (body.substDim i r) | .vneu n, r => .vneu (.npapp n r) | .vTubeApp tube arg, r => vApp (vPApp tube r) arg -- Path transport endpoint reductions: at `.zero` / `.one` the CCHM -- multi-clause system degenerates (the (j=0) or (j=1) clause fires) -- and the result is the specified endpoint at i=1. At generic `r` -- we fall through to `vCompNAtTerm` on the CCHM multi-clause system -- `[φ ↦ p@r, (r=0) ↦ a, (r=1) ↦ b]` with base `p@r` — which reduces -- further if the face substitutions happen to simplify. | .vPathTransp env i _ a _ _ _, .zero => eval env (a.substDim i .one) | .vPathTransp env i _ _ b _ _, .one => eval env (b.substDim i .one) | .vPathTransp env i A a b φ p, r => vCompNAtTerm env i A [ (φ, .papp p r) , (FaceFormula.dimExprEq0 r, a) , (FaceFormula.dimExprEq1 r, b) ] (.papp p r) | .vlam _ _ _, _ => .vneu (.nvar "") | .vTranspFun _ _ _ _ _, _ => .vneu (.nvar "") | .vHCompFun _ _ _ _, _ => .vneu (.nvar "") | .vCompFun _ _ _ _ _ _ _, _ => .vneu (.nvar "") | .vpair _ _, _ => .vneu (.nvar "") | .vctor _ _ _ _, _ => .vneu (.nvar "") | .vdimExpr _, _ => .vneu (.nvar "") /-- Homogeneous composition at the value level. The type `A` is *homogeneous* (doesn't vary along `i`); the tube and base are already-evaluated values. Reducing cases (in match order): 1. `φ = .top` — tube covers everything, result is `tube @ 1`. 2. `A = .pi` — CCHM Π hcomp: construct a `vHCompFun` closure that applies pointwise when the function is applied to an argument. 3. Otherwise — stuck `.vneu (.nhcomp ...)`. Note the crucial difference from `vTransp`: no constant-line check, because hcomp is *already* homogeneous — constancy is built in. -/ @[implemented_by vHCompValueRust] partial def vHCompValue (A : CType) (φ : FaceFormula) (tube base : CVal) : CVal := match φ with | .top => vPApp tube .one | _ => match A with | .pi _domA codA => .vHCompFun codA φ tube base | _ => .vneu (.nhcomp A φ tube base) /-- Heterogeneous composition at the term level. Takes `u` and `t` as `CTerm`s (not `CVal`s) so that the `comp_full` reduction can perform substitution *at the term level* — `eval env (u.substDim i .one)` — which is in general different from `vPApp (eval env u) .one`. Reducing cases (in match order): 1. `φ = .top` — C1: `eval env (u[i := 1])`. 2. `φ = .bot` — C2: `eval env (transp i A .bot t)`. 3. `CType.dimAbsent i A = true` — constant line: heterogeneous comp reduces to hcomp on the fixed type `A`, with `.plam i u` as the tube. 4. Otherwise — stuck `.vneu (.ncomp ...)`. Note cases (1) and (2) are decidable at the `.top`/`.bot` head of `φ`; case (3) discriminates on `A` only after `.top`/`.bot` are ruled out. All four cases are mutually exclusive. -/ @[implemented_by vCompAtTermRust] partial def vCompAtTerm (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (u t : CTerm) : CVal := match φ with | .top => eval env (u.substDim i .one) | .bot => eval env (.transp i A .bot t) | _ => if CType.dimAbsent i A then -- Constant line: hetero comp reduces to hcomp. `.plam i u` is the -- tube as a dim-closure. vHCompValue A φ (eval env (.plam i u)) (eval env t) else match A with | .pi domA codA => -- Hetero Π comp: package into a `vCompFun` closure. The CCHM -- β-rule runs at `vApp`-time with a full fill-based tube. .vCompFun env i domA codA φ u t | _ => .vneu (.ncomp i A φ (eval env u) (eval env t)) /-- Multi-clause heterogeneous composition at the term level. Dispatches by scanning the clause list: · If any clause has face `.top`, that clause's body substituted at `i := .one` is the result (CCHM multi-clause full-system rule). · If all clause faces are `.bot` or the list is empty, no clause fires — reduces to plain transport of the base. · If exactly one clause has a non-trivial face, delegate to `vCompAtTerm` (single-clause specialisation). · Otherwise produce a stuck `ncompN` neutral preserving env, line binder, type, evaluated clauses, and evaluated base. -/ @[implemented_by vCompNAtTermRust] partial def vCompNAtTerm (env : CEnv) (i : DimVar) (A : CType) (clauses : List (FaceFormula × CTerm)) (t : CTerm) : CVal := -- Scan for a `.top` clause first. match clauses.find? (fun ⟨φ, _⟩ => match φ with | .top => true | _ => false) with | some ⟨_, u⟩ => eval env (u.substDim i .one) | none => -- Strip `.bot` clauses (they never fire). let live := clauses.filter (fun ⟨φ, _⟩ => match φ with | .bot => false | _ => true) match live with | [] => eval env (.transp i A .bot t) | [⟨φ, u⟩] => vCompAtTerm env i A φ u t | _ => .vneu (.ncompN env i A (live.map (fun ⟨φ, u⟩ => (φ, eval env u))) (eval env t)) end /-! ## Reduction lemmas (axioms) `partial def` is opaque at the kernel level, so the defining cases of `eval`, `vApp`, and `vPApp` are not reducible by `rfl`. We state them as axioms — the same pattern used for `CTerm.step` and `step_papp_plam` in `Syntax.lean`. They exactly mirror the `partial def` match arms above, so they are consistent with the runtime implementation while also being usable in kernel-level proofs. -/ -- Reduction lemmas for `eval`. axiom eval_var (env : CEnv) (x : String) : eval env (.var x) = (env.lookup x).getD (.vneu (.nvar x)) axiom eval_lam (env : CEnv) (x : String) (body : CTerm) : eval env (.lam x body) = .vlam env x body axiom eval_app (env : CEnv) (f a : CTerm) : eval env (.app f a) = vApp (eval env f) (eval env a) axiom eval_plam (env : CEnv) (i : DimVar) (body : CTerm) : eval env (.plam i body) = .vplam env i body axiom eval_papp (env : CEnv) (t : CTerm) (r : DimExpr) : eval env (.papp t r) = vPApp (eval env t) r /-! ### `eval` on `.transp` — four disjoint cases Replaces the earlier coarse `eval_transp` axiom. Match-arm priority is: 1. `.top` face → identity (T1 at eval level). 2. Constant line → identity (T2 at eval level). 3. `.path A₀ a b` line, non-constant → `vPathTransp` closure. 4. Non-path, non-constant line → delegate to value-level `vTransp`. The four cases are mutually exclusive by precondition, so the axiom set is consistent. -/ /-- (1) T1 at eval level: transport under a full face is identity. -/ axiom eval_transp_top (env : CEnv) (i : DimVar) (A : CType) (t : CTerm) : eval env (.transp i A .top t) = eval env t /-- (2) T2 at eval level: transport along a constant line is identity. Covers `.univ`, constant-`pi`, and constant-`path` lines uniformly. -/ axiom eval_transp_const (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (t : CTerm) (hφ : φ ≠ .top) (hA : CType.dimAbsent i A = true) : eval env (.transp i A φ t) = eval env t /-- (3) Path transport: when the line's body is `.path A₀ a b` with the whole path-line genuinely varying, produce a `vPathTransp` closure that captures the endpoint CTerms `a`/`b` along with the env and the path term `t` (kept as a CTerm so later `.papp t r` constructions work for the multi-clause reduction at generic dim). Reduces further under `vPApp` at endpoints. -/ axiom eval_transp_path (env : CEnv) (i : DimVar) (A₀ : CType) (a b : CTerm) (φ : FaceFormula) (t : CTerm) (hφ : φ ≠ .top) (hA : CType.dimAbsent i (.path A₀ a b) = false) : eval env (.transp i (.path A₀ a b) φ t) = .vPathTransp env i A₀ a b φ t /-- (4) Non-path non-glue non-constant transport: delegate to the value-level `vTransp`, which is env-agnostic and handles `.pi` via `vTranspFun`. `.glue` is excluded because its CCHM transport formula lives in dedicated Glue-specific axioms (see `Glue.lean`); routing it through `vTransp` here would claim it reduces to a stuck neutral, which would contradict those axioms in their specific sub-cases. -/ axiom eval_transp_nonpath (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (t : CTerm) (hφ : φ ≠ .top) (hA : CType.dimAbsent i A = false) (h_not_path : ∀ A₀ a b, A ≠ .path A₀ a b) (h_not_glue : ∀ φG T f fInv sec ret coh Ai, A ≠ .glue φG T f fInv sec ret coh Ai) : eval env (.transp i A φ t) = vTransp i A φ (eval env t) /-- Π-case theorem (full CCHM): transport along any `pi domA codA` line produces a `vTranspFun` closure. Derived via `eval_transp_nonpath` (`pi ≠ path` and `pi ≠ glue` by constructor disjointness) plus `vTransp_pi`. -/ theorem eval_transp_pi (env : CEnv) (i : DimVar) (domA codA : CType) (φ : FaceFormula) (t : CTerm) (hφ : φ ≠ .top) (hA : CType.dimAbsent i (.pi domA codA) = false) : eval env (.transp i (.pi domA codA) φ t) = .vTranspFun i domA codA φ (eval env t) := by rw [eval_transp_nonpath env i _ φ t hφ hA (by intro _ _ _ h; nomatch h) (by intro _ _ _ _ _ _ _ _ h; nomatch h)] exact vTransp_pi _ _ _ _ _ hφ hA /-- Stuck fallback theorem. In our current `CType` universe `{univ, pi, path, glue}`, this never actually fires in practice: `univ` always has `dimAbsent = true`, so the const case wins; `pi` is handled by `eval_transp_pi`; `path` is handled by `eval_transp_path`; `glue` is handled by the dedicated Glue-transport axioms in `Glue.lean`. Kept here for parity with `vTransp_stuck` and future CType extensions. -/ theorem eval_transp_stuck (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (t : CTerm) (hφ : φ ≠ .top) (hA : CType.dimAbsent i A = false) (h_not_pi : ∀ domA codA, A ≠ .pi domA codA) (h_not_path : ∀ A₀ a b, A ≠ .path A₀ a b) (h_not_glue : ∀ φG T f fInv sec ret coh Ai, A ≠ .glue φG T f fInv sec ret coh Ai) : eval env (.transp i A φ t) = .vneu (.ntransp i A φ (eval env t)) := by rw [eval_transp_nonpath env i A φ t hφ hA h_not_path h_not_glue] exact vTransp_stuck _ _ _ _ hφ hA h_not_pi /-- **T5 at eval level**: face-formula congruence for transport. When two face formulas are semantically equal — i.e. `φ.eval env = ψ.eval env` for every env — transport under them computes the same result. This is a claim about Rust's evaluator: it inspects face formulas only through their truth-value semantics, not their syntactic form. Two faces that classify the same set of environments produce the same transport reduction, regardless of their concrete tree structure (e.g. `(i=0) ∧ ⊤` vs `(i=0)`). Stated at the eval level (rather than augmenting the partial def with a face-normalisation prepass) so the existing eval-axiom set is not re-audited. The Rust backend's discharge: a face-normalisation routine ensures syntactically distinct but semantically equal faces take the same dispatch branch. -/ axiom eval_transp_face_congr (env : CEnv) (i : DimVar) (A : CType) (φ ψ : FaceFormula) (t : CTerm) (h : ∀ ε, φ.eval ε = ψ.eval ε) : eval env (.transp i A φ t) = eval env (.transp i A ψ t) /-! ### `eval` on `.comp` — four disjoint cases The old coarse `eval_comp` (which asserted the stuck form unconditionally) is replaced by four case-axioms mirroring the arms of `vCompAtTerm`. The cases are disjoint by precondition, so the axiom set is consistent. -/ /-- **C1 at eval level**: with a full face, heterogeneous composition reduces to the system body substituted at `i := 1`. Crucially this is *term-level* substitution, not `vPApp` on the evaluated body — `u` may be e.g. a free variable whose value doesn't look like a function. -/ axiom eval_comp_top (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) : eval env (.comp i A .top u t) = eval env (u.substDim i .one) /-- **C2 at eval level**: with an empty face, the system contributes nothing and composition reduces to plain transport. -/ axiom eval_comp_bot (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) : eval env (.comp i A .bot u t) = eval env (.transp i A .bot t) /-- **Constant-line comp = hcomp**: when the type `A` doesn't vary along `i`, heterogeneous composition reduces to homogeneous composition on the (fixed) type `A`. The tube is `.plam i u` — the system body `u` packaged as a dim-closure over the line binder. -/ axiom eval_comp_const (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (u t : CTerm) (hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot) (hA : CType.dimAbsent i A = true) : eval env (.comp i A φ u t) = vHCompValue A φ (eval env (.plam i u)) (eval env t) /-- **Heterogeneous Π comp**: when A is a pi type with a genuinely-varying line, `vCompAtTerm` packages the comp into a `vCompFun` closure that will run the CCHM β-rule when the function is applied. -/ axiom eval_comp_pi (env : CEnv) (i : DimVar) (domA codA : CType) (φ : FaceFormula) (u t : CTerm) (hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot) (hA : CType.dimAbsent i (.pi domA codA) = false) : eval env (.comp i (.pi domA codA) φ u t) = .vCompFun env i domA codA φ u t /-- Stuck fallback: `.comp` whose face is neither `.top` nor `.bot`, whose line genuinely varies, and whose type is neither `.pi` nor a constant produces a neutral. -/ axiom eval_comp_stuck (env : CEnv) (i : DimVar) (A : CType) (φ : FaceFormula) (u t : CTerm) (hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot) (hA : CType.dimAbsent i A = false) (h_not_pi : ∀ domA codA, A ≠ .pi domA codA) : eval env (.comp i A φ u t) = .vneu (.ncomp i A φ (eval env u) (eval env t)) /-- `eval` on `.compN` delegates to `vCompNAtTerm`. -/ axiom eval_compN (env : CEnv) (i : DimVar) (A : CType) (clauses : List (FaceFormula × CTerm)) (t : CTerm) : eval env (.compN i A clauses t) = vCompNAtTerm env i A clauses t /-! ### `vHCompValue` — three disjoint cases -/ /-- Homogeneous composition under a full face: the tube covers everything, and the result is the tube evaluated at `1`. -/ axiom vHCompValue_top (A : CType) (tube base : CVal) : vHCompValue A .top tube base = vPApp tube .one /-- **CCHM Π hcomp rule**: homogeneous composition on a Π type produces a `vHCompFun` closure that applies pointwise when its function is applied to an argument. `domA` is stored in the type but unused in the resulting closure because hcomp on the domain is trivial (A is fixed, not varying). -/ axiom vHCompValue_pi (domA codA : CType) (φ : FaceFormula) (tube base : CVal) (hφ : φ ≠ .top) : vHCompValue (.pi domA codA) φ tube base = .vHCompFun codA φ tube base /-- Stuck fallback: hcomp on a non-pi type under a non-top face. Uses `nhcomp` (separate from `ncomp` because hcomp's type is fixed). -/ axiom vHCompValue_stuck (A : CType) (φ : FaceFormula) (tube base : CVal) (hφ : φ ≠ .top) (h_not_pi : ∀ domA codA, A ≠ .pi domA codA) : vHCompValue A φ tube base = .vneu (.nhcomp A φ tube base) -- Reduction lemmas for `vApp`. axiom vApp_vlam (env : CEnv) (x : String) (body : CTerm) (arg : CVal) : vApp (.vlam env x body) arg = eval (env.extend x arg) body axiom vApp_vneu (n : CNeu) (arg : CVal) : vApp (.vneu n) arg = .vneu (.napp n arg) /-- Full CCHM Π β-rule at the value level: applying a transported-function closure to an argument `arg` inversely transports `arg` through the domain, applies the underlying function `f`, and forward-transports the result through the codomain. When `CType.dimAbsent i domA = true`, `vTranspInv` reduces to identity (by `vTranspInv_const`) and this specialises to the simpler const-domain rule `vTransp i codA φ (vApp f arg)`. -/ axiom vApp_vTranspFun (i : DimVar) (domA codA : CType) (φ : FaceFormula) (f : CVal) (arg : CVal) : vApp (.vTranspFun i domA codA φ f) arg = vTransp i codA φ (vApp f (vTranspInv i domA φ arg)) /-- **CCHM Π hcomp β-rule** at the value level: applying a homogeneously composed function closure to `arg` yields hcomp on the codomain with: · tube = `λj. (tube @ j) arg` (represented by `vTubeApp`), · base = `base arg`. No inverse transport — hcomp's type is fixed, so the argument passes through unchanged. -/ axiom vApp_vHCompFun (codA : CType) (φ : FaceFormula) (tube base arg : CVal) : vApp (.vHCompFun codA φ tube base) arg = vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg) /-- **Full CCHM Π hetero comp β-rule**: applying `comp^i (pi A B) φ u u₀` to `y : A(1)` unfolds via the *fill* construction. For a fresh dim `$fj` and argument name `$y`: · `y_at_i = transp^{$fj} (A[i := inv $fj ∨ i]) φ y` — fill from A(1) down to A(i). · `y_at_0 = transp^{$fj} (A[i := inv $fj]) φ y` — reversed-line transport A(1) → A(0). The result is `comp^i B(i) φ (u (y_at_i)) (u₀ (y_at_0))` evaluated in an env extending `$y` with the argument. When `CType.dimAbsent i domA = true`, both fills degenerate to `y` (by T2 on the constant line), recovering the simpler const-domain formula `comp^i B(i) φ (u y) (u₀ y)`. Hygiene assumption: `$y` is not a user variable in `env`, and `$fj` is not a user DimVar in `domA`, `codA`, `φ`, `u`, `t`. These reserved names are chosen to minimise collision probability. -/ axiom vApp_vCompFun (env : CEnv) (i : DimVar) (domA codA : CType) (φ : FaceFormula) (u t : CTerm) (arg : CVal) : vApp (.vCompFun env i domA codA φ u t) arg = eval (env.extend "$y" arg) (.comp i codA φ (.app u (.transp ⟨"$fj"⟩ (domA.substDimExpr i (.join (.inv (.var ⟨"$fj"⟩)) (.var i))) φ (.var "$y"))) (.app t (.transp ⟨"$fj"⟩ (domA.substDimExpr i (.inv (.var ⟨"$fj"⟩))) φ (.var "$y")))) -- Reduction lemmas for `vPApp`. axiom vPApp_vplam (env : CEnv) (i : DimVar) (body : CTerm) (r : DimExpr) : vPApp (.vplam env i body) r = eval env (body.substDim i r) axiom vPApp_vneu (n : CNeu) (r : DimExpr) : vPApp (.vneu n) r = .vneu (.npapp n r) /-- `vTubeApp tube arg` under dim application reduces to `(tube @ r) arg`. Encodes the semantic meaning of `λj. (tube @ j) arg`. -/ axiom vPApp_vTubeApp (tube arg : CVal) (r : DimExpr) : vPApp (.vTubeApp tube arg) r = vApp (vPApp tube r) arg /-! ### `vCompNAtTerm` — compound equation mirroring the partial-def arms Single axiom exposing the full case analysis so that derived theorems can pattern-match on the clause list's structure. -/ axiom vCompNAtTerm_def (env : CEnv) (i : DimVar) (A : CType) (clauses : List (FaceFormula × CTerm)) (t : CTerm) : vCompNAtTerm env i A clauses t = match clauses.find? (fun ⟨φ, _⟩ => match φ with | .top => true | _ => false) with | some ⟨_, u⟩ => eval env (u.substDim i .one) | none => let live := clauses.filter (fun ⟨φ, _⟩ => match φ with | .bot => false | _ => true) match live with | [] => eval env (.transp i A .bot t) | [⟨φ, u⟩] => vCompAtTerm env i A φ u t | _ => .vneu (.ncompN env i A (live.map (fun ⟨φ, u⟩ => (φ, eval env u))) (eval env t)) /-! ### Path transport endpoint reductions Applied at `.zero`, the CCHM `(j=0)` clause fires, giving `a(1)`. Applied at `.one`, the `(j=1)` clause fires, giving `b(1)`. Applied at any other DimExpr, the multi-clause comp can't be computed without machinery we haven't built yet — stalls at `npathTranspApp`. The three axioms are disjoint by the shape of the DimExpr argument. -/ /-- Path transport at left endpoint: result is `a(1)`, i.e. `a` evaluated with `i` substituted by `.one`. This is *not* a transport of `a(0)` — it's the line's specified endpoint at `i=1`, made forced by CCHM's multi-clause `(j=0)` constraint. -/ axiom vPApp_vPathTransp_zero (env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula) (p : CTerm) : vPApp (.vPathTransp env i A a b φ p) .zero = eval env (a.substDim i .one) /-- Path transport at right endpoint: result is `b(1)`. -/ axiom vPApp_vPathTransp_one (env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula) (p : CTerm) : vPApp (.vPathTransp env i A a b φ p) .one = eval env (b.substDim i .one) /-- **Full CCHM path transport at a generic dim**: apply the path transport at `r` by evaluating the CCHM multi-clause comp `comp^i A [φ ↦ p@r, (r=0) ↦ a, (r=1) ↦ b] (p@r)` via `vCompNAtTerm`. This genuinely unsticks when `r`'s face substitution simplifies: · `r = .zero` → `(r=0)` becomes `.top`, firing the left clause → `a(1)`. · `r = .one` → `(r=1)` becomes `.top`, firing the right clause → `b(1)`. · `r = .var k` generic → both clauses are non-trivial; stalls at a structured `ncompN` neutral that can still unstick if `k` later becomes an endpoint. -/ axiom vPApp_vPathTransp_general (env : CEnv) (i : DimVar) (A : CType) (a b : CTerm) (φ : FaceFormula) (p : CTerm) (r : DimExpr) (h_zero : r ≠ .zero) (h_one : r ≠ .one) : vPApp (.vPathTransp env i A a b φ p) r = vCompNAtTerm env i A [ (φ, .papp p r) , (FaceFormula.dimExprEq0 r, a) , (FaceFormula.dimExprEq1 r, b) ] (.papp p r) /-! ### `eval` on `.glueIn` — three disjoint cases `glueIn [φ ↦ t] a` evaluates differently on/off/between the face `φ`: · `φ = .top`: the face is full, the result is `t` (only the T-side matters). · `φ = .bot`: the face is empty, the result is `a` (only the A-side matters). · Otherwise: a structurally stuck `nglueIn` neutral preserving both face-on and face-off values — later dim substitution into `φ` may collapse it. The three cases are mutually exclusive by precondition. -/ /-- (1) Full-face glueIn reduces to the T-side. -/ axiom eval_glueIn_top (env : CEnv) (t a : CTerm) : eval env (.glueIn .top t a) = eval env t /-- (2) Empty-face glueIn reduces to the A-side. -/ axiom eval_glueIn_bot (env : CEnv) (t a : CTerm) : eval env (.glueIn .bot t a) = eval env a /-- (3) Neutral-face glueIn produces an `nglueIn` stuck neutral preserving both evaluated sides. The face formula is kept syntactic so that later dim substitution can resolve it to `.top` or `.bot`. The `h_not_unglue` precondition rules out the η-redex shape `.glueIn φ t (.unglue φ f g)`, which reduces via `eval_glueIn_of_unglue` to `eval env g` instead of a stuck form. Without this restriction, the stuck rule and the η-rule would disagree on a common instance. -/ axiom eval_glueIn_stuck (env : CEnv) (φ : FaceFormula) (t a : CTerm) (hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot) (h_not_unglue : ∀ f g, a ≠ .unglue φ f g) : eval env (.glueIn φ t a) = .vneu (.nglueIn φ (eval env t) (eval env a)) /-! ### `eval` on `.unglue` — three disjoint cases `unglue [φ ↦ f] g` — extracting an A-value from a glued term: · `φ = .top`: apply the forward map: `vApp (eval f) (eval g)`. · `φ = .bot`: identity. `glueIn .bot t a` reduces to `a`, so the argument is already an A-value; unglue is the identity there. · Otherwise: stuck `nunglue` preserving `f` and `g`. All three cases are mutually exclusive. -/ /-- (1) Full-face unglue: apply the forward map pointwise. -/ axiom eval_unglue_top (env : CEnv) (f g : CTerm) : eval env (.unglue .top f g) = vApp (eval env f) (eval env g) /-- (2) Empty-face unglue: identity on `g`. This is the definitional content of `Glue [bot ↦ (T, e)] A = A`: values are already A-values. -/ axiom eval_unglue_bot (env : CEnv) (f g : CTerm) : eval env (.unglue .bot f g) = eval env g /-- (3) Neutral-face unglue: produce a stuck `nunglue` neutral preserving `f` and `g`. Later dim substitution into `φ` may resolve it to `.top` (→ forward-map application) or `.bot` (→ identity). The `h_not_glueIn` precondition rules out the β-redex shape `.unglue φ f (.glueIn φ t a)`, which reduces via `eval_unglue_of_glueIn` to `eval env a` under the overlap condition. Without this restriction, the stuck rule and the β-rule would disagree on a common instance. -/ axiom eval_unglue_stuck (env : CEnv) (φ : FaceFormula) (f g : CTerm) (hφ₁ : φ ≠ .top) (hφ₂ : φ ≠ .bot) (h_not_glueIn : ∀ t a, g ≠ .glueIn φ t a) : eval env (.unglue φ f g) = .vneu (.nunglue φ (eval env f) (eval env g)) /-! ### Glue β- and η-rules (eval level) The β-rule (`unglue · glueIn`) and η-rule (`glueIn · unglue`) on matching face formulas fire *regardless* of whether `φ` is `.top`, `.bot`, or stuck. On `.top` / `.bot` the existing face-dispatched axioms reduce to the same result under the overlap condition (consistency check below); on stuck faces, these rules are the only way the redex reduces — `eval_unglue_stuck` and `eval_glueIn_stuck` carry `h_not_glueIn` / `h_not_unglue` preconditions to avoid conflict. The `h_overlap` condition encodes the CCHM typing-side constraint `e.f t = a` (or `t = e.f g`) that accompanies a well-typed glueIn / unglue — the evaluator assumes it and short-circuits. -/ /-- β-rule: `unglue φ f (glueIn φ t a)` reduces to `a` under the overlap condition. Rust-discharge: the evaluator recognises the nested pattern and short-circuits when the overlap invariant holds (typing guarantees it). -/ axiom eval_unglue_of_glueIn (env : CEnv) (φ : FaceFormula) (f t a : CTerm) (h_overlap : eval env (.app f t) = eval env a) : eval env (.unglue φ f (.glueIn φ t a)) = eval env a /-- η-rule: `glueIn φ t (unglue φ f g)` reduces to `g` under the overlap condition. Rust-discharge: dual to `eval_unglue_of_glueIn`. -/ axiom eval_glueIn_of_unglue (env : CEnv) (φ : FaceFormula) (f t g : CTerm) (h_overlap : eval env t = eval env (.app f g)) : eval env (.glueIn φ t (.unglue φ f g)) = eval env g /-! ### `eval` on Σ constructors — three arms `.pair a b` evaluates component-wise to `.vpair`; `.fst t` / `.snd t` project through `vFst` / `vSnd`, which themselves β-reduce on `vpair` and produce stuck `.nfst` / `.nsnd` on neutrals. -/ /-- Pair construction evaluates component-wise. -/ axiom eval_pair (env : CEnv) (a b : CTerm) : eval env (.pair a b) = .vpair (eval env a) (eval env b) /-- First projection delegates to `vFst`. -/ axiom eval_fst (env : CEnv) (t : CTerm) : eval env (.fst t) = vFst (eval env t) /-- Second projection delegates to `vSnd`. -/ axiom eval_snd (env : CEnv) (t : CTerm) : eval env (.snd t) = vSnd (eval env t) /-- β-rule for `vFst` on a pair. -/ axiom vFst_vpair (a b : CVal) : vFst (.vpair a b) = a /-- β-rule for `vSnd` on a pair. -/ axiom vSnd_vpair (a b : CVal) : vSnd (.vpair a b) = b /-- `vFst` on a neutral produces a stuck `nfst` neutral. -/ axiom vFst_vneu (n : CNeu) : vFst (.vneu n) = .vneu (.nfst n) /-- `vSnd` on a neutral produces a stuck `nsnd` neutral. -/ axiom vSnd_vneu (n : CNeu) : vSnd (.vneu n) = .vneu (.nsnd n)