/- Topolei.Cubical.DimLine ======================= Lines of types — the domain of transport (Step 2 of the transport plan). A DimLine is a type abstracted over one dimension variable: λi. A This is the argument to transpⁱ (λi. A) φ t₀. The two endpoints are: at0 = A[i := false] — the type at i = 0 at1 = A[i := true] — the type at i = 1 A constant line const A i packages A with binder i. When i does not appear free in A, at0 = at1 = A. The face connection (atBool_eq0_face / atBool_eq1_face) states that whenever eq0 i or eq1 i holds in an environment, the environment's value at i selects the correct endpoint type. -/ import CubicalTransport.Subst -- ── DimLine ─────────────────────────────────────────────────────────────────── /-- A line of types: a CType abstracted over one dimension variable. -/ structure DimLine where binder : DimVar body : CType -- ── Endpoint projections ────────────────────────────────────────────────────── def DimLine.at0 (L : DimLine) : CType := L.body.substDim L.binder false def DimLine.at1 (L : DimLine) : CType := L.body.substDim L.binder true def DimLine.atBool (L : DimLine) (b : Bool) : CType := L.body.substDim L.binder b -- ── atBool reduction lemmas ─────────────────────────────────────────────────── theorem DimLine.atBool_false (L : DimLine) : L.atBool false = L.at0 := rfl theorem DimLine.atBool_true (L : DimLine) : L.atBool true = L.at1 := rfl theorem DimLine.atBool_cases (L : DimLine) (b : Bool) : L.atBool b = if b then L.at1 else L.at0 := by cases b <;> rfl -- ── Constant line ───────────────────────────────────────────────────────────── def DimLine.const (A : CType) (i : DimVar) : DimLine := { binder := i, body := A } theorem DimLine.const_at0 (A : CType) (i : DimVar) : (DimLine.const A i).at0 = A.substDim i false := rfl theorem DimLine.const_at1 (A : CType) (i : DimVar) : (DimLine.const A i).at1 = A.substDim i true := rfl -- ── Absent dimension ────────────────────────────────────────────────────────── -- Syntactic predicate: i does not appear in any DimExpr within the term/type. -- `DimExpr.dimAbsent` is defined in Interval.lean (its natural home). mutual def CTerm.dimAbsent (i : DimVar) : CTerm → Bool | .var _ => true | .lam _ t => t.dimAbsent i | .app f a => f.dimAbsent i && a.dimAbsent i | .plam j t => if j = i then true else t.dimAbsent i | .papp t r => t.dimAbsent i && r.dimAbsent i -- transp/comp: A is ignored here (matching the same approximation -- in `CTerm.substDim`). φ is checked via FaceFormula.dimAbsent. | .transp _ _ φ t => φ.dimAbsent i && t.dimAbsent i | .comp _ _ φ u t => φ.dimAbsent i && u.dimAbsent i && t.dimAbsent i | .compN _ _ clauses t => CTerm.dimAbsent.clauses i clauses && t.dimAbsent i -- Glue introduction / elimination: check face + all sub-terms. | .glueIn φ t a => φ.dimAbsent i && t.dimAbsent i && a.dimAbsent i | .unglue φ f g => φ.dimAbsent i && f.dimAbsent i && g.dimAbsent i -- Σ constructors: structural recursion into sub-terms. | .pair a b => a.dimAbsent i && b.dimAbsent i | .fst t => t.dimAbsent i | .snd t => t.dimAbsent i /-- Helper: check that `i` is absent from every clause in a system. -/ def CTerm.dimAbsent.clauses (i : DimVar) : List (FaceFormula × CTerm) → Bool | [] => true | (φ, u) :: rest => φ.dimAbsent i && u.dimAbsent i && CTerm.dimAbsent.clauses i rest end def CType.dimAbsent (i : DimVar) : CType → Bool | .univ => true | .pi A B => A.dimAbsent i && B.dimAbsent i | .path A a t => A.dimAbsent i && a.dimAbsent i && t.dimAbsent i | .sigma A B => A.dimAbsent i && B.dimAbsent i | .glue φ T f fInv sec ret coh A => φ.dimAbsent i && T.dimAbsent i && f.dimAbsent i && fInv.dimAbsent i && sec.dimAbsent i && ret.dimAbsent i && coh.dimAbsent i && A.dimAbsent i -- ── Absence → subst is identity: DimExpr level ─────────────────────────────── -- "sub `var i → X` in `r` leaves r unchanged when i is absent from r" -- Note: DimExpr.subst i X r substitutes (var i → X) in r. -- Dot notation r.subst i X inserts r as the *replacement*, not target — -- so we write the explicit form DimExpr.subst i X r here. theorem DimExpr.subst_of_absent (i : DimVar) (X : DimExpr) (r : DimExpr) (h : r.dimAbsent i = true) : DimExpr.subst i X r = r := by induction r with | zero => rfl | one => rfl | var j => simp only [dimAbsent, bne_iff_ne] at h simp only [DimExpr.subst, if_neg h] | inv r ih => simp only [dimAbsent] at h; simp [DimExpr.subst, ih h] | meet r s ihr ihs => simp only [dimAbsent, Bool.and_eq_true] at h simp [DimExpr.subst, ihr h.1, ihs h.2] | join r s ihr ihs => simp only [dimAbsent, Bool.and_eq_true] at h simp [DimExpr.subst, ihr h.1, ihs h.2] -- ── Absence → subst is identity: CTerm and CType ───────────────────────────── -- These require simultaneous induction on the CTerm/CType mutual inductive. -- The `induction` tactic is blocked for mutual types, so we use a `def` -- (which Lean accepts for structural recursion via match) to build the proof. -- -- Generalised to arbitrary `r : DimExpr` (not just Bool endpoints) — needed -- for line reversal in transport, where `r = inv i`. The Bool version is -- a corollary at the canonical endpoint DimExpr. mutual private def CTerm.substDim_absent_aux (i : DimVar) (r : DimExpr) : (t : CTerm) → CTerm.dimAbsent i t = true → CTerm.substDim i r t = t | .var _, _ => rfl | .lam x t, h => by simp only [CTerm.dimAbsent] at h show CTerm.lam x (t.substDim i r) = CTerm.lam x t congr 1; exact CTerm.substDim_absent_aux i r t h | .app f a, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h show CTerm.app (f.substDim i r) (a.substDim i r) = CTerm.app f a congr 1 · exact CTerm.substDim_absent_aux i r f h.1 · exact CTerm.substDim_absent_aux i r a h.2 | .plam j t, h => by show (if j = i then CTerm.plam j t else CTerm.plam j (t.substDim i r)) = CTerm.plam j t by_cases hji : j = i · subst hji; simp only [ite_true] · simp only [if_neg hji] simp only [CTerm.dimAbsent, if_neg hji] at h exact congrArg (CTerm.plam j) (CTerm.substDim_absent_aux i r t h) | .papp t s, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h show CTerm.papp (t.substDim i r) (DimExpr.subst i r s) = CTerm.papp t s congr 1 · exact CTerm.substDim_absent_aux i r t h.1 · exact DimExpr.subst_of_absent i r s h.2 | .transp j A φ t, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] have hφ := FaceFormula.substDim_of_absent i r φ h.1 have ht := CTerm.substDim_absent_aux i r t h.2 rw [hφ, ht] | .comp j A φ u t, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] obtain ⟨⟨hφ, hu⟩, ht⟩ := h rw [FaceFormula.substDim_of_absent i r φ hφ, CTerm.substDim_absent_aux i r u hu, CTerm.substDim_absent_aux i r t ht] | .compN j A clauses t, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] rw [CTerm.substDim.clauses_of_absent i r clauses h.1, CTerm.substDim_absent_aux i r t h.2] | .glueIn φ t a, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] obtain ⟨⟨hφ, ht⟩, ha⟩ := h rw [FaceFormula.substDim_of_absent i r φ hφ, CTerm.substDim_absent_aux i r t ht, CTerm.substDim_absent_aux i r a ha] | .unglue φ f g, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] obtain ⟨⟨hφ, hf⟩, hg⟩ := h rw [FaceFormula.substDim_of_absent i r φ hφ, CTerm.substDim_absent_aux i r f hf, CTerm.substDim_absent_aux i r g hg] | .pair a b, h => by simp only [CTerm.dimAbsent, Bool.and_eq_true] at h simp only [CTerm.substDim] rw [CTerm.substDim_absent_aux i r a h.1, CTerm.substDim_absent_aux i r b h.2] | .fst t, h => by simp only [CTerm.dimAbsent] at h simp only [CTerm.substDim] rw [CTerm.substDim_absent_aux i r t h] | .snd t, h => by simp only [CTerm.dimAbsent] at h simp only [CTerm.substDim] rw [CTerm.substDim_absent_aux i r t h] /-- Helper: `substDim.clauses` is identity on clause lists whose every `(face, body)` pair has `i` absent. -/ private def CTerm.substDim.clauses_of_absent (i : DimVar) (r : DimExpr) : (clauses : List (FaceFormula × CTerm)) → CTerm.dimAbsent.clauses i clauses = true → CTerm.substDim.clauses i r clauses = clauses | [], _ => rfl | (φ, u) :: rest, h => by simp only [CTerm.dimAbsent.clauses, Bool.and_eq_true] at h simp only [CTerm.substDim.clauses] obtain ⟨⟨hφ, hu⟩, hrest⟩ := h rw [FaceFormula.substDim_of_absent i r φ hφ, CTerm.substDim_absent_aux i r u hu, CTerm.substDim.clauses_of_absent i r rest hrest] end /-- Generalised: `CTerm.substDim i r t = t` whenever `i` is absent from `t`, for any DimExpr `r`. -/ theorem CTerm.substDim_of_absent (i : DimVar) (r : DimExpr) (t : CTerm) (h : CTerm.dimAbsent i t = true) : CTerm.substDim i r t = t := CTerm.substDim_absent_aux i r t h /-- Bool-endpoint corollary of `CTerm.substDim_of_absent`. -/ theorem CTerm.substDimBool_of_absent (i : DimVar) (b : Bool) (t : CTerm) (h : CTerm.dimAbsent i t = true) : CTerm.substDimBool i b t = t := by unfold CTerm.substDimBool exact CTerm.substDim_of_absent i _ t h private def CType.substDim_absent_aux (i : DimVar) (b : Bool) : (A : CType) → CType.dimAbsent i A = true → CType.substDim i b A = A | .univ, _ => rfl | .pi A B, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.pi (CType.substDim i b A) (CType.substDim i b B) = CType.pi A B congr 1 · exact CType.substDim_absent_aux i b A h.1 · exact CType.substDim_absent_aux i b B h.2 | .path A a t, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.path (CType.substDim i b A) (CTerm.substDimBool i b a) (CTerm.substDimBool i b t) = CType.path A a t congr 1 · exact CType.substDim_absent_aux i b A h.1.1 · exact CTerm.substDimBool_of_absent i b a h.1.2 · exact CTerm.substDimBool_of_absent i b t h.2 | .sigma A B, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.sigma (CType.substDim i b A) (CType.substDim i b B) = CType.sigma A B congr 1 · exact CType.substDim_absent_aux i b A h.1 · exact CType.substDim_absent_aux i b B h.2 | .glue φ T f fInv sec ret coh A, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.glue (φ.substDim i (if b then DimExpr.one else DimExpr.zero)) (CType.substDim i b T) (CTerm.substDimBool i b f) (CTerm.substDimBool i b fInv) (CTerm.substDimBool i b sec) (CTerm.substDimBool i b ret) (CTerm.substDimBool i b coh) (CType.substDim i b A) = CType.glue φ T f fInv sec ret coh A obtain ⟨⟨⟨⟨⟨⟨⟨hφ, hT⟩, hf⟩, hfInv⟩, hsec⟩, hret⟩, hcoh⟩, hA⟩ := h rw [FaceFormula.substDim_of_absent i _ φ hφ, CType.substDim_absent_aux i b T hT, CTerm.substDimBool_of_absent i b f hf, CTerm.substDimBool_of_absent i b fInv hfInv, CTerm.substDimBool_of_absent i b sec hsec, CTerm.substDimBool_of_absent i b ret hret, CTerm.substDimBool_of_absent i b coh hcoh, CType.substDim_absent_aux i b A hA] theorem CType.substDim_of_absent (i : DimVar) (b : Bool) (A : CType) (h : CType.dimAbsent i A = true) : CType.substDim i b A = A := CType.substDim_absent_aux i b A h -- ── CType.substDimExpr absent-subst (general DimExpr version) ───────────────── private def CType.substDimExpr_absent_aux (i : DimVar) (r : DimExpr) : (A : CType) → CType.dimAbsent i A = true → CType.substDimExpr i r A = A | .univ, _ => rfl | .pi A B, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.pi (A.substDimExpr i r) (B.substDimExpr i r) = CType.pi A B congr 1 · exact CType.substDimExpr_absent_aux i r A h.1 · exact CType.substDimExpr_absent_aux i r B h.2 | .path A a t, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.path (A.substDimExpr i r) (a.substDim i r) (t.substDim i r) = CType.path A a t congr 1 · exact CType.substDimExpr_absent_aux i r A h.1.1 · exact CTerm.substDim_of_absent i r a h.1.2 · exact CTerm.substDim_of_absent i r t h.2 | .sigma A B, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.sigma (A.substDimExpr i r) (B.substDimExpr i r) = CType.sigma A B congr 1 · exact CType.substDimExpr_absent_aux i r A h.1 · exact CType.substDimExpr_absent_aux i r B h.2 | .glue φ T f fInv sec ret coh A, h => by simp only [CType.dimAbsent, Bool.and_eq_true] at h show CType.glue (φ.substDim i r) (T.substDimExpr i r) (f.substDim i r) (fInv.substDim i r) (sec.substDim i r) (ret.substDim i r) (coh.substDim i r) (A.substDimExpr i r) = CType.glue φ T f fInv sec ret coh A obtain ⟨⟨⟨⟨⟨⟨⟨hφ, hT⟩, hf⟩, hfInv⟩, hsec⟩, hret⟩, hcoh⟩, hA⟩ := h rw [FaceFormula.substDim_of_absent i r φ hφ, CType.substDimExpr_absent_aux i r T hT, CTerm.substDim_of_absent i r f hf, CTerm.substDim_of_absent i r fInv hfInv, CTerm.substDim_of_absent i r sec hsec, CTerm.substDim_of_absent i r ret hret, CTerm.substDim_of_absent i r coh hcoh, CType.substDimExpr_absent_aux i r A hA] /-- Generalised: when `i` is absent from `A`, substituting `i` by any `DimExpr` leaves `A` unchanged. Equivalently: line reversal (via `i := inv i`) is a no-op on constant-in-`i` types — the fact that makes `vTranspInv` reduce to identity in the constant-domain case. -/ theorem CType.substDimExpr_of_absent (i : DimVar) (r : DimExpr) (A : CType) (h : CType.dimAbsent i A = true) : CType.substDimExpr i r A = A := CType.substDimExpr_absent_aux i r A h -- ── Constancy: at0 = at1 when binder is absent ─────────────────────────────── theorem DimLine.const_endpoints_eq (A : CType) (i : DimVar) (h : CType.dimAbsent i A = true) : (DimLine.const A i).at0 = (DimLine.const A i).at1 := by simp [DimLine.const_at0, DimLine.const_at1, CType.substDim_of_absent i false A h, CType.substDim_of_absent i true A h] -- ── Face connection ─────────────────────────────────────────────────────────── /-- On the (eq0 i) face, the line evaluates to at0. -/ theorem DimLine.atBool_eq0_face (L : DimLine) (env : DimVar → Bool) (h : (FaceFormula.eq0 L.binder).eval env = true) : L.atBool (env L.binder) = L.at0 := by simp only [FaceFormula.eval] at h cases hb : env L.binder with | false => simp [DimLine.atBool, DimLine.at0] | true => simp [hb] at h /-- On the (eq1 i) face, the line evaluates to at1. -/ theorem DimLine.atBool_eq1_face (L : DimLine) (env : DimVar → Bool) (h : (FaceFormula.eq1 L.binder).eval env = true) : L.atBool (env L.binder) = L.at1 := by simp only [FaceFormula.eval] at h cases hb : env L.binder with | false => simp [hb] at h | true => simp [DimLine.atBool, DimLine.at1] /-- For any environment, atBool gives either at0 or at1. -/ theorem DimLine.atBool_is_endpoint (L : DimLine) (env : DimVar → Bool) : L.atBool (env L.binder) = L.at0 ∨ L.atBool (env L.binder) = L.at1 := by cases env L.binder · left; rfl · right; rfl -- ── Substitution idempotence ────────────────────────────────────────────────── -- After substDimBool i b, dimension i is no longer free. -- A second substDimBool i b is then a no-op (substDimBool_of_absent). -- Step 1: substituting i out of a DimExpr makes i absent. theorem DimExpr.dimAbsent_after_subst (i : DimVar) (r : DimExpr) (hr : r.dimAbsent i = true) (e : DimExpr) : (DimExpr.subst i r e).dimAbsent i = true := by induction e with | zero => rfl | one => rfl | var j => simp only [DimExpr.subst] by_cases hji : j = i · simp [hji, hr] · simp only [if_neg hji, DimExpr.dimAbsent, bne_iff_ne]; exact hji | inv e ih => simp [DimExpr.subst, DimExpr.dimAbsent, ih] | meet a b iha ihb => simp only [DimExpr.subst, DimExpr.dimAbsent, iha, ihb, Bool.and_self] | join a b iha ihb => simp only [DimExpr.subst, DimExpr.dimAbsent, iha, ihb, Bool.and_self] -- `DimExpr.dimAbsent_endpoint` now lives in Interval.lean. -- Step 2: after substDim i r, i is absent from any CTerm (mutual with a -- helper `dimAbsent.clauses_after_substDim` for the compN clause list). mutual private def CTerm.dimAbsent_after_substDim_aux (i : DimVar) (r : DimExpr) (hr : r.dimAbsent i = true) : (t : CTerm) → (t.substDim i r).dimAbsent i = true | .var _ => rfl | .lam _ t => CTerm.dimAbsent_after_substDim_aux i r hr t | .app f a => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent_after_substDim_aux i r hr f, CTerm.dimAbsent_after_substDim_aux i r hr a, Bool.and_self] | .plam j t => by simp only [CTerm.substDim] by_cases hji : j = i · subst hji; simp [CTerm.dimAbsent] · simp only [if_neg hji, CTerm.dimAbsent] exact CTerm.dimAbsent_after_substDim_aux i r hr t | .papp t s => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent_after_substDim_aux i r hr t, DimExpr.dimAbsent_after_subst i r hr s, Bool.and_self] | .transp _ _ φ t => by simp only [CTerm.substDim, CTerm.dimAbsent, FaceFormula.dimAbsent_after_substDim i r hr φ, CTerm.dimAbsent_after_substDim_aux i r hr t, Bool.and_self] | .comp _ _ φ u t => by simp only [CTerm.substDim, CTerm.dimAbsent, FaceFormula.dimAbsent_after_substDim i r hr φ, CTerm.dimAbsent_after_substDim_aux i r hr u, CTerm.dimAbsent_after_substDim_aux i r hr t, Bool.and_self] | .compN _ _ clauses t => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent.clauses_after_substDim i r hr clauses, CTerm.dimAbsent_after_substDim_aux i r hr t, Bool.and_true] | .glueIn φ t a => by simp only [CTerm.substDim, CTerm.dimAbsent, FaceFormula.dimAbsent_after_substDim i r hr φ, CTerm.dimAbsent_after_substDim_aux i r hr t, CTerm.dimAbsent_after_substDim_aux i r hr a, Bool.and_self] | .unglue φ f g => by simp only [CTerm.substDim, CTerm.dimAbsent, FaceFormula.dimAbsent_after_substDim i r hr φ, CTerm.dimAbsent_after_substDim_aux i r hr f, CTerm.dimAbsent_after_substDim_aux i r hr g, Bool.and_self] | .pair a b => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent_after_substDim_aux i r hr a, CTerm.dimAbsent_after_substDim_aux i r hr b, Bool.and_self] | .fst t => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent_after_substDim_aux i r hr t] | .snd t => by simp only [CTerm.substDim, CTerm.dimAbsent, CTerm.dimAbsent_after_substDim_aux i r hr t] /-- Helper: `i` is absent from every clause in the result of substituting `i := r` in a clause list (provided `r` doesn't mention `i`). -/ private def CTerm.dimAbsent.clauses_after_substDim (i : DimVar) (r : DimExpr) (hr : r.dimAbsent i = true) : (clauses : List (FaceFormula × CTerm)) → CTerm.dimAbsent.clauses i (CTerm.substDim.clauses i r clauses) = true | [] => rfl | (φ, u) :: rest => by simp only [CTerm.substDim.clauses, CTerm.dimAbsent.clauses, Bool.and_eq_true] refine ⟨⟨?_, ?_⟩, ?_⟩ · exact FaceFormula.dimAbsent_after_substDim i r hr φ · exact CTerm.dimAbsent_after_substDim_aux i r hr u · exact CTerm.dimAbsent.clauses_after_substDim i r hr rest end theorem CTerm.dimAbsent_after_substDimBool (i : DimVar) (b : Bool) (t : CTerm) : (t.substDimBool i b).dimAbsent i = true := CTerm.dimAbsent_after_substDim_aux i _ (DimExpr.dimAbsent_endpoint i b) t -- Step 3: after CType.substDim i b, i is absent from the type. private def CType.dimAbsent_after_substDim_aux (i : DimVar) (b : Bool) : (A : CType) → (A.substDim i b).dimAbsent i = true | .univ => rfl | .pi A B => by simp only [CType.substDim, CType.dimAbsent, CType.dimAbsent_after_substDim_aux i b A, CType.dimAbsent_after_substDim_aux i b B, Bool.and_self] | .path A a t => by simp only [CType.substDim, CType.dimAbsent, CType.dimAbsent_after_substDim_aux i b A, CTerm.dimAbsent_after_substDimBool i b a, CTerm.dimAbsent_after_substDimBool i b t, Bool.and_self] | .sigma A B => by simp only [CType.substDim, CType.dimAbsent, CType.dimAbsent_after_substDim_aux i b A, CType.dimAbsent_after_substDim_aux i b B, Bool.and_self] | .glue φ T f fInv sec ret coh A => by simp only [CType.substDim, CType.dimAbsent, FaceFormula.dimAbsent_after_substDim i _ (DimExpr.dimAbsent_endpoint i b) φ, CType.dimAbsent_after_substDim_aux i b T, CTerm.dimAbsent_after_substDimBool i b f, CTerm.dimAbsent_after_substDimBool i b fInv, CTerm.dimAbsent_after_substDimBool i b sec, CTerm.dimAbsent_after_substDimBool i b ret, CTerm.dimAbsent_after_substDimBool i b coh, CType.dimAbsent_after_substDim_aux i b A, Bool.and_self] theorem CType.dimAbsent_after_substDim (i : DimVar) (b : Bool) (A : CType) : (A.substDim i b).dimAbsent i = true := CType.dimAbsent_after_substDim_aux i b A -- Step 4: idempotence. /-- Substituting dimension i twice at the same Bool endpoint is idempotent: after the first subst i is absent; the second is a no-op. -/ theorem CTerm.substDimBool_idem (i : DimVar) (b : Bool) (t : CTerm) : (t.substDimBool i b).substDimBool i b = t.substDimBool i b := CTerm.substDimBool_of_absent i b _ (CTerm.dimAbsent_after_substDimBool i b t) theorem CType.substDim_idem (i : DimVar) (b : Bool) (A : CType) : (A.substDim i b).substDim i b = A.substDim i b := CType.substDim_of_absent i b _ (CType.dimAbsent_after_substDim i b A) -- ── Substitution commutativity ──────────────────────────────────────────────── -- Substituting at disjoint dimensions i ≠ j commutes. -- General preconditions: r.dimAbsent j = true (r doesn't use j) and vice versa. -- For Bool endpoints (.zero / .one) both hold trivially. theorem DimExpr.subst_comm (i j : DimVar) (r s : DimExpr) (hij : i ≠ j) (hrj : r.dimAbsent j = true) (hsi : s.dimAbsent i = true) (e : DimExpr) : DimExpr.subst i r (DimExpr.subst j s e) = DimExpr.subst j s (DimExpr.subst i r e) := by induction e with | zero => rfl | one => rfl | var k => by_cases hki : k = i · subst hki -- LHS: subst i r (subst j s (var i)) -- = subst i r (var i) [i ≠ j] -- = r -- RHS: subst j s (subst i r (var i)) -- = subst j s r = r [r absent from j] simp only [DimExpr.subst, if_neg hij, ite_true, DimExpr.subst_of_absent j s r hrj] · by_cases hkj : k = j · subst hkj -- LHS: subst i r (subst j s (var j)) -- = subst i r s = s [s absent from i] -- RHS: subst j s (subst i r (var j)) -- = subst j s (var j) [j ≠ i] -- = s simp only [DimExpr.subst, if_neg hki, ite_true, DimExpr.subst_of_absent i r s hsi] · -- k ≠ i, k ≠ j: both sides reduce to var k simp [DimExpr.subst, hki, hkj] | inv e ih => simp [DimExpr.subst, ih] | meet a b iha ihb => simp [DimExpr.subst, iha, ihb] | join a b iha ihb => simp [DimExpr.subst, iha, ihb] -- Bool endpoints have dimAbsent = true for every dimension. private theorem DimExpr.subst_comm_bool (i j : DimVar) (b c : Bool) (hij : i ≠ j) (e : DimExpr) : DimExpr.subst i (if b then .one else .zero) (DimExpr.subst j (if c then .one else .zero) e) = DimExpr.subst j (if c then .one else .zero) (DimExpr.subst i (if b then .one else .zero) e) := DimExpr.subst_comm i j _ _ hij (DimExpr.dimAbsent_endpoint j b) (DimExpr.dimAbsent_endpoint i c) e -- CTerm commutativity — private def for structural recursion, mutual with -- its clause-list helper so the .compN arm can recurse through the list. mutual private def CTerm.substDim_comm_aux (i j : DimVar) (r s : DimExpr) (hij : i ≠ j) (hrj : r.dimAbsent j = true) (hsi : s.dimAbsent i = true) : (t : CTerm) → (t.substDim i r).substDim j s = (t.substDim j s).substDim i r | .var _ => rfl | .lam x t => congrArg (CTerm.lam x) (CTerm.substDim_comm_aux i j r s hij hrj hsi t) | .app f a => by simp only [CTerm.substDim] congr 1 · exact CTerm.substDim_comm_aux i j r s hij hrj hsi f · exact CTerm.substDim_comm_aux i j r s hij hrj hsi a | .plam k t => by by_cases hki : k = i <;> by_cases hkj : k = j · exact absurd (hki.symm.trans hkj) hij · subst hki simp only [CTerm.substDim, ite_true, if_neg hij] · subst hkj simp only [CTerm.substDim, ite_true, if_neg (Ne.symm hij)] · simp only [CTerm.substDim, if_neg hki, if_neg hkj] exact congrArg (CTerm.plam k) (CTerm.substDim_comm_aux i j r s hij hrj hsi t) | .papp t e => by simp only [CTerm.substDim] rw [CTerm.substDim_comm_aux i j r s hij hrj hsi t, (DimExpr.subst_comm i j r s hij hrj hsi e).symm] | .transp k A φ t => by simp only [CTerm.substDim] congr 1 · exact FaceFormula.substDim_comm i j r s hij hrj hsi φ · exact CTerm.substDim_comm_aux i j r s hij hrj hsi t | .comp _ _ φ u t => by simp only [CTerm.substDim] congr 1 · exact FaceFormula.substDim_comm i j r s hij hrj hsi φ · exact CTerm.substDim_comm_aux i j r s hij hrj hsi u · exact CTerm.substDim_comm_aux i j r s hij hrj hsi t | .compN _ _ clauses t => by simp only [CTerm.substDim] congr 1 · exact CTerm.substDim.clauses_comm_aux i j r s hij hrj hsi clauses · exact CTerm.substDim_comm_aux i j r s hij hrj hsi t | .glueIn φ t a => by simp only [CTerm.substDim] congr 1 · exact FaceFormula.substDim_comm i j r s hij hrj hsi φ · exact CTerm.substDim_comm_aux i j r s hij hrj hsi t · exact CTerm.substDim_comm_aux i j r s hij hrj hsi a | .unglue φ f g => by simp only [CTerm.substDim] congr 1 · exact FaceFormula.substDim_comm i j r s hij hrj hsi φ · exact CTerm.substDim_comm_aux i j r s hij hrj hsi f · exact CTerm.substDim_comm_aux i j r s hij hrj hsi g | .pair a b => by simp only [CTerm.substDim] congr 1 · exact CTerm.substDim_comm_aux i j r s hij hrj hsi a · exact CTerm.substDim_comm_aux i j r s hij hrj hsi b | .fst t => by simp only [CTerm.substDim] exact congrArg CTerm.fst (CTerm.substDim_comm_aux i j r s hij hrj hsi t) | .snd t => by simp only [CTerm.substDim] exact congrArg CTerm.snd (CTerm.substDim_comm_aux i j r s hij hrj hsi t) /-- Helper: `substDim.clauses` commutes on disjoint dim variables. -/ private def CTerm.substDim.clauses_comm_aux (i j : DimVar) (r s : DimExpr) (hij : i ≠ j) (hrj : r.dimAbsent j = true) (hsi : s.dimAbsent i = true) : (clauses : List (FaceFormula × CTerm)) → CTerm.substDim.clauses j s (CTerm.substDim.clauses i r clauses) = CTerm.substDim.clauses i r (CTerm.substDim.clauses j s clauses) | [] => rfl | (φ, u) :: rest => by simp only [CTerm.substDim.clauses] congr 1 · exact Prod.ext (FaceFormula.substDim_comm i j r s hij hrj hsi φ) (CTerm.substDim_comm_aux i j r s hij hrj hsi u) · exact CTerm.substDim.clauses_comm_aux i j r s hij hrj hsi rest end theorem CTerm.substDimBool_comm (i j : DimVar) (b c : Bool) (hij : i ≠ j) (t : CTerm) : (t.substDimBool i b).substDimBool j c = (t.substDimBool j c).substDimBool i b := CTerm.substDim_comm_aux i j _ _ hij (DimExpr.dimAbsent_endpoint j b) (DimExpr.dimAbsent_endpoint i c) t -- CType commutativity private def CType.substDim_comm_aux (i j : DimVar) (b c : Bool) (hij : i ≠ j) : (A : CType) → (A.substDim i b).substDim j c = (A.substDim j c).substDim i b | .univ => rfl | .pi A B => by simp only [CType.substDim] rw [CType.substDim_comm_aux i j b c hij A, CType.substDim_comm_aux i j b c hij B] | .path A a t => by simp only [CType.substDim] rw [CType.substDim_comm_aux i j b c hij A, CTerm.substDimBool_comm i j b c hij a, CTerm.substDimBool_comm i j b c hij t] | .sigma A B => by simp only [CType.substDim] rw [CType.substDim_comm_aux i j b c hij A, CType.substDim_comm_aux i j b c hij B] | .glue φ T f fInv sec ret coh A => by simp only [CType.substDim] rw [FaceFormula.substDim_comm i j _ _ hij (DimExpr.dimAbsent_endpoint j b) (DimExpr.dimAbsent_endpoint i c) φ, CType.substDim_comm_aux i j b c hij T, CTerm.substDimBool_comm i j b c hij f, CTerm.substDimBool_comm i j b c hij fInv, CTerm.substDimBool_comm i j b c hij sec, CTerm.substDimBool_comm i j b c hij ret, CTerm.substDimBool_comm i j b c hij coh, CType.substDim_comm_aux i j b c hij A] theorem CType.substDim_comm (i j : DimVar) (b c : Bool) (hij : i ≠ j) (A : CType) : (A.substDim i b).substDim j c = (A.substDim j c).substDim i b := CType.substDim_comm_aux i j b c hij A