import Mathlib
import Langlib.Classes.ContextFree.Definition
import Langlib.Grammars.ContextFree.UnrestrictedCharacterization
import Langlib.Grammars.ContextFree.Toolbox
import Langlib.Classes.Regular.Basics.NonRegular
import Langlib.Utilities.Tactics

/-! # `a^n b^n` as a CFL

This file constructs a context-free grammar for `{a^n b^n}` and proves that the
language is context-free.
-/

open Language List

/-- Context-free grammar for the language `{aⁿbⁿ | n ∈ ℕ}` over `Bool`. -/
def cfg_anbn : CF_grammar Bool where
  nt := Unit
  initial := ()
  rules := [
    ((), [symbol.terminal false, symbol.nonterminal (), symbol.terminal true]),
    ((), [])
  ]

private def sentential_form (w : List (symbol Bool Unit)) : Prop :=
  (∃ n : ℕ, w = List.map symbol.terminal (List.replicate n false) ++
    [symbol.nonterminal ()] ++
    List.map symbol.terminal (List.replicate n true)) ∨
  (∃ n : ℕ, w = List.map symbol.terminal (List.replicate n false ++ List.replicate n true))

private lemma sentential_form_step (w w' : List (symbol Bool Unit))
    (hw : sentential_form w) (ht : CF_transforms cfg_anbn w w') :
    sentential_form w' := by
  rcases hw with ⟨n, rfl⟩ | ⟨n, rfl⟩ <;> simp_all +decide [CF_transforms, cfg_anbn]
  · rcases ht with (⟨x, y, hxy, rfl⟩ | ⟨x, y, hxy, rfl⟩) <;> simp_all +decide [sentential_form]
    · rw [List.append_eq_append_iff] at hxy
      rcases hxy with (⟨as, rfl, h⟩ | ⟨bs, h, h'⟩) <;> simp_all +decide [List.append_eq_append_iff]
      · rcases as with _ | ⟨a, as⟩ <;> simp_all +decide [List.replicate]
        · refine Or.inl ⟨n + 1, Or.inl ⟨[symbol.terminal false], ?_, ?_⟩⟩
          · rw [List.replicate_succ']
          · simpa [h, List.replicate_succ]
        · replace h := congr_arg List.toFinset h.2
          rw [Finset.ext_iff] at h
          specialize h (symbol.nonterminal ())
          aesop
      · rcases bs with _ | ⟨b, bs⟩ <;> simp_all +decide [List.append_eq_append_iff]
        · refine Or.inl ⟨n + 1, ?_⟩
          simp_all +decide [List.replicate]
          exact Or.inl ⟨[symbol.terminal false], by rw [← h]; exact Nat.recOn n (by simp +decide) fun n ihn => by simp +decide [List.replicate] at ihn ⊢; aesop⟩
        · no_nonterminal (symbol.nonterminal ()) at h
    · rw [List.append_eq_append_iff] at hxy
      rcases hxy with (⟨as, rfl, h⟩ | ⟨bs, h, h'⟩) <;> simp_all +decide [List.append_eq_append_iff]
      · rcases as with _ | ⟨a, as⟩ <;> simp_all +decide [List.replicate]
        · exact Or.inr ⟨n, Or.inl ⟨[], by aesop⟩⟩
        · replace h := congr_arg List.toFinset h.2
          rw [Finset.ext_iff] at h
          specialize h (symbol.nonterminal ())
          aesop
      · rcases bs with _ | ⟨a, bs⟩ <;> simp_all +decide [List.append_assoc]
        · exact Or.inr ⟨n, Or.inl ⟨[], by aesop⟩⟩
        · no_nonterminal (a) at h
  · rcases ht with (⟨x, y, hxy, rfl⟩ | ⟨x, y, hxy, rfl⟩)
    · no_nonterminal
    · no_nonterminal

private lemma sentential_form_of_derives (w : List (symbol Bool Unit))
    (hd : CF_derives cfg_anbn [symbol.nonterminal ()] w) :
    sentential_form w := by
  induction hd with
  | refl => exact Or.inl ⟨0, by simp⟩
  | tail _ ht ih => exact sentential_form_step _ _ ih ht

private lemma CF_language_cfg_anbn_sub_anbn :
    ∀ w, w ∈ CF_language cfg_anbn → w ∈ anbn := by
  intro w hw
  have hsf : sentential_form (List.map symbol.terminal w) :=
    sentential_form_of_derives _ hw
  rcases hsf with ⟨n, hn⟩ | ⟨n, hn⟩
  · exfalso
    have hmem : symbol.nonterminal () ∈ List.map symbol.terminal w := by
      rw [hn]
      simp
    simp at hmem
  ·
    have hinj : Function.Injective (symbol.terminal (T := Bool) (N := Unit)) := by
      intro a b h
      cases h
      rfl
    exact ⟨n, hinj.list_map hn⟩

private lemma anbn_sub_CF_language_cfg_anbn :
    ∀ w, w ∈ anbn → w ∈ CF_language cfg_anbn := by
  intro w hw
  obtain ⟨n, rfl⟩ := hw
  have h_deriv : ∀ n : ℕ, CF_derives cfg_anbn [symbol.nonterminal ()]
      (List.replicate n (symbol.terminal false) ++ [symbol.nonterminal ()] ++ List.replicate n (symbol.terminal true)) := by
    intro n
    induction' n with n ih
    · constructor
    ·
      have h_step : CF_derives cfg_anbn
          (List.replicate n (symbol.terminal false) ++ [symbol.nonterminal ()] ++ List.replicate n (symbol.terminal true))
          (List.replicate n (symbol.terminal false) ++ [symbol.terminal false, symbol.nonterminal (), symbol.terminal true] ++ List.replicate n (symbol.terminal true)) := by
        apply_rules [CF_deri_of_tran, CF_deri_with_prefix_and_postfix]
        use ((), [symbol.terminal false, symbol.nonterminal (), symbol.terminal true])
        exact ⟨replicate n (symbol.terminal false), replicate n (symbol.terminal true), by simp +decide [cfg_anbn], by simp +decide, by simp +decide⟩
      convert ih.trans h_step using 1
      simp +decide [List.replicate_add]
      exact Nat.recOn n rfl fun n ih => by rw [replicate_succ']; aesop
  have h_final : CF_derives cfg_anbn
      (List.replicate n (symbol.terminal false) ++ [symbol.nonterminal ()] ++ List.replicate n (symbol.terminal true))
      (List.replicate n (symbol.terminal false) ++ List.replicate n (symbol.terminal true)) := by
    apply_rules [CF_deri_of_tran, CF_deri_with_prefix_and_postfix]
    use ((), [])
    exact ⟨replicate n (symbol.terminal false), replicate n (symbol.terminal true), by simp +decide [cfg_anbn], by simp +decide, by simp +decide⟩
  apply_rules [CF_deri_of_deri_deri, h_deriv]
  any_goals tauto
  convert CF_deri_self using 1
  aesop

/-- The grammar `cfg_anbn` generates exactly `{aⁿbⁿ}`. -/
theorem CF_language_cfg_anbn : CF_language cfg_anbn = anbn := by
  ext w
  exact ⟨CF_language_cfg_anbn_sub_anbn w, anbn_sub_CF_language_cfg_anbn w⟩

/-- The language `{aⁿbⁿ}` is context-free. -/
theorem anbn_is_CF : is_CF anbn :=
  is_CF_via_cfg_implies_is_CF ⟨cfg_anbn, CF_language_cfg_anbn⟩
