import Mathlib
import Langlib.Grammars.Unrestricted.Toolbox
import Langlib.Classes.RecursivelyEnumerable.Basics.Lifting
import Langlib.Classes.RecursivelyEnumerable.Definition
import Langlib.Classes.RecursivelyEnumerable.Closure.Concatenation
import Langlib.Utilities.ClosurePredicates
import Langlib.Utilities.ListUtils
import Langlib.Classes.RecursivelyEnumerable.Closure.Star.Helpers

/-! # RE Closure Under Kleene Star

This file proves that the class of recursively enumerable languages is closed under Kleene star.
The proof constructs an unrestricted grammar for `L*` given an unrestricted grammar for `L`,
following the construction from the Lean 3 proof `RE_star.lean`.

## Grammar Construction

Given a grammar `g` for `L` with nonterminals `N` and start symbol `S`:
- New nonterminals: `Z` (start), `H` (separator), `R` (scanner), plus lifted originals
- Rules:
  1. `Z → Z S H` (generate another copy)
  2. `Z → R H` (start terminal scanning)
  3. `R H → R` (scanner jumps over separator)
  4. `R H → ε` (scanner+separator deleted at end)
  5. All original rules (with nonterminals lifted)
  6. `R t → t R` for each terminal `t` (scanner moves left past terminals)

## Main declarations

- `RE_of_star_RE` — the main closure theorem
-/

variable {T : Type}

section construction

/-- New nonterminal type: original nonterminals ⊕ 3 fresh ones. -/
private abbrev nn (N : Type) : Type := N ⊕ Fin 3

/-- New symbol type. -/
private abbrev ns (T N : Type) : Type := symbol T (nn N)

private def Z {N : Type} : ns T N := symbol.nonterminal (Sum.inr 0)
private def H {N : Type} : ns T N := symbol.nonterminal (Sum.inr 1)
private def R {N : Type} : ns T N := symbol.nonterminal (Sum.inr 2)
private def S₀ {g : grammar T} : ns T g.nt := symbol.nonterminal (Sum.inl g.initial)

private def wrap_sym {N : Type} : symbol T N → ns T N
  | symbol.terminal t    => symbol.terminal t
  | symbol.nonterminal n => symbol.nonterminal (Sum.inl n)

private def wrap_gr {N : Type} (r : grule T N) : grule T (nn N) :=
  ⟨List.map wrap_sym r.input_L,
   Sum.inl r.input_N,
   List.map wrap_sym r.input_R,
   List.map wrap_sym r.output_string⟩

private def rules_that_scan_terminals (g : grammar T) : List (grule T (nn g.nt)) :=
  List.map (fun t => grule.mk [] (Sum.inr 2) [symbol.terminal t] [symbol.terminal t, R])
    (all_used_terminals g)

/-- The grammar for the Kleene star of a language generated by `g`. -/
private def star_grammar (g : grammar T) : grammar T :=
  ⟨nn g.nt, Sum.inr 0,
   grule.mk [] (Sum.inr 0) [] [Z, S₀, H] ::
   grule.mk [] (Sum.inr 0) [] [R, H] ::
   grule.mk [] (Sum.inr 2) [H] [R] ::
   grule.mk [] (Sum.inr 2) [H] [] ::
   List.map wrap_gr g.rules ++
   rules_that_scan_terminals g⟩

end construction

-- Utility: nonterminal not in map terminal
private lemma nonterminal_not_in_map_terminal {N : Type} {n : N} {w : List T} :
    symbol.nonterminal n ∉ List.map (symbol.terminal (N := N)) w := by
  intro h; rw [List.mem_map] at h; rcases h with ⟨_, _, h⟩; cases h

section easy_direction

private lemma wrap_sym_eq_lift_symbol {g : grammar T} :
    @wrap_sym T g.nt = lift_symbol_ Sum.inl := by
  ext x; cases x <;> rfl

private lemma wrap_gr_eq_lift_rule {g : grammar T} (r : grule T g.nt) :
    wrap_gr r = lift_rule_ Sum.inl r := by
  simp [wrap_gr, lift_rule_, lift_string_, ← wrap_sym_eq_lift_symbol]

/-- Wrapped rules of g are in star_grammar's rules. -/
private lemma wrapped_rule_mem {g : grammar T} {r : grule T g.nt} (hr : r ∈ g.rules) :
    wrap_gr r ∈ (star_grammar g).rules := by
  simp only [star_grammar]
  iterate 4 apply List.mem_cons_of_mem
  exact List.mem_append_left _ (List.mem_map_of_mem hr)

/-- Scanning rules are in star_grammar's rules. -/
private lemma scan_rule_mem {g : grammar T} {t : T} (ht : t ∈ all_used_terminals g) :
    grule.mk [] (Sum.inr 2) [symbol.terminal t] [symbol.terminal t, R] ∈ (star_grammar g).rules := by
  simp only [star_grammar]
  iterate 4 apply List.mem_cons_of_mem
  exact List.mem_append_right _ (List.mem_map_of_mem ht)

/-- The lifted grammar embedding g into star_grammar g. -/
private noncomputable def star_lg (g : grammar T) : lifted_grammar_ T := {
  g₀ := g
  g := star_grammar g
  lift_nt := Sum.inl
  sink_nt := Sum.getLeft?
  lift_inj := Sum.inl_injective
  sink_inj := by
    intro x y h
    match x, y with
    | Sum.inl a, Sum.inl b => left; simp [Sum.getLeft?] at h; exact congrArg Sum.inl h
    | Sum.inl _, Sum.inr _ => simp [Sum.getLeft?] at h
    | Sum.inr _, Sum.inl _ => simp [Sum.getLeft?] at h
    | Sum.inr _, Sum.inr _ => right; simp [Sum.getLeft?]
  lift_nt_sink := by intro n; simp [Sum.getLeft?]
  corresponding_rules := by
    intro r rin
    have := wrapped_rule_mem rin
    rw [wrap_gr_eq_lift_rule] at this
    exact this
  preimage_of_rules := by
    intro r ⟨rin, n₀, hn₀⟩
    have hinl : r.input_N = Sum.inl n₀ := hn₀.symm
    simp only [star_grammar] at rin
    rcases List.mem_cons.mp rin with rfl | rin
    · simp at hinl
    rcases List.mem_cons.mp rin with rfl | rin
    · simp at hinl
    rcases List.mem_cons.mp rin with rfl | rin
    · simp at hinl
    rcases List.mem_cons.mp rin with rfl | rin
    · simp at hinl
    rcases List.mem_append.mp rin with rin | rin
    · rw [List.mem_map] at rin
      rcases rin with ⟨r₀, hr₀, rfl⟩
      exact ⟨r₀, hr₀, by rw [wrap_gr_eq_lift_rule]⟩
    · simp only [rules_that_scan_terminals, List.mem_map] at rin
      rcases rin with ⟨t, _, rfl⟩; simp at hinl
}

/-- Lift a derivation from g to star_grammar g. -/
private lemma lift_generates {g : grammar T} {w : List T}
    (h : grammar_generates g w) :
  grammar_derives (star_grammar g) [S₀] (List.map symbol.terminal w) := by
  have lft := lift_deri_ (star_lg g) h
  simp only [lift_string_, List.map_cons, List.map_nil, lift_symbol_] at lft
  convert lft using 1
  simp [List.map_map, lift_symbol_]

/-- Terminal t from a derived word is in all_used_terminals. -/
private lemma terminal_in_all_used {g : grammar T} {w : List T}
    (hgen : grammar_generates g w) {t : T} (ht : t ∈ w) :
  t ∈ all_used_terminals g := by
  have stin : (symbol.terminal t : symbol T g.nt) ∈ List.map symbol.terminal w :=
    List.mem_map_of_mem (f := symbol.terminal) ht
  rcases grammar_generates_only_legit_terminals hgen stin with ⟨r, rin, htin⟩ | ⟨imposs⟩
  · simp only [all_used_terminals, List.mem_filterMap]
    exact ⟨symbol.terminal t,
      List.mem_flatten.mpr ⟨_, List.mem_map_of_mem (f := grule.output_string) rin, htin⟩,
      by simp [as_terminal]⟩
  · exact absurd imposs (by intro h; cases h)

/-
R scans left past a single word of terminals.
-/
private lemma scan_word {g : grammar T} {word : List T}
    (hw : ∀ t ∈ word, t ∈ all_used_terminals g)
    {sfx : List (ns T g.nt)} :
  grammar_derives (star_grammar g)
    ([R] ++ List.map symbol.terminal word ++ sfx)
    (List.map symbol.terminal word ++ [R] ++ sfx) := by
  have h_scan : ∀ (word : List T) (sfx : List (ns T g.nt)),
    (∀ t ∈ word, t ∈ all_used_terminals g) →
    grammar_derives (star_grammar g) ([R] ++ List.map symbol.terminal word ++ sfx) (List.map symbol.terminal word ++ [R] ++ sfx) := by
      intros word sfx hw
      induction' word with t word ih generalizing sfx;
      · exact?;
      · have h_scan : grammar_transforms (star_grammar g) ([R] ++ List.map symbol.terminal (t :: word) ++ sfx) ([symbol.terminal t] ++ [R] ++ List.map symbol.terminal word ++ sfx) := by
          use grule.mk [] (Sum.inr 2) [symbol.terminal t] [symbol.terminal t, R];
          simp +zetaDelta at *;
          exact ⟨ scan_rule_mem hw.1, [ ], List.map symbol.terminal word ++ sfx, rfl, rfl ⟩;
        have h_ind : grammar_derives (star_grammar g) ([symbol.terminal t] ++ [R] ++ List.map symbol.terminal word ++ sfx) ([symbol.terminal t] ++ List.map symbol.terminal word ++ [R] ++ sfx) := by
          convert grammar_deri_with_prefix [ symbol.terminal t ] ( ih sfx fun x hx => hw x ( List.mem_cons_of_mem _ hx ) ) using 1;
        exact grammar_deri_of_tran_deri h_scan h_ind;
  exact h_scan word sfx hw

/-
Terminal scan over multiple words separated by H's.
-/
private lemma terminal_scan {g : grammar T} {w : List (List T)}
    (hw : ∀ v ∈ w, ∀ t ∈ v, t ∈ all_used_terminals g) :
  grammar_derives (star_grammar g)
    ([R] ++ (List.map (fun v => [H] ++ List.map symbol.terminal v) w).flatten ++ [H])
    (List.map symbol.terminal w.flatten ++ [R, H]) := by
  revert w;
  intro w hw; induction' w with v w hw ih <;> simp_all +decide [ List.flatten ] ;
  · exact Relation.ReflTransGen.refl;
  · have h_scan : grammar_derives (star_grammar g) (R :: H :: (List.map symbol.terminal v ++ ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H]))) (R :: (List.map symbol.terminal v ++ ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H]))) := by
      have h_scan : grammar_transforms (star_grammar g) (R :: H :: (List.map symbol.terminal v ++ ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H]))) (R :: (List.map symbol.terminal v ++ ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H]))) := by
        unfold grammar_transforms; simp +decide [ star_grammar ] ;
        exact Or.inr <| Or.inr <| Or.inl ⟨ [ ], List.map symbol.terminal v ++ ( ( List.map ( fun v => H :: List.map symbol.terminal v ) w ).flatten ++ [ H ] ), rfl, rfl ⟩;
      exact Relation.ReflTransGen.single h_scan;
    have h_scan : grammar_derives (star_grammar g) (R :: (List.map symbol.terminal v ++ ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H]))) (List.map symbol.terminal v ++ R :: ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H])) := by
      convert scan_word _ using 1 ; aesop;
      exact hw.1;
    have h_scan : grammar_derives (star_grammar g) (List.map symbol.terminal v ++ R :: ((List.map (fun v => H :: List.map symbol.terminal v) w).flatten ++ [H])) (List.map symbol.terminal v ++ ((List.map (List.map symbol.terminal) w).flatten ++ [R, H])) := by
      apply grammar_deri_with_prefix; aesop;
    exact grammar_deri_of_deri_deri ‹_› ( grammar_deri_of_deri_deri ‹_› ‹_› )

/-
The short induction builds derivations for each word.
-/
private lemma short_induction {g : grammar T} {w : List (List T)}
    (ass : ∀ wᵢ ∈ w.reverse, grammar_generates g wᵢ) :
  grammar_derives (star_grammar g) [Z]
    (Z :: (List.map (· ++ [H]) (List.map (List.map symbol.terminal) w.reverse)).flatten) := by
  induction' w using List.reverseRecOn with w ih;
  · constructor;
  · rename_i h; specialize h ( fun wᵢ hwᵢ => ass wᵢ <| by simp +decide [ hwᵢ ] ) ; simp_all +decide [ List.map_append, List.flatten_append ] ;
    -- Apply the step_Z_expand rule to get [Z, S₀, H] ++ ih.
    have h_expand : grammar_derives (star_grammar g) [Z] ([Z, S₀, H] ++ (List.map ((fun x => x ++ [H]) ∘ List.map symbol.terminal) w).reverse.flatten) := by
      convert grammar_deri_of_deri_deri h ( grammar_deri_of_tran _ ) using 1;
      unfold grammar_transforms; simp +decide [ star_grammar ] ;
      exact Or.inl ⟨ [ ], _, rfl, rfl ⟩;
    have h_lift : grammar_derives (star_grammar g) [S₀, H] (List.map symbol.terminal ih ++ [H]) := by
      have h_lift : grammar_derives (star_grammar g) [S₀] (List.map symbol.terminal ih) := by
        exact lift_generates ass.1;
      apply grammar_deri_with_postfix [H] h_lift;
    convert grammar_deri_of_deri_deri h_expand _ using 1;
    convert grammar_deri_with_prefix [ Z ] ( grammar_deri_with_postfix ( ( List.map ( ( fun x => x ++ [ H ] ) ∘ List.map symbol.terminal ) w ).reverse.flatten ) h_lift ) using 1;
    simp +decide [ List.append_assoc ]

/-
The easy direction: L* ⊆ grammar_language (star_grammar g)
-/
private lemma in_star_grammar_of_in_star {g : grammar T} {w : List T}
    (hw : w ∈ KStar.kstar (grammar_language g)) :
  grammar_generates (star_grammar g) w := by
  obtain ⟨L, rfl, hL⟩ := hw;
  -- Set v = L.reverse. Then v.reverse = L.
  set v := L.reverse;
  -- Use short_induction to get: grammar_derives [Z] (Z :: (map (· ++ [H]) (map (map terminal) L)).flatten)
  have h1 : grammar_derives (star_grammar g) [Z] (Z :: (List.map (· ++ [H]) (List.map (List.map symbol.terminal) L)).flatten) := by
    convert short_induction _;
    exact?;
    aesop;
  -- Apply step_Z_to_RH (Z → R H) with postfix = rest of the string.
  have h2 : grammar_derives (star_grammar g) (Z :: (List.map (· ++ [H]) (List.map (List.map symbol.terminal) L)).flatten) ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map symbol.terminal) L)).flatten) := by
    -- Apply the rule Z → R H to the initial symbol Z.
    have h_step : grammar_transforms (star_grammar g) [Z] [R, H] := by
      constructor;
      swap;
      exact grule.mk [] ( Sum.inr 0 ) [] [ R, H ];
      simp +decide [ star_grammar ];
      exists [ ], [ ];
    exact grammar_deri_of_tran_deri h_step ( grammar_deri_self ) |> fun h => by simpa using grammar_deri_with_postfix _ h;
  -- Apply terminal_scan to derive: [R] ++ (map (fun v => [H] ++ map terminal v) L).flatten ++ [H] →* map terminal L.flatten ++ [R, H]
  have h3 : grammar_derives (star_grammar g) ([R] ++ (List.map (fun v => [H] ++ List.map symbol.terminal v) L).flatten ++ [H]) (List.map symbol.terminal L.flatten ++ [R, H]) := by
    apply terminal_scan;
    exact?;
  -- Apply step_RH_delete (R H → ε) at the end.
  have h4 : grammar_derives (star_grammar g) (List.map symbol.terminal L.flatten ++ [R, H]) (List.map symbol.terminal L.flatten) := by
    apply grammar_deri_of_tran_deri;
    rotate_right;
    exact List.map symbol.terminal L.flatten;
    · constructor;
      swap;
      exact grule.mk [] ( Sum.inr 2 ) [ symbol.nonterminal ( Sum.inr 1 ) ] [ ];
      simp +decide [ star_grammar ];
      exact ⟨ Or.inl rfl, ( List.map ( List.map symbol.terminal ) L ).flatten, [ ], by simp +decide [ H, R ] ⟩;
    · exact?;
  -- Reorganize the separator structure:
  have h5 : grammar_derives (star_grammar g) ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map symbol.terminal) L)).flatten) ([R] ++ (List.map (fun v => [H] ++ List.map symbol.terminal v) L).flatten ++ [H]) := by
    have h5 : grammar_derives (star_grammar g) ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map symbol.terminal) L)).flatten) ([R] ++ [H] ++ (List.map (fun v => List.map symbol.terminal v ++ [H]) L).flatten) := by
      simp +decide [ List.map_map ];
      exact Relation.ReflTransGen.refl;
    convert h5 using 1;
    clear h1 h2 h3 h4 h5 hL v;
    induction L <;> aesop;
  exact grammar_deri_of_deri_deri ( grammar_deri_of_deri_deri ( grammar_deri_of_deri_deri h1 h2 ) h5 ) ( grammar_deri_of_deri_deri h3 h4 )

end easy_direction

section hard_direction

-- The star_induction invariant
private def StarInvariant (g : grammar T) (α : List (ns T g.nt)) : Prop :=
  (∃ x : List (List (symbol T g.nt)),
    (∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) ∧
    α = [Z] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) ∨
  (∃ x : List (List (symbol T g.nt)),
    (∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) ∧
    α = [R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) ∨
  (∃ w : List (List T), ∃ β : List T, ∃ γ : List (symbol T g.nt),
    ∃ x : List (List (symbol T g.nt)),
    (∀ wᵢ ∈ w, grammar_generates g wᵢ) ∧
    (grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ)) ∧
    (∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) ∧
    α = List.map symbol.terminal w.flatten ++
        List.map symbol.terminal β ++ [R] ++
        List.map wrap_sym γ ++ [H] ++
        (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) ∨
  (∃ u : List T, u ∈ KStar.kstar (grammar_language g) ∧
    α = List.map symbol.terminal u) ∨
  (∃ σ : List (symbol T g.nt), α = List.map wrap_sym σ ++ [R]) ∨
  ((∃ ω : List (ns T g.nt), α = ω ++ [H]) ∧ Z ∉ α ∧ R ∉ α)

/-
No grammar can transform a string of all terminals.
-/
private lemma no_transform_all_terminals {g' : grammar T} {u : List T} {α' : List (symbol T g'.nt)} :
    ¬ grammar_transforms g' (List.map symbol.terminal u) α' := by
  rintro ⟨ r, hr, u, v, hv ⟩;
  replace hv := congr_arg ( fun x => symbol.nonterminal r.input_N ∈ x ) hv.1 ; aesop

-- Helper: wrap_sym properties
private lemma wrap_sym_injective {N : Type} : Function.Injective (@wrap_sym T N) := by
  intro a b h; cases a <;> cases b <;> simp [wrap_sym] at h <;> exact congrArg _ h

private lemma wrap_sym_ne_Z {N : Type} (a : symbol T N) : wrap_sym a ≠ @Z T N := by
  cases a <;> simp [wrap_sym, Z]

private lemma wrap_sym_ne_H {N : Type} (a : symbol T N) : wrap_sym a ≠ @H T N := by
  cases a <;> simp [wrap_sym, H]

private lemma wrap_sym_ne_R {N : Type} (a : symbol T N) : wrap_sym a ≠ @R T N := by
  cases a <;> simp [wrap_sym, R]

private lemma Z_not_in_map_wrap {N : Type} {l : List (symbol T N)} :
    Z ∉ List.map wrap_sym l := by
  intro h; rw [List.mem_map] at h; rcases h with ⟨a, _, ha⟩; exact wrap_sym_ne_Z a ha

private lemma H_not_in_map_wrap {N : Type} {l : List (symbol T N)} :
    H ∉ List.map wrap_sym l := by
  intro h; rw [List.mem_map] at h; rcases h with ⟨a, _, ha⟩; exact wrap_sym_ne_H a ha

private lemma R_not_in_map_wrap {N : Type} {l : List (symbol T N)} :
    R ∉ List.map wrap_sym l := by
  intro h; rw [List.mem_map] at h; rcases h with ⟨a, _, ha⟩; exact wrap_sym_ne_R a ha

/-
The hard direction cases are very complex (see RE_star.lean Lean 3 proof, ~2000 lines).

Case 1: β = [Z] ++ (map (\cdot ++ [H]) (map (map wrap_sym) x)).flatten
-/
private lemma star_case_1 {g : grammar T} {α' : List (ns T g.nt)}
    {x : List (List (symbol T g.nt))}
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (h_step : grammar_transforms (star_grammar g)
      ([Z] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) α') :
  StarInvariant g α' := by
  revert h_step;
  unfold grammar_transforms;
  simp +decide [ star_grammar ];
  rintro ( ⟨ u, v, huv, rfl ⟩ | ⟨ u, v, huv, rfl ⟩ | ⟨ u, v, huv, rfl ⟩ | ⟨ u, v, huv, rfl ⟩ | ⟨ a, ha, u, v, huv, rfl ⟩ );
  · rcases u with ( _ | ⟨ a, u ⟩ ) <;> simp_all +decide [ Z ];
    · refine' Or.inl ⟨ _, _, _ ⟩;
      exact [ [ symbol.nonterminal g.initial ] ] ++ x;
      · grind +suggestions;
      · aesop;
    · have h_contradiction : symbol.nonterminal (Sum.inr 0) ∉ (List.map (fun x => x ++ [H]) (List.map (List.map wrap_sym) x)).flatten := by
        convert Z_not_in_blocks using 1;
      aesop;
  · rcases u with ( _ | ⟨ a, u ⟩ ) <;> simp_all +decide [ Z ];
    · exact Or.inr <| Or.inl ⟨ x, hx, by aesop ⟩;
    · have h_contradiction : symbol.nonterminal (Sum.inr 0) ∈ (List.map ((fun x => x ++ [H]) ∘ List.map wrap_sym) x).flatten := by
        aesop;
      exact absurd h_contradiction ( by simpa using Z_not_in_blocks );
  · rcases u with ( _ | ⟨ a, u ⟩ ) <;> simp_all +decide [ Z ];
    have h_contra : symbol.nonterminal (Sum.inr 2) ∈ (List.map ((fun x => x ++ [H]) ∘ List.map wrap_sym) x).flatten := by
      aesop;
    exact absurd h_contra ( by simpa using R_not_in_blocks );
  · rcases u with ( _ | ⟨ a, u ⟩ ) <;> simp_all +decide [ Z ];
    have h_contradiction : symbol.nonterminal (Sum.inr 2) ∈ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten := by
      aesop;
    exact absurd h_contradiction ( by simpa using R_not_in_blocks );
  · rcases ha with ( ⟨ r, hr, rfl ⟩ | ha );
    · rcases u with ( _ | ⟨ u, u ⟩ ) <;> simp_all +decide [ wrap_gr ];
      · replace huv := congr_arg List.head? huv ; simp_all +decide [ Z ];
        cases h : r.input_L.head? <;> simp_all +decide [ wrap_sym ];
        cases ‹symbol T g.nt› <;> cases huv;
      · -- Apply the match_in_block lemma to find the block in x that contains the nonterminal.
        obtain ⟨x₁, xₘ, x₂, u₁, v₁, hx₁, hx₂, hu₁, hv₁⟩ : ∃ x₁ xₘ x₂ u₁ v₁, x = x₁ ++ [xₘ] ++ x₂ ∧ xₘ = u₁ ++ r.input_L ++ [symbol.nonterminal r.input_N] ++ r.input_R ++ v₁ ∧ u = (List.map (· ++ [H]) (List.map (List.map wrap_sym) x₁)).flatten ++ List.map wrap_sym u₁ ∧ v = List.map wrap_sym v₁ ++ [H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x₂)).flatten := by
          apply match_in_block;
          · rintro rfl; simp_all +decide [ List.flatten ];
          · aesop;
        -- Apply the valid_update_block lemma to show that the new block is valid.
        have h_valid_update : ∀ xᵢ ∈ x₁ ++ [u₁ ++ r.output_string ++ v₁] ++ x₂, grammar_derives g [symbol.nonterminal g.initial] xᵢ := by
          apply valid_update_block;
          · grind;
          · assumption;
        exact Or.inl ⟨ x₁ ++ [ u₁ ++ r.output_string ++ v₁ ] ++ x₂, h_valid_update, by aesop ⟩;
    · unfold rules_that_scan_terminals at ha; simp_all +decide [ List.mem_map ] ;
      rcases ha with ⟨ t, ht, rfl ⟩;
      rcases u with ( _ | ⟨ u, u ⟩ ) <;> simp_all +decide [ Z ];
      have h_contradiction : symbol.nonterminal (Sum.inr 2) ∉ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten := by
        convert R_not_in_blocks using 1;
      aesop

/-
The blocks structure ends with [H] when x ≠ [].
-/
private lemma blocks_end_with_H {N : Type} {x : List (List (symbol T N))} (hne : x ≠ []) :
    ∃ ω : List (ns T N), (List.map (· ++ [@H T N]) (List.map (List.map wrap_sym) x)).flatten = ω ++ [H] := by
  induction' x using List.reverseRecOn with x xs ih <;> simp_all +decide [ List.map ];
  by_cases hx : x = [] <;> simp_all +decide [ Function.comp ];
  exact ⟨ _, by rw [ ih.choose_spec, ← List.append_assoc ] ⟩

/-
R uniqueness: if R only appears at position 0 in R :: tail, and
    R :: tail = u ++ [R] ++ rest, then u = [].
-/
private lemma R_at_head_means_u_nil {g : grammar T}
    {x : List (List (symbol T g.nt))}
    {u rest : List (ns T g.nt)}
    (hbef : R :: H :: (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
      u ++ [R] ++ rest) :
  u = [] := by
  -- Apply the lemma head_unique_elem with the given hypotheses.
  have := head_unique_elem (by
  rfl : R :: H :: (List.map (· ++ [@H T g.nt]) (List.map (List.map wrap_sym) x)).flatten = @R T g.nt :: (R :: H :: (List.map (· ++ [@H T g.nt]) (List.map (List.map wrap_sym) x)).flatten).tail) (by
  convert R_only_at_head using 1 : @R T g.nt ∉ (R :: H :: (List.map (· ++ [@H T g.nt]) (List.map (List.map wrap_sym) x)).flatten).tail) hbef; aesop

/-
Case 2 wrapped rule sub-case
-/
private lemma star_case_2_wrapped {g : grammar T}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt}
    {u v : List (ns T g.nt)}
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (hbef : [R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
      u ++ List.map wrap_sym r₀.input_L ++
        [symbol.nonterminal (Sum.inl r₀.input_N)] ++
        List.map wrap_sym r₀.input_R ++ v) :
  StarInvariant g (u ++ List.map wrap_sym r₀.output_string ++ v) := by
  rcases u with ( _ | ⟨ u, _ | ⟨ u, u ⟩ ⟩ ) <;> simp_all +decide [ List.map ];
  · replace hbef := congr_arg List.head? hbef ; simp_all +decide;
    cases h : r₀.input_L <;> simp_all +decide [ R ];
    cases ‹symbol T g.nt› <;> cases hbef;
  · rcases hbef with ⟨ rfl, hbef ⟩;
    cases h : r₀.input_L <;> simp_all +decide [ Function.comp ];
    · cases hbef.1;
    · cases ‹symbol T g.nt› <;> cases hbef.1;
  · rcases hbef with ⟨ rfl, rfl, h ⟩;
    have := @match_in_block;
    specialize @this T g.nt r₀ x u v ; simp_all +decide [ List.map ];
    by_cases hx : x = [] <;> simp_all +decide [ List.map ];
    obtain ⟨ x₁, x₂, u₁, v₁, rfl, rfl, rfl ⟩ := this h; ( have := @valid_update_block; simp_all +decide [ List.map ] ; );
    refine' Or.inr <| Or.inl ⟨ x₁ ++ [ u₁ ++ ( r₀.output_string ++ v₁ ) ] ++ x₂, _, _ ⟩ <;> simp_all +decide [ List.map ];
    · exact this hx hr₀;
    · congr! 2

/-- Z is not in [R, H] ++ blocks(x) -/
private lemma Z_not_in_case2_string {g : grammar T}
    {x : List (List (symbol T g.nt))} :
    Z ∉ ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten : List (ns T g.nt)) := by
  simp [List.mem_append, R, H, Z]
  intro a _ b _; cases b <;> simp [wrap_sym]

/-- R only appears at position 0 in [R, H] ++ blocks(x) -/
private lemma R_unique_in_case2 {g : grammar T}
    {x : List (List (symbol T g.nt))}
    {u rest : List (ns T g.nt)}
    (h : ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten : List (ns T g.nt)) =
      u ++ [symbol.nonterminal (Sum.inr 2)] ++ rest) :
    u = [] := by
  have := R_at_head_means_u_nil (show R :: H :: _ = u ++ [R] ++ rest from by
    simp [List.append_assoc] at h ⊢; exact h)
  exact this

/-- No rule of star_grammar has input_N = Sum.inr 1 (H) -/
private lemma no_H_rule {g : grammar T} {r : grule T (nn g.nt)}
    (hr : r ∈ (star_grammar g).rules) (hi : r.input_N = Sum.inr 1) : False := by
  simp [star_grammar] at hr
  rcases hr with rfl | rfl | rfl | rfl | ⟨a, ha, rfl⟩ | hr <;>
    simp_all +decide [wrap_gr, rules_that_scan_terminals, List.mem_map]
  rcases hr with ⟨t, ht, rfl⟩; simp at hi

/-- Extract wrapped rule from star_grammar rule that is not Sum.inr -/
private lemma extract_wrapped_rule {g : grammar T} {r : grule T (nn g.nt)}
    (hr : r ∈ (star_grammar g).rules)
    (hinr : ∀ i : Fin 3, r.input_N ≠ Sum.inr i) :
    ∃ r₀ ∈ g.rules, r = wrap_gr r₀ := by
  simp [star_grammar] at hr
  rcases hr with rfl | rfl | rfl | rfl | ⟨r₀, hr₀, rfl⟩ | hr
  · exact absurd rfl (hinr 0)
  · exact absurd rfl (hinr 0)
  · exact absurd rfl (hinr 2)
  · exact absurd rfl (hinr 2)
  · exact ⟨r₀, hr₀, rfl⟩
  · simp [rules_that_scan_terminals, List.mem_map] at hr
    rcases hr with ⟨t, ht, rfl⟩
    exact absurd rfl (hinr 2)

/-
Sub-case: R·H→R in case 2. hbef simplifies to v = blocks(x).
-/
private lemma star_case_2_RH_to_R {g : grammar T}
    {x : List (List (symbol T g.nt))}
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) :
    StarInvariant g ([R] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  rcases x with ( _ | ⟨ y, _ | ⟨ z, x ⟩ ⟩ ) <;> simp_all +decide [ StarInvariant ];
  · refine Or.inr <| Or.inr <| Or.inl ⟨ [ ], ?_, [ ], y, ?_, [ ], ?_, rfl ⟩ <;> aesop;
  · refine Or.inr <| Or.inr <| Or.inl <| ?_;
    use [];
    refine' ⟨ by tauto, [ ], y, _, x.cons z, _, _ ⟩ <;> aesop

/-
Sub-case: R·H→ε in case 2. Result is blocks(x).
-/
private lemma star_case_2_RH_to_eps {g : grammar T}
    {x : List (List (symbol T g.nt))}
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) :
    StarInvariant g ((List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  by_cases hx_empty : x = [];
  · exact Or.inr <| Or.inr <| Or.inr <| Or.inl ⟨ [ ],Language.nil_mem_kstar _, by simp +decide [ hx_empty ] ⟩;
  · obtain ⟨ω, hω⟩ := blocks_end_with_H hx_empty;
    refine' Or.inr ( Or.inr ( Or.inr ( Or.inr ( Or.inr ⟨ ⟨ ω, by aesop ⟩, _, _ ⟩ ) ) ) ) <;> simp_all +decide [ List.map_append, List.map_map ];
    · refine' ⟨ _, _ ⟩;
      · intro hZω
        have hZω' : Z ∈ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten := by
          grind
        exact Z_not_in_blocks hZω';
      · rintro ⟨ ⟩;
    · have hR_not_in_blocks : R ∉ (List.map (· ++ [@H T g.nt]) (List.map (List.map wrap_sym) x)).flatten := by
        convert R_not_in_blocks using 1;
      aesop

/-
Case 2: β = [R, H] ++ blocks(x)
-/
private lemma star_case_2 {g : grammar T} {α' : List (ns T g.nt)}
    {x : List (List (symbol T g.nt))}
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (h_step : grammar_transforms (star_grammar g)
      ([R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) α') :
  StarInvariant g α' := by
  obtain ⟨ r, hr, u, v, hbef, _, rfl ⟩ := h_step;
  by_cases h : ∃ i : Fin 3, r.input_N = Sum.inr i;
  · rcases h with ⟨ i, hi ⟩ ; fin_cases i <;> simp_all +decide [ star_grammar ] ;
    · have h_contradiction : Z ∈ [R, H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten := by
        grind +locals;
      exact absurd h_contradiction ( by simpa using Z_not_in_case2_string );
    · grind +locals;
    · -- Since $u ++ r.input_L = []$, we have $u = []$ and $r.input_L = []$.
      have hu_empty : u = [] := by
        apply R_at_head_means_u_nil;
        rotate_left;
        exact x;
        exact r.input_R ++ v;
        rcases hr with ( rfl | rfl | rfl | rfl | ⟨ a, ha, rfl ⟩ | hr ) <;> simp_all +decide [ Function.comp ];
        · rfl;
        · rfl;
        · cases hi;
        · unfold rules_that_scan_terminals at hr; aesop;
      have hr_input_L_empty : r.input_L = [] := by
        grind +locals;
      -- Since $r.input_R = [H]$, we have $v = (List.map ((fun x => x ++ [H]) ∘ List.map wrap_sym) x).flatten$.
      have hv_eq : v = (List.map ((fun x => x ++ [H]) ∘ List.map wrap_sym) x).flatten := by
        grind +locals;
      rcases hr with ( rfl | rfl | rfl | rfl | ⟨ a, ha, rfl ⟩ | hr ) <;> simp_all +decide [ star_grammar ];
      · convert star_case_2_RH_to_R hx using 1;
        aesop;
      · convert star_case_2_RH_to_eps hx using 1;
        rw [ List.map_map ];
      · unfold wrap_gr at hi; aesop;
      · unfold rules_that_scan_terminals at hr; simp_all +decide [ List.mem_map ] ;
        rcases hr with ⟨ a, ha, rfl ⟩ ; simp_all +decide [ StarInvariant ] ;
        cases hbef.2;
  · obtain ⟨ r₀, hr₀, rfl ⟩ := extract_wrapped_rule hr ( by simpa using h ) ; exact star_case_2_wrapped hx hr₀ hbef;

/-
Z is not in case 3 string
-/
private lemma Z_not_in_case3_string {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))} :
    Z ∉ (List.map symbol.terminal w.flatten ++
         List.map symbol.terminal β ++ [R] ++
         List.map wrap_sym γ ++ [H] ++
         (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten :
         List (ns T g.nt)) := by
  unfold Z; simp +decide [ List.mem_append ] ;
  unfold R H; simp +decide ;
  exact ⟨ fun _ _ => wrap_sym_ne_Z _, fun _ _ _ _ => wrap_sym_ne_Z _ ⟩

/-
R is unique in case 3 string
-/
private lemma R_unique_in_case3 {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {u rest : List (ns T g.nt)}
    (h : List.map symbol.terminal w.flatten ++
         List.map symbol.terminal β ++ [R] ++
         List.map wrap_sym γ ++ [H] ++
         (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
       u ++ [R] ++ rest) :
    u = List.map symbol.terminal (w.flatten ++ β) := by
  have h_u : ∀ {l₁ l₂ l₃ : List (ns T g.nt)}, l₁ ++ [R] ++ l₂ = l₃ ++ [R] ++ rest → R ∉ l₁ → R ∉ l₂ → l₁ = l₃ := by
    intros l₁ l₂ l₃ h_eq h₁ h₂; induction' l₁ with l₁_head l₁_tail l₁_ih generalizing l₃ <;> induction' l₃ with l₃_head l₃_tail l₃_ih <;> simp_all +decide [ List.append_assoc ] ;
  contrapose! h_u;
  refine' ⟨ ( List.map ( List.map symbol.terminal ) w ).flatten ++ List.map symbol.terminal β, List.map wrap_sym γ ++ H :: ( List.map ( ( fun x => x ++ [ H ] ) ∘ List.map wrap_sym ) x ).flatten, u, _, _, _, _ ⟩ <;> simp_all +decide [ List.append_assoc ];
  · exact ⟨ fun x hx y hy => ne_of_apply_ne ( fun z => z ) ( by simp +decide [ R ] ), fun x hx => ne_of_apply_ne ( fun z => z ) ( by simp +decide [ R ] ) ⟩;
  · exact ⟨ fun x hx => by intro H; have := wrap_sym_ne_R x; aesop, by rintro ⟨ ⟩, fun x hx => ⟨ fun y hy => by intro H; have := wrap_sym_ne_R y; aesop, by rintro ⟨ ⟩ ⟩ ⟩;
  · grind +ring

/-
Wrapped match in γ: decompose γ and update derivation
-/
private lemma star_case_3_wrapped_in_gamma {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt}
    {γ_L γ_R : List (symbol T g.nt)}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (hγ_eq : γ = γ_L ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++
              r₀.input_R ++ γ_R) :
    StarInvariant g
      (List.map symbol.terminal w.flatten ++
       List.map symbol.terminal β ++ [R] ++
       List.map wrap_sym (γ_L ++ r₀.output_string ++ γ_R) ++ [H] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  refine Or.inr <| Or.inr <| Or.inl ⟨ w, β, γ_L ++ r₀.output_string ++ γ_R, x, hw, ?_, hx, ?_ ⟩;
  · convert grammar_deri_of_deri_tran hβγ _ using 1;
    exact ⟨ r₀, hr₀, List.map symbol.terminal β ++ γ_L, γ_R, by aesop ⟩;
  · rfl

/-
Wrapped match in blocks: update block
-/
private lemma star_case_3_wrapped_in_blocks {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt}
    {x₁ : List (List (symbol T g.nt))} {xₘ : List (symbol T g.nt)}
    {x₂ : List (List (symbol T g.nt))}
    {u₁ v₁ : List (symbol T g.nt)}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (hx_eq : x = x₁ ++ [xₘ] ++ x₂)
    (hxₘ_eq : xₘ = u₁ ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++
              r₀.input_R ++ v₁) :
    StarInvariant g
      (List.map symbol.terminal w.flatten ++
       List.map symbol.terminal β ++ [R] ++
       List.map wrap_sym γ ++ [H] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym)
         (x₁ ++ [u₁ ++ r₀.output_string ++ v₁] ++ x₂))).flatten) := by
  -- Apply the valid_update_block lemma to conclude the proof.
  have h_valid_update : ∀ xᵢ ∈ x₁ ++ [u₁ ++ r₀.output_string ++ v₁] ++ x₂, grammar_derives g [symbol.nonterminal g.initial] xᵢ := by
    apply_rules [ valid_update_block ];
    aesop;
  apply Or.inr;
  refine Or.inr <| Or.inl ⟨ w, β, γ, x₁ ++ [ u₁ ++ r₀.output_string ++ v₁ ] ++ x₂, hw, hβγ, h_valid_update, ?_ ⟩;
  rfl

/-
R is not in the wrapped rule input pattern.
-/
private lemma R_not_in_wrapped_input_local {g : grammar T} {r₀ : grule T g.nt} :
    R ∉ List.map wrap_sym r₀.input_L ++
      [symbol.nonterminal (Sum.inl r₀.input_N)] ++
      List.map wrap_sym r₀.input_R := by
  simp +decide [ R, wrap_sym ];
  constructor <;> intro x hx <;> cases x <;> simp +decide [ * ] at hx ⊢

/-
R is not in wrap(γ) ++ [H] ++ blocks(x)
-/
private lemma R_not_in_gamma_H_blocks {g : grammar T}
    {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))} :
    R ∉ (List.map wrap_sym γ ++ [H] ++
        (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten :
        List (ns T g.nt)) := by
  unfold R H wrap_sym; aesop;

/-- The nonterminal Sum.inl n can only appear in wrap(γ) or blocks, not in terminals or [R].
    So u must contain the terminal prefix and [R]. -/
private lemma case3_wrapped_strip_prefix {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt} {u v : List (ns T g.nt)}
    (hbef : List.map symbol.terminal w.flatten ++
            List.map symbol.terminal β ++ [R] ++
            List.map wrap_sym γ ++ [H] ++
            (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
          u ++ List.map wrap_sym r₀.input_L ++
            [symbol.nonterminal (Sum.inl r₀.input_N)] ++
            List.map wrap_sym r₀.input_R ++ v) :
    ∃ u' : List (ns T g.nt),
      u = List.map symbol.terminal (w.flatten ++ β) ++ [R] ++ u' ∧
      List.map wrap_sym γ ++ [H] ++
        (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
        u' ++ List.map wrap_sym r₀.input_L ++
          [symbol.nonterminal (Sum.inl r₀.input_N)] ++
          List.map wrap_sym r₀.input_R ++ v := by
  -- R appears once on the LHS at the boundary between terminals and wrap(γ).
  -- The wrapped rule pattern doesn't contain R.
  -- So using split_at_H with separator=R, the match must be after R.
  have h_R_not_in_mid : R ∉ (List.map wrap_sym r₀.input_L ++
      [symbol.nonterminal (Sum.inl r₀.input_N)] ++
      List.map wrap_sym r₀.input_R) := R_not_in_wrapped_input_local
  have hbef' : List.map symbol.terminal (w.flatten ++ β) ++ [R] ++
      (List.map wrap_sym γ ++ [H] ++
        (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) =
      u ++ (List.map wrap_sym r₀.input_L ++
        [symbol.nonterminal (Sum.inl r₀.input_N)] ++
        List.map wrap_sym r₀.input_R) ++ v := by
    simp only [List.append_assoc, List.map_append] at hbef ⊢; exact hbef
  rcases split_at_separator h_R_not_in_mid hbef' with ⟨u₁, h_a, h_v₁⟩ | ⟨v₁, h_u₁, h_b₁⟩
  · -- Case: match is in terminals. But terminals can't contain nonterminals.
    exfalso
    have : symbol.nonterminal (Sum.inl r₀.input_N) ∈
        List.map (symbol.terminal (N := nn g.nt)) (w.flatten ++ β) := by
      rw [h_a]; simp [List.mem_append]
    exact nonterminal_not_in_map_terminal this
  · -- Case: match is after R. Then u = terminals ++ [R] ++ v₁.
    exact ⟨v₁, h_u₁, by simp only [List.append_assoc] at h_b₁ ⊢; exact h_b₁⟩

/-
H is not in the wrapped rule input pattern (local version).
-/
private lemma H_not_in_wrapped_input_local {g : grammar T} {r₀ : grule T g.nt} :
    H ∉ List.map wrap_sym r₀.input_L ++
      [symbol.nonterminal (Sum.inl r₀.input_N)] ++
      List.map wrap_sym r₀.input_R := by
  simp +decide [ H, wrap_sym ];
  constructor <;> intros x hx <;> cases x <;> simp +decide [ Sum.inl.injEq, Sum.inr.injEq ] at hx ⊢

/-
Local version of split_at_separator for H
-/
private lemma split_at_H {g : grammar T}
    {a b u v : List (ns T g.nt)}
    {mid : List (ns T g.nt)}
    (h_H_not_in_mid : H ∉ mid)
    (h_eq : a ++ [H] ++ b = u ++ mid ++ v) :
  (∃ u' : List (ns T g.nt), a = u ++ mid ++ u' ∧ v = u' ++ [H] ++ b) ∨
  (∃ v' : List (ns T g.nt), u = a ++ [H] ++ v' ∧ b = v' ++ mid ++ v) := by
  by_cases hu : u.length ≤ a.length <;> simp_all +decide [ List.append_eq_append_iff ];
  · rcases h_eq with ( ⟨ as, rfl, h ⟩ | ⟨ bs, rfl, h ⟩ ) <;> simp_all +decide [ List.append_assoc ];
    · cases mid <;> aesop;
    · rcases h with ( ⟨ as, rfl, rfl ⟩ | ⟨ bs', rfl, h ⟩ ) <;> simp_all +decide [ List.append_assoc ];
      cases bs' <;> aesop;
  · rcases h_eq with ( ⟨ as, rfl, h ⟩ | ⟨ bs, rfl, h ⟩ ) <;> simp_all +decide [ List.length_append ];
    cases as <;> aesop

/-
Case 3 wrapped: match in γ
-/
private lemma case3_wrapped_gamma {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt}
    {u' u'' : List (ns T g.nt)}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (h_gamma : List.map wrap_sym γ =
      u' ++ List.map wrap_sym r₀.input_L ++
        [symbol.nonterminal (Sum.inl r₀.input_N)] ++
        List.map wrap_sym r₀.input_R ++ u'') :
    StarInvariant g
      (List.map symbol.terminal (w.flatten ++ β) ++ [R] ++
       u' ++ List.map wrap_sym r₀.output_string ++ u'' ++ [H] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  -- Let's verify that $u'$ and $u''$ are in the range of $wrap_sym$.
  have hu'_in_range : ∃ γ_L : List (symbol T g.nt), u' = List.map wrap_sym γ_L := by
    have hu'_in_range : ∀ u' : List (ns T g.nt), (∀ u'ᵢ ∈ u', u'ᵢ ∈ List.map wrap_sym γ) → ∃ γ_L : List (symbol T g.nt), u' = List.map wrap_sym γ_L := by
      intros u' hu'
      induction' u' with u' ih;
      · exact ⟨ [ ], rfl ⟩;
      · simp +zetaDelta at *;
        rcases hu' with ⟨ ⟨ a, ha, rfl ⟩, hu' ⟩ ; rcases ‹ ( ∀ u'ᵢ ∈ ih, ∃ a ∈ γ, wrap_sym a = u'ᵢ ) → ∃ γ_L, ih = List.map wrap_sym γ_L › hu' with ⟨ γ_L, rfl ⟩ ; exact ⟨ a :: γ_L, by simp +decide ⟩ ;
    exact hu'_in_range u' fun u'ᵢ hu'ᵢ => h_gamma.symm ▸ List.mem_append_left _ ( List.mem_append_left _ ( List.mem_append_left _ ( List.mem_append_left _ hu'ᵢ ) ) )
  have hu''_in_range : ∃ γ_R : List (symbol T g.nt), u'' = List.map wrap_sym γ_R := by
    have hu''_in_range : ∀ a ∈ u'', ∃ b : symbol T g.nt, a = wrap_sym b := by
      intro a ha
      have ha_gamma : a ∈ List.map wrap_sym γ := by
        grind;
      rw [ List.mem_map ] at ha_gamma; obtain ⟨ b, hb, rfl ⟩ := ha_gamma; exact ⟨ b, rfl ⟩ ;
    have hu''_in_range : ∀ {l : List (ns T g.nt)}, (∀ a ∈ l, ∃ b : symbol T g.nt, a = wrap_sym b) → ∃ γ_R : List (symbol T g.nt), l = List.map wrap_sym γ_R := by
      intros l hl; induction' l with a l ih <;> simp_all +decide [ List.map ] ;
      rcases hl.1 with ⟨ b, rfl ⟩ ; rcases ih with ⟨ γ_R, rfl ⟩ ; exact ⟨ b :: γ_R, by simp +decide ⟩ ;
    exact hu''_in_range ‹_›;
  obtain ⟨ γ_L, rfl ⟩ := hu'_in_range; obtain ⟨ γ_R, rfl ⟩ := hu''_in_range;
  -- Let's verify that γ = γ_L ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++ r₀.input_R ++ γ_R.
  have h_gamma_eq : γ = γ_L ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++ r₀.input_R ++ γ_R := by
    have h_gamma_eq : List.map wrap_sym γ = List.map wrap_sym (γ_L ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++ r₀.input_R ++ γ_R) := by
      aesop;
    exact List.map_injective_iff.mpr ( wrap_sym_injective ) h_gamma_eq;
  convert star_case_3_wrapped_in_gamma hw hβγ hx hr₀ h_gamma_eq using 1;
  simp +decide [ List.map_append ]

/-
Case 3 wrapped: match in blocks
-/
private lemma case3_wrapped_blocks {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt}
    {v' v : List (ns T g.nt)}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (h_blocks : (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
      v' ++ List.map wrap_sym r₀.input_L ++
        [symbol.nonterminal (Sum.inl r₀.input_N)] ++
        List.map wrap_sym r₀.input_R ++ v) :
    StarInvariant g
      (List.map symbol.terminal (w.flatten ++ β) ++ [R] ++
       List.map wrap_sym γ ++ [H] ++
       v' ++ List.map wrap_sym r₀.output_string ++ v) := by
  by_contra h_contra;
  obtain ⟨x₁, xₘ, x₂, u₁, v₁, hx_eq, hxₘ_eq⟩ : ∃ x₁ : List (List (symbol T g.nt)), ∃ xₘ : List (symbol T g.nt), ∃ x₂ : List (List (symbol T g.nt)), ∃ u₁ : List (symbol T g.nt), ∃ v₁ : List (symbol T g.nt), x = x₁ ++ [xₘ] ++ x₂ ∧ xₘ = u₁ ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++ r₀.input_R ++ v₁ ∧ v' = (List.map (· ++ [H]) (List.map (List.map wrap_sym) x₁)).flatten ++ List.map wrap_sym u₁ ∧ v = List.map wrap_sym v₁ ++ [H] ++ (List.map (· ++ [H]) (List.map (List.map wrap_sym) x₂)).flatten := by
    have := @match_in_block T g.nt r₀ x v' v;
    by_cases hx' : x = [] <;> simp_all +decide;
    convert this _;
    convert h_blocks using 1;
  refine' h_contra _;
  convert star_case_3_wrapped_in_blocks hw hβγ ( fun xᵢ hxᵢ => hx xᵢ ( by aesop ) ) hr₀ hx_eq hxₘ_eq.1 using 1;
  simp +decide [ hxₘ_eq, List.flatten ]

/-- Case 3 wrapped rule sub-case -/
private lemma star_case_3_wrapped {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {r₀ : grule T g.nt} {u v : List (ns T g.nt)}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (hr₀ : r₀ ∈ g.rules)
    (hbef : List.map symbol.terminal w.flatten ++
            List.map symbol.terminal β ++ [R] ++
            List.map wrap_sym γ ++ [H] ++
            (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
          u ++ List.map wrap_sym r₀.input_L ++
            [symbol.nonterminal (Sum.inl r₀.input_N)] ++
            List.map wrap_sym r₀.input_R ++ v) :
    StarInvariant g (u ++ List.map wrap_sym r₀.output_string ++ v) := by
  obtain ⟨u', hu_eq, h_suffix⟩ := case3_wrapped_strip_prefix hbef
  rw [hu_eq]
  have h_suffix' : List.map wrap_sym γ ++ [H] ++
      (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
      u' ++ (List.map wrap_sym r₀.input_L ++
        [symbol.nonterminal (Sum.inl r₀.input_N)] ++
        List.map wrap_sym r₀.input_R) ++ v := by
    simp only [List.append_assoc] at h_suffix ⊢; exact h_suffix
  rcases split_at_H H_not_in_wrapped_input_local h_suffix' with ⟨u'', h_gamma, h_v⟩ | ⟨v', h_u', h_blocks⟩
  · rw [h_v]
    have h_gamma' : List.map wrap_sym γ =
        u' ++ List.map wrap_sym r₀.input_L ++
          [symbol.nonterminal (Sum.inl r₀.input_N)] ++
          List.map wrap_sym r₀.input_R ++ u'' := by
      simp only [List.append_assoc] at h_gamma ⊢; exact h_gamma
    convert case3_wrapped_gamma hw hβγ hx hr₀ h_gamma' using 1
    simp [List.append_assoc]
  · rw [h_u']
    have h_blocks' : (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten =
        v' ++ List.map wrap_sym r₀.input_L ++
          [symbol.nonterminal (Sum.inl r₀.input_N)] ++
          List.map wrap_sym r₀.input_R ++ v := by
      simp only [List.append_assoc] at h_blocks ⊢; exact h_blocks
    convert case3_wrapped_blocks hw hβγ hx hr₀ h_blocks' using 1
    simp [List.append_assoc]

/-
Case 3 sub-case: R·H → R with γ = []
-/
private lemma star_case_3_RH_to_R {g : grammar T}
    {w : List (List T)} {β : List T}
    {x : List (List (symbol T g.nt))}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβ : grammar_generates g β)
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) :
    StarInvariant g
      (List.map symbol.terminal (w.flatten ++ β) ++ [R] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  by_cases hx_empty : x = [];
  · -- Since x is empty, the result string simplifies to terminals(w.flatten ++ β) ++ [R], which is case 5.
    use Or.inr (Or.inr (Or.inr (Or.inr (Or.inl ⟨List.map symbol.terminal (w.flatten ++ β), by
      unfold wrap_sym; aesop;⟩))) );
  · -- Since x is not empty, we can take y as the first element of x.
    obtain ⟨y, rest, hx_eq⟩ : ∃ y rest, x = y :: rest := by
      exact List.exists_cons_of_ne_nil hx_empty;
    apply Or.inr; apply Or.inr; apply Or.inl; use w ++ [β], [], y, rest; aesop;

/-
Case 3 sub-case: R·H → ε with γ = []
-/
private lemma star_case_3_RH_to_eps {g : grammar T}
    {w : List (List T)} {β : List T}
    {x : List (List (symbol T g.nt))}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβ : grammar_generates g β)
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) :
    StarInvariant g
      (List.map symbol.terminal (w.flatten ++ β) ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  by_cases hx_empty : x = [] <;> simp_all +decide [ StarInvariant ];
  · refine Or.inr <| Or.inr <| Or.inr <| Or.inl ⟨ w.flatten ++ β, ?_, ?_ ⟩ <;> simp_all +decide [ KStar.kstar ];
    exact ⟨ w ++ [ β ], by simp +decide, fun y hy => by aesop ⟩;
  · right;
    right;
    refine Or.inr <| Or.inr <| Or.inr ?_;
    refine' ⟨ _, _, _, _ ⟩;
    · exact blocks_end_with_H hx_empty |> fun ⟨ ω, hω ⟩ => ⟨ ( List.map ( List.map symbol.terminal ) w ).flatten ++ ( List.map symbol.terminal β ) ++ ω, by aesop ⟩;
    · refine' ⟨ _, _, _ ⟩;
      · exact fun _ _ _ _ => by rintro ⟨ ⟩ ;
      · exact fun t ht => by rintro ⟨ ⟩ ;
      · exact fun y hy => ⟨ fun z hz => wrap_sym_ne_Z z, by rintro ⟨ ⟩ ⟩;
    · rintro x hx y hy; exact ne_of_apply_ne ( fun z => z ) ( by simp +decide [ R ] ) ;
    · exact ⟨ fun x hx => by rintro ⟨ ⟩, fun x hx => ⟨ fun y hy => wrap_sym_ne_R _, by rintro ⟨ ⟩ ⟩ ⟩

/-
Case 3 sub-case: R·t → t·R scanning
-/
private lemma star_case_3_scan {g : grammar T}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    {t : T}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ symbol.terminal t :: γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ) :
    StarInvariant g
      (List.map symbol.terminal (w.flatten ++ β ++ [t]) ++ [R] ++
       List.map wrap_sym γ ++ [H] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) := by
  refine Or.inr <| Or.inr <| Or.inl ⟨ w, β ++ [ t ], γ, x, ?_, ?_, ?_, ?_ ⟩;
  · assumption;
  · aesop;
  · assumption;
  · simp +decide [ List.map_append ]

/-
Case 3: Scanning phase
-/
private lemma star_case_3 {g : grammar T} {α' : List (ns T g.nt)}
    {w : List (List T)} {β : List T} {γ : List (symbol T g.nt)}
    {x : List (List (symbol T g.nt))}
    (hw : ∀ wᵢ ∈ w, grammar_generates g wᵢ)
    (hβγ : grammar_derives g [symbol.nonterminal g.initial]
      (List.map symbol.terminal β ++ γ))
    (hx : ∀ xᵢ ∈ x, grammar_derives g [symbol.nonterminal g.initial] xᵢ)
    (h_step : grammar_transforms (star_grammar g)
      (List.map symbol.terminal w.flatten ++
       List.map symbol.terminal β ++ [R] ++
       List.map wrap_sym γ ++ [H] ++
       (List.map (· ++ [H]) (List.map (List.map wrap_sym) x)).flatten) α') :
  StarInvariant g α' := by
  obtain ⟨ r, hr, u, v, hbef, rfl ⟩ := h_step;
  by_cases h : ∃ i : Fin 3, r.input_N = Sum.inr i;
  · rcases h with ⟨ i, hi ⟩ ; fin_cases i <;> simp_all +decide [ star_grammar ] ;
    · replace hbef := congr_arg ( fun l => Z ∈ l ) hbef ; simp_all +decide [ Z_not_in_case3_string ] ;
      contrapose! hbef; simp_all +decide [ Z, R, H ] ;
      exact ⟨ fun a ha => wrap_sym_ne_Z a, fun a ha b hb => wrap_sym_ne_Z b ⟩;
    · exact False.elim <| no_H_rule ( by
        unfold star_grammar; aesop; ) hi;
    · -- By R_unique_in_case3, u = map terminal (w.flatten ++ β).
      have hu : u = List.map symbol.terminal (w.flatten ++ β) := by
        apply R_unique_in_case3;
        convert hbef using 1;
        rotate_left;
        rotate_left;
        exact γ;
        exact x;
        exact r.input_R ++ v;
        · simp +decide [ List.map_flatMap, List.flatten ];
        · rcases hr with ( rfl | rfl | rfl | rfl | ⟨ a, ha, rfl ⟩ | hr ) <;> simp_all +decide [ wrap_gr ];
          · rfl;
          · rfl;
          · unfold rules_that_scan_terminals at hr; aesop;
      rcases hr with ( rfl | rfl | rfl | rfl | ⟨ r₀, hr₀, rfl ⟩ | hr_scan ) <;> simp_all +decide [ star_grammar ];
      · have hγ : γ = [] := by
          cases γ <;> simp_all +decide [ List.map ];
          cases ‹symbol T g.nt› <;> simp_all +decide [ wrap_sym ];
          · cases hbef.2.1;
          · cases hbef.2.1;
        convert star_case_3_RH_to_R hw ( show grammar_generates g β from ?_ ) hx using 1;
        · aesop;
        · unfold grammar_generates; aesop;
      · cases γ <;> simp_all +decide [ wrap_sym ];
        · convert star_case_3_RH_to_eps hw _ hx using 1;
          rotate_left;
          exact β;
          · convert hβγ using 1;
          · simp +decide [ ← hbef.2, List.map_append, List.flatten_append ];
        · cases ‹symbol T g.nt› <;> simp_all +decide [ H ];
      · unfold wrap_gr at hi; aesop;
      · unfold rules_that_scan_terminals at hr_scan; simp_all +decide [ List.mem_map ] ;
        rcases hr_scan with ⟨ a, ha, rfl ⟩ ; simp_all +decide [ List.map ] ;
        cases γ <;> simp_all +decide [ List.map ];
        · cases hbef.2.1;
        · cases ‹symbol T g.nt› <;> simp_all +decide [ wrap_sym ];
          exact Or.inr <| Or.inr <| Or.inl ⟨ w, β ++ [ a ], ‹_›, x, hw, by
            simpa [ List.map_append ] using hβγ, hx, by
            grind ⟩;
  · obtain ⟨ r₀, hr₀, rfl ⟩ := extract_wrapped_rule hr ( by simpa using h );
    convert star_case_3_wrapped hw hβγ hx hr₀ hbef using 1

/-
No rule of star_grammar with input_N = Sum.inr can match a string
    of the form `map wrap_sym σ ++ [R]`.
-/
private lemma no_inr_rule_on_wrap_R {g : grammar T} {σ : List (symbol T g.nt)}
    {r : grule T (nn g.nt)} {u v : List (ns T g.nt)}
    (hr : r ∈ (star_grammar g).rules)
    (hinr : ∃ i : Fin 3, r.input_N = Sum.inr i)
    (hbef : List.map wrap_sym σ ++ [R] =
      u ++ r.input_L ++ [symbol.nonterminal r.input_N] ++ r.input_R ++ v) :
  False := by
  rcases hinr with ⟨ i, hi ⟩ ; rcases i with ( _ | _ | _ | i ) <;> simp_all +decide [ star_grammar ];
  · replace hbef := congr_arg List.reverse hbef ; simp_all +decide [ List.reverse_append ];
    cases v : v.reverse <;> simp_all +decide [ List.reverse_singleton ];
    · grind +locals;
    · have h_contra : ∀ x ∈ List.map wrap_sym σ, x ≠ symbol.nonterminal (Sum.inr 0) := by
        simp +zetaDelta at *;
        intro a ha; cases a <;> simp +decide [ wrap_sym ] ;
      grind;
  · rcases hr with ( rfl | rfl | rfl | rfl | ⟨ a, ha, rfl ⟩ | hr ) <;> simp_all +decide [ List.append_assoc ];
    · cases hi;
    · unfold rules_that_scan_terminals at hr; aesop;
  · rcases hr with ( rfl | rfl | rfl | rfl | ⟨ a, ha, rfl ⟩ | hr ) <;> simp_all +decide [ List.append_assoc ];
    · replace hbef := congr_arg ( fun x => x.dropLast ) hbef ; simp_all +decide [ List.dropLast ] ;
      induction σ generalizing u v <;> simp_all +decide [ List.map ];
      cases u <;> simp_all +decide [ List.dropLast ];
      cases ‹symbol T g.nt› <;> simp_all +decide [ wrap_sym ];
    · replace hbef := congr_arg ( fun x => x.dropLast ) hbef ; simp_all +decide [ List.dropLast ] ;
      induction σ generalizing u v <;> simp_all +decide [ List.map ];
      cases u <;> simp_all +decide [ List.dropLast ];
      cases ‹symbol T g.nt› <;> simp_all +decide [ wrap_sym ];
    · cases hi;
    · replace hbef := congr_arg List.reverse hbef ; simp_all +decide [ List.reverse_append ];
      replace hbef := congr_arg List.tail hbef ; simp_all +decide [ List.tail_append ];
      split_ifs at hbef <;> simp_all +decide [ List.map_eq_append_iff ];
      · unfold rules_that_scan_terminals at hr; aesop;
      · obtain ⟨ l₁, l₁_1, l₂, rfl, hu, hl₁, hl₂ ⟩ := hbef; replace hl₂ := congr_arg List.head? hl₂; simp_all +decide [ List.head? ] ;
        cases l₂ <;> simp_all +decide [ wrap_sym ];
        cases ‹symbol T g.nt› <;> cases hl₂;
      · rcases hbef with ⟨ l₁, l₁_1, l₂, rfl, rfl, h₁, h₂ ⟩ ; replace h₂ := congr_arg List.head? h₂ ; simp_all +decide [ List.head? ] ;
        rcases l₂ with ( _ | ⟨ a, l₂ ⟩ ) <;> simp_all +decide [ List.map ];
        cases a <;> cases h₂;
  · linarith

/-
Case 5: β = map wrap_sym σ ++ [R]
-/
private lemma star_case_5 {g : grammar T} {α' : List (ns T g.nt)}
    {σ : List (symbol T g.nt)}
    (h_step : grammar_transforms (star_grammar g)
      (List.map wrap_sym σ ++ [R]) α') :
  StarInvariant g α' := by
  unfold grammar_transforms at h_step; unfold StarInvariant at *;
  obtain ⟨ r, hr, u, v, hbef, rfl ⟩ := h_step;
  by_cases h : ∃ i : Fin 3, r.input_N = Sum.inr i;
  · exact False.elim <| no_inr_rule_on_wrap_R hr h hbef;
  · -- Since r is a wrapped rule, we can write it as r = wrap_gr r₀ for some r₀ ∈ g.rules.
    obtain ⟨r₀, hr₀, hr_eq⟩ : ∃ r₀ ∈ g.rules, r = wrap_gr r₀ := by
      unfold star_grammar at hr; simp_all +decide [ List.mem_map ] ;
      unfold rules_that_scan_terminals at hr; simp_all +decide [ List.mem_map ] ;
      rcases hr with ( rfl | rfl | rfl | rfl | ⟨ r₀, hr₀, rfl ⟩ | ⟨ t, ht, rfl ⟩ ) <;> tauto;
    -- Since R is not in the map of wrap_sym, the only way for R to appear in the list is if it is the last element of v.
    have hv_last : R ∈ v := by
      replace hbef := congr_arg List.reverse hbef ; simp_all +decide [ List.reverse_append ] ;
      replace hbef := congr_arg List.head? hbef ; simp_all +decide;
      cases v <;> simp_all +decide [ List.getLast? ];
      cases h : ( wrap_gr r₀ ).input_R <;> simp_all +decide [ List.getLast ];
      · cases h : ( wrap_gr r₀ ).input_N <;> simp_all +decide [ R ];
        tauto;
      · unfold wrap_gr at h; simp_all +decide [ List.getLast ] ;
        grind +suggestions;
    -- Since R is the last element of v, we can write v as v' ++ [R] for some v'.
    obtain ⟨v', hv'⟩ : ∃ v', v = v' ++ [R] := by
      have hv_last : R ∈ v → ∃ v', v = v' ++ [R] := by
        intro hv_last
        induction' v with v ih;
        · contradiction;
        · induction' ih using List.reverseRecOn with ih ih ih <;> simp_all +decide [ List.map ];
          replace hbef := congr_arg List.reverse hbef ; simp_all +decide [ List.reverse_append ];
          cases ih <;> simp_all +decide [ wrap_gr ];
          · cases hbef.1;
          · exact ⟨ v :: ih, by simp +decide ⟩;
      exact hv_last ‹_›;
    simp_all +decide [ wrap_gr ];
    -- Since `wrap_sym` is injective, we can find preimages `u₀` and `v₀` for `u` and `v'` respectively.
    obtain ⟨u₀, hu₀⟩ : ∃ u₀ : List (symbol T g.nt), List.map wrap_sym u₀ = u := by
      have hu₀ : u = List.take (u.length) (List.map wrap_sym σ ++ [R]) := by
        rw [ hbef, List.take_append_of_le_length ] <;> norm_num;
      rw [ hu₀, List.take_append_of_le_length ] <;> norm_num;
      · exact ⟨ List.take u.length σ, by rw [ List.map_take ] ⟩;
      · grind
    obtain ⟨v₀, hv₀⟩ : ∃ v₀ : List (symbol T g.nt), List.map wrap_sym v₀ = v' := by
      have hv₀ : ∀ x ∈ v', ∃ y : symbol T g.nt, wrap_sym y = x := by
        intro x hx
        have hx_in_map : x ∈ List.map wrap_sym (σ ++ r₀.input_L ++ [symbol.nonterminal r₀.input_N] ++ r₀.input_R) := by
          replace hbef := congr_arg List.reverse hbef; simp_all +decide [ List.reverse_append ] ;
        exact List.mem_map.mp hx_in_map |> Exists.imp fun y hy => hy.2;
      have hv₀ : ∀ {l : List (ns T g.nt)}, (∀ x ∈ l, ∃ y : symbol T g.nt, wrap_sym y = x) → ∃ v₀ : List (symbol T g.nt), List.map wrap_sym v₀ = l := by
        intros l hl; induction' l with x l ih <;> simp_all +decide [ List.map ] ;
        rcases hl.1 with ⟨ y, rfl ⟩ ; obtain ⟨ v₀, hv₀ ⟩ := ih; exact ⟨ y :: v₀, by simp +decide [ hv₀ ] ⟩ ;
      exact hv₀ ‹_›;
    refine Or.inr <| Or.inr <| Or.inr <| Or.inr <| ⟨ u₀ ++ r₀.output_string ++ v₀, ?_ ⟩ ; aesop

-- Need: ∃ σ', u ++ map wrap_sym r₀.output ++ v' ++ [R] = map wrap_sym σ' ++ [R]

/-
Case 6: β = ω ++ [H], Z ∉ β, R ∉ β
-/
private lemma star_case_6 {g : grammar T} {α' : List (ns T g.nt)}
    {ω : List (ns T g.nt)}
    (h_noZ : Z ∉ ω ++ [H])
    (h_noR : R ∉ ω ++ [H])
    (h_step : grammar_transforms (star_grammar g) (ω ++ [H]) α') :
  StarInvariant g α' := by
  rcases h_step with ⟨ r, h₁, h₂, h₃, _, rfl, h₅ ⟩;
  rcases r with ⟨ L, N, R, S ⟩;
  rcases N with ( N | N ) <;> simp_all +decide [ star_grammar ];
  · rcases h₁ with ( ⟨ r, hr, hr' ⟩ | hr );
    · unfold wrap_gr at hr'; simp_all +decide [ List.map_append ] ;
      refine Or.inr <| Or.inr <| Or.inr <| Or.inr <| Or.inr ⟨ ?_, ?_, ?_ ⟩;
      · use h₂ ++ S ++ h₃.dropLast;
        simp_all +decide [ List.append_assoc ];
        rw [ List.dropLast_append_getLast? ];
        replace := congr_arg List.reverse ‹_›; norm_num [ List.reverse_append ] at this;
        replace this := congr_arg List.head? this; simp_all +decide ;
        cases h₃ <;> simp_all +decide [ List.getLast? ];
        cases R <;> simp_all +decide [ List.getLast ];
        · cases this;
        · grind +suggestions;
      · simp_all +decide [ ← hr'.2.2.2 ];
        exact?;
      · grind +suggestions;
    · unfold rules_that_scan_terminals at hr; aesop;
  · fin_cases N <;> simp_all +decide [ star_grammar ];
    · tauto;
    · rcases h₁ with ( ⟨ r, hr, hr' ⟩ | hr ) <;> simp_all +decide [ wrap_gr, rules_that_scan_terminals ];
    · unfold _root_.R at * ; tauto

/-- The StarInvariant is preserved under one grammar_transforms step. -/
private lemma star_invariant_step {g : grammar T} {β α' : List (ns T g.nt)}
    (h_inv : StarInvariant g β)
    (h_step : grammar_transforms (star_grammar g) β α') :
  StarInvariant g α' := by
  unfold StarInvariant at h_inv
  rcases h_inv with ⟨x, hx, rfl⟩ | ⟨x, hx, rfl⟩ |
    ⟨w, β, γ, x, hw, hβγ, hx, rfl⟩ | ⟨u, hu, rfl⟩ | ⟨σ, rfl⟩ | ⟨⟨ω, rfl⟩, hnoZ, hnoR⟩
  · exact star_case_1 hx h_step
  · exact star_case_2 hx h_step
  · exact star_case_3 hw hβγ hx h_step
  · exact absurd h_step no_transform_all_terminals
  · exact star_case_5 h_step
  · exact star_case_6 hnoZ hnoR h_step

private lemma star_induction {g : grammar T} {α : List (ns T g.nt)}
    (ass : grammar_derives (star_grammar g) [Z] α) :
  StarInvariant g α := by
  induction ass with
  | refl =>
    -- Base case: α = [Z], which is case 1 with x = []
    left
    exact ⟨[], by simp, by simp⟩
  | tail _ step ih =>
    exact star_invariant_step ih step

-- The hard direction: grammar_language (star_grammar g) ⊆ L*
private lemma in_star_of_in_star_grammar {g : grammar T} {w : List T}
    (hw : grammar_generates (star_grammar g) w) :
  w ∈ KStar.kstar (grammar_language g) := by
  have result := star_induction hw
  unfold StarInvariant at result
  rcases result with ⟨x, _, hα⟩ | ⟨x, _, hα⟩ |
    ⟨w', β, γ, x, _, _, _, hα⟩ | ⟨u, hu, hα⟩ | ⟨σ, hα⟩ | ⟨⟨ω, hα⟩, _, _⟩
  · exfalso
    have : (Z : ns T g.nt) ∈ List.map symbol.terminal w := by rw [hα]; simp
    exact nonterminal_not_in_map_terminal this
  · exfalso
    have : (R : ns T g.nt) ∈ List.map symbol.terminal w := by rw [hα]; simp
    exact nonterminal_not_in_map_terminal this
  · exfalso
    have : (R : ns T g.nt) ∈ List.map symbol.terminal w := by
      rw [hα]; simp [List.mem_append]
    exact nonterminal_not_in_map_terminal this
  · have w_eq : w = u := by
      have hinj : Function.Injective (symbol.terminal (T := T) (N := (star_grammar g).nt)) := by
        intro a b h; cases h; rfl
      exact (List.map_injective_iff.mpr hinj) hα
    rw [w_eq]; exact hu
  · exfalso
    have : (R : ns T g.nt) ∈ List.map symbol.terminal w := by
      rw [hα]; simp [List.mem_append]
    exact nonterminal_not_in_map_terminal this
  · exfalso
    have : (H : ns T g.nt) ∈ List.map symbol.terminal w := by
      rw [hα]; simp [List.mem_append]
    exact nonterminal_not_in_map_terminal this

end hard_direction

/-- The class of recursively enumerable languages is closed under the Kleene star. -/
theorem RE_of_star_RE (L : Language T) :
    is_RE L → is_RE (KStar.kstar L) := by
  rintro ⟨g, hg⟩
  use star_grammar g
  ext w
  constructor
  · intro hw
    rw [← hg]
    exact in_star_of_in_star_grammar hw
  · intro hw
    rw [← hg] at hw
    exact in_star_grammar_of_in_star hw

/-- The class of recursively enumerable languages is closed under Kleene star. -/
theorem RE_closedUnderKleeneStar : ClosedUnderKleeneStar (α := T) is_RE :=
  fun L hL => RE_of_star_RE L hL
