module public import Langlib.Classes.Indexed.Closure.Union public import Langlib.Utilities.ClosurePredicates import Mathlib.Tactic /-! # Indexed Languages Are Closed Under Concatenation Given indexed grammars `g₁` and `g₂`, we construct a grammar with a fresh start symbol and one start rule expanding to the two lifted initial symbols in sequence. Proof idea: derivations from the fresh start first enter the lifted copy of `g₁`, then continue with the lifted copy of `g₂`. The lifting lemmas show that each component derivation is preserved, so terminal yields are exactly concatenations of a word from the first language and a word from the second. -/ open List variable {T : Type} private def concatStartRule (g₁ g₂ : IndexedGrammar T) : IRule T (UnionNT g₁.nt g₂.nt) (UnionFlag g₁.flag g₂.flag) := { lhs := UnionNT.start consume := none rhs := [ IRhsSymbol.nonterminal (UnionNT.inl g₁.initial) none , IRhsSymbol.nonterminal (UnionNT.inr g₂.initial) none ] } private def indexedConcat (g₁ g₂ : IndexedGrammar T) : IndexedGrammar T where nt := UnionNT g₁.nt g₂.nt flag := UnionFlag g₁.flag g₂.flag initial := UnionNT.start rules := [concatStartRule g₁ g₂] ++ g₁.rules.map liftRule1 ++ g₂.rules.map liftRule2 private def concatLiftISym1 (g₁ g₂ : IndexedGrammar T) : g₁.ISym → (indexedConcat g₁ g₂).ISym | .terminal t => .terminal t | .indexed n σ => .indexed (UnionNT.inl n) (σ.map UnionFlag.inl) private def concatLiftISym2 (g₁ g₂ : IndexedGrammar T) : g₂.ISym → (indexedConcat g₁ g₂).ISym | .terminal t => .terminal t | .indexed n σ => .indexed (UnionNT.inr n) (σ.map UnionFlag.inr) private lemma concat_lift1_expandRhs (g₁ g₂ : IndexedGrammar T) (rhs : List (IRhsSymbol T g₁.nt g₁.flag)) (σ : List g₁.flag) : (g₁.expandRhs rhs σ).map (concatLiftISym1 g₁ g₂) = (indexedConcat g₁ g₂).expandRhs (rhs.map liftRhs1) (σ.map UnionFlag.inl) := by unfold IndexedGrammar.expandRhs induction rhs <;> aesop private lemma concat_lift2_expandRhs (g₁ g₂ : IndexedGrammar T) (rhs : List (IRhsSymbol T g₂.nt g₂.flag)) (σ : List g₂.flag) : (g₂.expandRhs rhs σ).map (concatLiftISym2 g₁ g₂) = (indexedConcat g₁ g₂).expandRhs (rhs.map liftRhs2) (σ.map UnionFlag.inr) := by unfold IndexedGrammar.expandRhs rw [List.map_map, List.map_map] congr! 2 funext s cases s <;> simp +decide [liftRhs2, concatLiftISym2] cases ‹Option g₂.flag› <;> rfl private lemma concat_lift1_tran (g₁ g₂ : IndexedGrammar T) {w₁ w₂ : List g₁.ISym} (h : g₁.Transforms w₁ w₂) : (indexedConcat g₁ g₂).Transforms (w₁.map (concatLiftISym1 g₁ g₂)) (w₂.map (concatLiftISym1 g₁ g₂)) := by obtain ⟨r, u, v, σ, hr, hw₁, hw₂⟩ := h refine ⟨liftRule1 r, u.map (concatLiftISym1 g₁ g₂), v.map (concatLiftISym1 g₁ g₂), σ.map UnionFlag.inl, ?_, ?_, ?_⟩ · simp [indexedConcat] exact Or.inr (Or.inl ⟨r, hr, rfl⟩) · cases hc : r.consume with | none => rw [hc] at hw₁ rw [hw₁] simp [liftRule1, concatLiftISym1, hc] | some f => rw [hc] at hw₁ rw [hw₁] simp [liftRule1, concatLiftISym1, hc] · rw [hw₂] simp [liftRule1, concat_lift1_expandRhs] private lemma concat_lift2_tran (g₁ g₂ : IndexedGrammar T) {w₁ w₂ : List g₂.ISym} (h : g₂.Transforms w₁ w₂) : (indexedConcat g₁ g₂).Transforms (w₁.map (concatLiftISym2 g₁ g₂)) (w₂.map (concatLiftISym2 g₁ g₂)) := by obtain ⟨r, u, v, σ, hr, hw₁, hw₂⟩ := h refine ⟨liftRule2 r, u.map (concatLiftISym2 g₁ g₂), v.map (concatLiftISym2 g₁ g₂), σ.map UnionFlag.inr, ?_, ?_, ?_⟩ · simp [indexedConcat] exact Or.inr (show (∃ a ∈ g₁.rules, liftRule1 a = liftRule2 r) ∨ ∃ a ∈ g₂.rules, liftRule2 a = liftRule2 r from Or.inr ⟨r, hr, rfl⟩) · cases hc : r.consume with | none => rw [hc] at hw₁ rw [hw₁] simp [liftRule2, concatLiftISym2, hc] | some f => rw [hc] at hw₁ rw [hw₁] simp [liftRule2, concatLiftISym2, hc] · rw [hw₂] simp [liftRule2, concat_lift2_expandRhs] private lemma concat_lift1_deri (g₁ g₂ : IndexedGrammar T) {w₁ w₂ : List g₁.ISym} (h : g₁.Derives w₁ w₂) : (indexedConcat g₁ g₂).Derives (w₁.map (concatLiftISym1 g₁ g₂)) (w₂.map (concatLiftISym1 g₁ g₂)) := by induction h with | refl => exact Relation.ReflTransGen.refl | tail _ ht ih => exact ih.tail (concat_lift1_tran g₁ g₂ ht) private lemma concat_lift2_deri (g₁ g₂ : IndexedGrammar T) {w₁ w₂ : List g₂.ISym} (h : g₂.Derives w₁ w₂) : (indexedConcat g₁ g₂).Derives (w₁.map (concatLiftISym2 g₁ g₂)) (w₂.map (concatLiftISym2 g₁ g₂)) := by induction h with | refl => exact Relation.ReflTransGen.refl | tail _ ht ih => exact ih.tail (concat_lift2_tran g₁ g₂ ht) private lemma concat_start_tran (g₁ g₂ : IndexedGrammar T) : (indexedConcat g₁ g₂).Transforms [IndexedGrammar.ISym.indexed UnionNT.start []] [ IndexedGrammar.ISym.indexed (UnionNT.inl g₁.initial) [] , IndexedGrammar.ISym.indexed (UnionNT.inr g₂.initial) [] ] := by refine ⟨concatStartRule g₁ g₂, [], [], [], ?_, ?_, ?_⟩ · simp [indexedConcat] · simp [concatStartRule] · simp [concatStartRule, IndexedGrammar.expandRhs] private theorem indexedConcat_generates_append (g₁ g₂ : IndexedGrammar T) {w₁ w₂ : List T} (h₁ : g₁.Generates w₁) (h₂ : g₂.Generates w₂) : (indexedConcat g₁ g₂).Generates (w₁ ++ w₂) := by unfold IndexedGrammar.Generates at h₁ h₂ ⊢ have hstart := IndexedGrammar.deri_of_tran (concat_start_tran g₁ g₂) have hleft := IndexedGrammar.deri_with_suffix ([IndexedGrammar.ISym.indexed (UnionNT.inr g₂.initial) []] : List (indexedConcat g₁ g₂).ISym) (concat_lift1_deri g₁ g₂ h₁) have hright := IndexedGrammar.deri_with_prefix ((w₁.map IndexedGrammar.ISym.terminal).map (concatLiftISym1 g₁ g₂)) (concat_lift2_deri g₁ g₂ h₂) refine hstart.trans (hleft.trans ?_) simpa [concatLiftISym1, concatLiftISym2, List.map_append, List.map_map] using hright private theorem indexedConcat_language_subset_product (g₁ g₂ : IndexedGrammar T) : Set.Subset (g₁.Language * g₂.Language) (indexedConcat g₁ g₂).Language := by intro w hw rw [Language.mul_def] at hw obtain ⟨w₁, hw₁, w₂, hw₂, rfl⟩ := hw exact indexedConcat_generates_append g₁ g₂ hw₁ hw₂ private lemma map_eq_append_singleton_of_injective {α β : Type} {f : α → β} (hf : Function.Injective f) {xs : List α} {x : α} {u v : List β} (h : xs.map f = u ++ [f x] ++ v) : ∃ xs₁ xs₂ : List α, xs = xs₁ ++ [x] ++ xs₂ ∧ u = xs₁.map f ∧ v = xs₂.map f := by have h' : xs.map f = u ++ f x :: v := by simpa using h obtain ⟨xs₁, rest, hxs, hu, hrest⟩ := List.map_eq_append_iff.mp h' cases rest with | nil => simp at hrest | cons y xs₂ => simp only [List.map_cons, List.cons.injEq] at hrest obtain ⟨hy, hv⟩ := hrest subst hxs have hyx : y = x := hf hy subst y exact ⟨xs₁, xs₂, by simp, hu.symm, hv.symm⟩ private lemma append_eq_append_cons_of_not_mem {γ : Type} {x : γ} {block rest u v : List γ} (hno : x ∉ block) : block ++ rest = u ++ x :: v → ∃ u', u = block ++ u' ∧ rest = u' ++ x :: v := by induction block generalizing u with | nil => intro h exact ⟨u, by simp, h⟩ | cons b block ih => intro h cases u with | nil => simp at h exact False.elim (hno (by simp [h.1])) | cons c u => simp only [List.cons_append, List.cons.injEq] at h obtain ⟨rfl, htail⟩ := h have hno_tail : x ∉ block := by intro hx exact hno (by simp [hx]) obtain ⟨u', hu, hrest⟩ := ih hno_tail htail exact ⟨u', by simp [hu], hrest⟩ private lemma append_eq_append_cons_of_not_mem_right {γ : Type} {x : γ} {left right u v : List γ} (hno : x ∉ right) : left ++ right = u ++ x :: v → ∃ pre post : List γ, left = pre ++ x :: post ∧ u = pre ∧ v = post ++ right := by induction left generalizing u with | nil => intro h have hx : x ∈ right := by simp at h rw [h] simp exact False.elim (hno hx) | cons a left ih => intro h cases u with | nil => simp only [List.nil_append, List.cons_append, List.cons.injEq] at h obtain ⟨rfl, hv⟩ := h exact ⟨[], left, by simp, rfl, by simpa using hv.symm⟩ | cons b u => simp only [List.cons_append, List.cons.injEq] at h obtain ⟨rfl, htail⟩ := h obtain ⟨pre, post, hleft, hu, hv⟩ := ih htail exact ⟨a :: pre, post, by simp [hleft], by simp [hu], hv⟩ private lemma append_eq_append_cons_of_not_mem_left {γ : Type} {x : γ} {left right u v : List γ} (hno : x ∉ left) : left ++ right = u ++ x :: v → ∃ pre post : List γ, right = pre ++ x :: post ∧ u = left ++ pre ∧ v = post := by intro h obtain ⟨pre, hpre, hright⟩ := append_eq_append_cons_of_not_mem (γ := γ) (x := x) (block := left) (rest := right) (u := u) (v := v) hno h exact ⟨pre, v, hright, hpre, rfl⟩ private lemma concatLiftISym1_injective (g₁ g₂ : IndexedGrammar T) : Function.Injective (concatLiftISym1 g₁ g₂) := by intro x y h cases x <;> cases y <;> simp [concatLiftISym1] at h ⊢ · exact h · exact ⟨by cases h.1; rfl, by exact List.map_injective_iff.mpr (by intro a b hab; cases hab; rfl) h.2⟩ private lemma concatLiftISym2_injective (g₁ g₂ : IndexedGrammar T) : Function.Injective (concatLiftISym2 g₁ g₂) := by intro x y h cases x <;> cases y <;> simp [concatLiftISym2] at h ⊢ · exact h · exact ⟨by cases h.1; rfl, by exact List.map_injective_iff.mpr (by intro a b hab; cases hab; rfl) h.2⟩ private lemma inl_indexed_not_mem_lift2 (g₁ g₂ : IndexedGrammar T) (n : g₁.nt) (σ : List (UnionFlag g₁.flag g₂.flag)) (ys : List g₂.ISym) : (IndexedGrammar.ISym.indexed (UnionNT.inl n) σ : (indexedConcat g₁ g₂).ISym) ∉ ys.map (concatLiftISym2 g₁ g₂) := by intro h obtain ⟨y, _hy, hy⟩ := List.mem_map.mp h cases y <;> simp [concatLiftISym2] at hy private lemma inr_indexed_not_mem_lift1 (g₁ g₂ : IndexedGrammar T) (n : g₂.nt) (σ : List (UnionFlag g₁.flag g₂.flag)) (xs : List g₁.ISym) : (IndexedGrammar.ISym.indexed (UnionNT.inr n) σ : (indexedConcat g₁ g₂).ISym) ∉ xs.map (concatLiftISym1 g₁ g₂) := by intro h obtain ⟨x, _hx, hx⟩ := List.mem_map.mp h cases x <;> simp [concatLiftISym1] at hx private lemma concat_first_step (g₁ g₂ : IndexedGrammar T) {b : List (indexedConcat g₁ g₂).ISym} (h : (indexedConcat g₁ g₂).Transforms [IndexedGrammar.ISym.indexed UnionNT.start []] b) : b = [ IndexedGrammar.ISym.indexed (UnionNT.inl g₁.initial) [] , IndexedGrammar.ISym.indexed (UnionNT.inr g₂.initial) [] ] := by obtain ⟨r, u, v, σ, hr, hu, hv⟩ := h rcases r with ⟨lhs, consume, rhs⟩ cases consume with | none => rcases u with (_ | ⟨uh, ut⟩) <;> rcases v with (_ | ⟨vh, vt⟩) <;> simp_all +decide [indexedConcat, concatStartRule, liftRule1, liftRule2, IndexedGrammar.expandRhs, List.append_assoc] obtain ⟨hlhs, hσ⟩ := hu subst lhs subst σ rcases hr with hr | hleft | hright · rw [hr] simp · obtain ⟨a, _ha, hstart, _hc, _hrhs⟩ := hleft cases hstart · obtain ⟨a, _ha, hstart, _hc, _hrhs⟩ := hright cases hstart | some f => rcases u with (_ | ⟨uh, ut⟩) <;> rcases v with (_ | ⟨vh, vt⟩) <;> simp_all +decide [indexedConcat, concatStartRule, liftRule1, liftRule2, List.append_assoc] private lemma split_mixed_at_lift1_indexed (g₁ g₂ : IndexedGrammar T) {xs : List g₁.ISym} {ys : List g₂.ISym} {u v : List (indexedConcat g₁ g₂).ISym} {n : g₁.nt} {σ : List (UnionFlag g₁.flag g₂.flag)} (h : xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) = u ++ [IndexedGrammar.ISym.indexed (UnionNT.inl n) σ] ++ v) : ∃ xs₁ xs₂ : List g₁.ISym, ∃ τ : List g₁.flag, xs = xs₁ ++ [IndexedGrammar.ISym.indexed n τ] ++ xs₂ ∧ σ = τ.map UnionFlag.inl ∧ u = xs₁.map (concatLiftISym1 g₁ g₂) ∧ v = xs₂.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) := by obtain ⟨pre, post, hleft, hu, hv⟩ := append_eq_append_cons_of_not_mem_right (x := (IndexedGrammar.ISym.indexed (UnionNT.inl n) σ : (indexedConcat g₁ g₂).ISym)) (left := xs.map (concatLiftISym1 g₁ g₂)) (right := ys.map (concatLiftISym2 g₁ g₂)) (u := u) (v := v) (inl_indexed_not_mem_lift2 g₁ g₂ n σ ys) (by simpa [List.append_assoc] using h) have hmem : (IndexedGrammar.ISym.indexed (UnionNT.inl n) σ : (indexedConcat g₁ g₂).ISym) ∈ xs.map (concatLiftISym1 g₁ g₂) := by rw [hleft] simp obtain ⟨s, _hs, hs⟩ := List.mem_map.mp hmem cases s with | terminal t => simp [concatLiftISym1] at hs | indexed m τ => simp [concatLiftISym1] at hs obtain ⟨hm, hσ⟩ := hs cases hm subst σ obtain ⟨xs₁, xs₂, hxs, hpre, hpost⟩ := map_eq_append_singleton_of_injective (concatLiftISym1_injective g₁ g₂) (xs := xs) (x := IndexedGrammar.ISym.indexed n τ) (u := pre) (v := post) (by simpa [concatLiftISym1] using hleft) exact ⟨xs₁, xs₂, τ, hxs, rfl, by simp [hu, hpre], by simp [hv, hpost]⟩ private lemma split_mixed_at_lift2_indexed (g₁ g₂ : IndexedGrammar T) {xs : List g₁.ISym} {ys : List g₂.ISym} {u v : List (indexedConcat g₁ g₂).ISym} {n : g₂.nt} {σ : List (UnionFlag g₁.flag g₂.flag)} (h : xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) = u ++ [IndexedGrammar.ISym.indexed (UnionNT.inr n) σ] ++ v) : ∃ ys₁ ys₂ : List g₂.ISym, ∃ τ : List g₂.flag, ys = ys₁ ++ [IndexedGrammar.ISym.indexed n τ] ++ ys₂ ∧ σ = τ.map UnionFlag.inr ∧ u = xs.map (concatLiftISym1 g₁ g₂) ++ ys₁.map (concatLiftISym2 g₁ g₂) ∧ v = ys₂.map (concatLiftISym2 g₁ g₂) := by obtain ⟨pre, post, hright, hu, hv⟩ := append_eq_append_cons_of_not_mem_left (x := (IndexedGrammar.ISym.indexed (UnionNT.inr n) σ : (indexedConcat g₁ g₂).ISym)) (left := xs.map (concatLiftISym1 g₁ g₂)) (right := ys.map (concatLiftISym2 g₁ g₂)) (u := u) (v := v) (inr_indexed_not_mem_lift1 g₁ g₂ n σ xs) (by simpa [List.append_assoc] using h) have hmem : (IndexedGrammar.ISym.indexed (UnionNT.inr n) σ : (indexedConcat g₁ g₂).ISym) ∈ ys.map (concatLiftISym2 g₁ g₂) := by rw [hright] simp obtain ⟨s, _hs, hs⟩ := List.mem_map.mp hmem cases s with | terminal t => simp [concatLiftISym2] at hs | indexed m τ => simp [concatLiftISym2] at hs obtain ⟨hm, hσ⟩ := hs cases hm subst σ obtain ⟨ys₁, ys₂, hys, hpre, hpost⟩ := map_eq_append_singleton_of_injective (concatLiftISym2_injective g₁ g₂) (xs := ys) (x := IndexedGrammar.ISym.indexed n τ) (u := pre) (v := post) (by simpa [concatLiftISym2] using hright) exact ⟨ys₁, ys₂, τ, hys, rfl, by simp [hu, hpre], by simp [hv, hpost]⟩ private lemma concat_mixed_step (g₁ g₂ : IndexedGrammar T) {xs : List g₁.ISym} {ys : List g₂.ISym} {z : List (indexedConcat g₁ g₂).ISym} (h : (indexedConcat g₁ g₂).Transforms (xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂)) z) : (∃ xs' : List g₁.ISym, z = xs'.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) ∧ g₁.Transforms xs xs') ∨ (∃ ys' : List g₂.ISym, z = xs.map (concatLiftISym1 g₁ g₂) ++ ys'.map (concatLiftISym2 g₁ g₂) ∧ g₂.Transforms ys ys') := by obtain ⟨r, u, v, σ, hr, hw, hz⟩ := h simp [indexedConcat] at hr rcases hr with hstart | hleft | hright · subst r have hmem : (IndexedGrammar.ISym.indexed UnionNT.start σ : (indexedConcat g₁ g₂).ISym) ∈ xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) := by rw [hw] simp [concatStartRule] rw [List.mem_append] at hmem rcases hmem with hmem | hmem · obtain ⟨x, _hx, hx⟩ := List.mem_map.mp hmem cases x <;> simp [concatLiftISym1] at hx · obtain ⟨y, _hy, hy⟩ := List.mem_map.mp hmem cases y <;> simp [concatLiftISym2] at hy · obtain ⟨r₁, hr₁, hrule⟩ := hleft subst r cases hc : r₁.consume with | none => simp [liftRule1, hc] at hw hz obtain ⟨xs₁, xs₂, τ, hxs, hσ, hu, hv⟩ := split_mixed_at_lift1_indexed g₁ g₂ (by simpa [List.singleton_append] using hw) subst σ subst xs left refine ⟨xs₁ ++ g₁.expandRhs r₁.rhs τ ++ xs₂, ?_, ?_⟩ · rw [hz, hu, hv] simp [concat_lift1_expandRhs, List.map_append, List.append_assoc] · refine ⟨r₁, xs₁, xs₂, τ, hr₁, ?_, rfl⟩ simp [hc] | some f => simp [liftRule1, hc] at hw hz obtain ⟨xs₁, xs₂, τ, hxs, hσ, hu, hv⟩ := split_mixed_at_lift1_indexed g₁ g₂ (by simpa [List.singleton_append] using hw) cases τ with | nil => simp at hσ | cons f' τ' => simp at hσ obtain ⟨hf, hτ⟩ := hσ cases hf subst xs left refine ⟨xs₁ ++ g₁.expandRhs r₁.rhs τ' ++ xs₂, ?_, ?_⟩ · rw [hz, hu, hv] simp [concat_lift1_expandRhs, hτ, List.map_append, List.append_assoc] · refine ⟨r₁, xs₁, xs₂, τ', hr₁, ?_, rfl⟩ simp [hc] · obtain ⟨r₂, hr₂, hrule⟩ := hright subst r cases hc : r₂.consume with | none => simp [liftRule2, hc] at hw hz obtain ⟨ys₁, ys₂, τ, hys, hσ, hu, hv⟩ := split_mixed_at_lift2_indexed g₁ g₂ (by simpa [List.singleton_append] using hw) subst σ subst ys right refine ⟨ys₁ ++ g₂.expandRhs r₂.rhs τ ++ ys₂, ?_, ?_⟩ · rw [hz, hu, hv] simp [concat_lift2_expandRhs, List.map_append, List.append_assoc] · refine ⟨r₂, ys₁, ys₂, τ, hr₂, ?_, rfl⟩ simp [hc] | some f => simp [liftRule2, hc] at hw hz obtain ⟨ys₁, ys₂, τ, hys, hσ, hu, hv⟩ := split_mixed_at_lift2_indexed g₁ g₂ (by simpa [List.singleton_append] using hw) cases τ with | nil => simp at hσ | cons f' τ' => simp at hσ obtain ⟨hf, hτ⟩ := hσ cases hf subst ys right refine ⟨ys₁ ++ g₂.expandRhs r₂.rhs τ' ++ ys₂, ?_, ?_⟩ · rw [hz, hu, hv] simp [concat_lift2_expandRhs, hτ, List.map_append, List.append_assoc] · refine ⟨r₂, ys₁, ys₂, τ', hr₂, ?_, rfl⟩ simp [hc] private lemma concat_mixed_derives (g₁ g₂ : IndexedGrammar T) {xs₀ xs : List g₁.ISym} {ys₀ ys : List g₂.ISym} {z : List (indexedConcat g₁ g₂).ISym} (hd₁ : g₁.Derives xs₀ xs) (hd₂ : g₂.Derives ys₀ ys) (h : (indexedConcat g₁ g₂).Derives (xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂)) z) : ∃ xs' ys', z = xs'.map (concatLiftISym1 g₁ g₂) ++ ys'.map (concatLiftISym2 g₁ g₂) ∧ g₁.Derives xs₀ xs' ∧ g₂.Derives ys₀ ys' := by induction h with | refl => exact ⟨xs, ys, rfl, hd₁, hd₂⟩ | tail hprev ht ih => obtain ⟨xs₁, ys₁, hz₁, hdxs₁, hdys₁⟩ := ih rw [hz₁] at ht rcases concat_mixed_step g₁ g₂ ht with hleft | hright · obtain ⟨xs₂, hz₂, hstep⟩ := hleft exact ⟨xs₂, ys₁, hz₂, hdxs₁.tail hstep, hdys₁⟩ · obtain ⟨ys₂, hz₂, hstep⟩ := hright exact ⟨xs₁, ys₂, hz₂, hdxs₁, hdys₁.tail hstep⟩ private lemma concatLiftISym1_terminal_inv (g₁ g₂ : IndexedGrammar T) {xs : List g₁.ISym} {w : List T} (h : xs.map (concatLiftISym1 g₁ g₂) = w.map IndexedGrammar.ISym.terminal) : xs = w.map IndexedGrammar.ISym.terminal := by induction xs generalizing w with | nil => cases w <;> simp_all | cons x xs ih => cases w with | nil => simp at h | cons a w => cases x <;> simp [concatLiftISym1] at h ⊢ exact ⟨h.1, ih h.2⟩ private lemma concatLiftISym2_terminal_inv (g₁ g₂ : IndexedGrammar T) {ys : List g₂.ISym} {w : List T} (h : ys.map (concatLiftISym2 g₁ g₂) = w.map IndexedGrammar.ISym.terminal) : ys = w.map IndexedGrammar.ISym.terminal := by induction ys generalizing w with | nil => cases w <;> simp_all | cons y ys ih => cases w with | nil => simp at h | cons a w => cases y <;> simp [concatLiftISym2] at h ⊢ exact ⟨h.1, ih h.2⟩ private lemma concat_terminal_split (g₁ g₂ : IndexedGrammar T) {xs : List g₁.ISym} {ys : List g₂.ISym} {w : List T} (h : xs.map (concatLiftISym1 g₁ g₂) ++ ys.map (concatLiftISym2 g₁ g₂) = w.map IndexedGrammar.ISym.terminal) : ∃ w₁ w₂ : List T, w = w₁ ++ w₂ ∧ xs = w₁.map IndexedGrammar.ISym.terminal ∧ ys = w₂.map IndexedGrammar.ISym.terminal := by let w₁ := w.take xs.length let w₂ := w.drop xs.length have hw : w = w₁ ++ w₂ := by simp [w₁, w₂] have hleft : xs.map (concatLiftISym1 g₁ g₂) = w₁.map IndexedGrammar.ISym.terminal := by have ht := congrArg (List.take xs.length) h simpa [w₁, List.length_map] using ht have hright : ys.map (concatLiftISym2 g₁ g₂) = w₂.map IndexedGrammar.ISym.terminal := by have hd := congrArg (List.drop xs.length) h simpa [w₂, List.length_map] using hd exact ⟨w₁, w₂, hw, concatLiftISym1_terminal_inv g₁ g₂ hleft, concatLiftISym2_terminal_inv g₁ g₂ hright⟩ private theorem indexedConcat_language_product_subset (g₁ g₂ : IndexedGrammar T) : Set.Subset (indexedConcat g₁ g₂).Language (g₁.Language * g₂.Language) := by intro w hw change (indexedConcat g₁ g₂).Generates w at hw unfold IndexedGrammar.Generates at hw rcases hw.cases_head with hrefl | ⟨b, hfirst, hrest⟩ · cases w <;> simp [indexedConcat] at hrefl · have hb := concat_first_step g₁ g₂ hfirst subst b have hmixed : (indexedConcat g₁ g₂).Derives (([IndexedGrammar.ISym.indexed g₁.initial []] : List g₁.ISym).map (concatLiftISym1 g₁ g₂) ++ ([IndexedGrammar.ISym.indexed g₂.initial []] : List g₂.ISym).map (concatLiftISym2 g₁ g₂)) (w.map IndexedGrammar.ISym.terminal) := by simpa [concatLiftISym1, concatLiftISym2] using hrest obtain ⟨xs, ys, hterm, hdxs, hdys⟩ := concat_mixed_derives g₁ g₂ (IndexedGrammar.deri_self g₁ [IndexedGrammar.ISym.indexed g₁.initial []]) (IndexedGrammar.deri_self g₂ [IndexedGrammar.ISym.indexed g₂.initial []]) hmixed obtain ⟨w₁, w₂, hw, hxs, hys⟩ := concat_terminal_split g₁ g₂ hterm.symm rw [Language.mul_def] refine ⟨w₁, ?_, w₂, ?_, hw.symm⟩ · change g₁.Generates w₁ unfold IndexedGrammar.Generates simpa [hxs] using hdxs · change g₂.Generates w₂ unfold IndexedGrammar.Generates simpa [hys] using hdys private theorem indexedConcat_language (g₁ g₂ : IndexedGrammar T) : (indexedConcat g₁ g₂).Language = g₁.Language * g₂.Language := by apply Set.Subset.antisymm · exact indexedConcat_language_product_subset g₁ g₂ · exact indexedConcat_language_subset_product g₁ g₂ /-- Indexed languages are closed under concatenation. -/ public theorem Indexed_closedUnderConcatenation : ClosedUnderConcatenation (α := T) is_Indexed := by intro L₁ L₂ h₁ h₂ obtain ⟨g₁, hg₁⟩ := h₁ obtain ⟨g₂, hg₂⟩ := h₂ refine ⟨indexedConcat g₁ g₂, ?_⟩ rw [indexedConcat_language, hg₁, hg₂]