/-
Copyright (c) 2025 Harmonic. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/
import Mathlib
import Langlib.Grammars.LeftRegular.Definition
import Langlib.Grammars.RightRegular.Definition
import Langlib.Grammars.RightRegular.UnrestrictedCharacterization
import Langlib.Automata.FiniteState.Equivalence.RegularDFAEquiv

/-! # Equivalence of Left-Regular and Right-Regular Grammars

This file proves that the class of languages generated by left-regular grammars
coincides with the class of languages generated by right-regular grammars, i.e.,
both define exactly the regular languages.

## Proof strategy

The key observation is that reversing the output of each rule converts a left-regular
grammar into a right-regular grammar (and vice versa), and a derivation step in one
corresponds to a derivation step on the reversed sentential form in the other.

Concretely, if `g` is a left-regular grammar, we construct a right-regular grammar
`RG_of_LG g` such that `LG_language g = (RG_language (RG_of_LG g)).reverse`.
Since the class of right-regular languages is closed under reversal (via Mathlib's
`Language.isRegular_reverse_iff` and `is_RG_iff_isRegular`), this gives
`is_LG L → is_RG L`. The converse direction is symmetric.

## Main results

- `is_LG_iff_is_RG` — A language is left-regular iff it is right-regular.
- `LG_eq_RG` — The class of left-regular languages equals the class of right-regular
  languages.

## References

* Hopcroft, Motwani, Ullman. *Introduction to Automata Theory, Languages, and Computation*,
  3rd ed. Section 3.3.
-/

open Relation Classical

noncomputable section

variable {T : Type}

-- ============================================================================
-- Part 1: Conversions between LG and RG rules/grammars
-- ============================================================================

/-- Convert a left-regular rule to a right-regular rule by reversing the output.

- `A → Ba` becomes `A → aB`
- `A → a` stays `A → a`
- `A → ε` stays `A → ε` -/
def RG_rule_of_LG_rule {N : Type} : LG_rule T N → RG_rule T N
  | .cons A a B   => .cons A a B
  | .single A a   => .single A a
  | .epsilon A    => .epsilon A

/-- Convert a right-regular rule to a left-regular rule by reversing the output.

- `A → aB` becomes `A → Ba`
- `A → a` stays `A → a`
- `A → ε` stays `A → ε` -/
def LG_rule_of_RG_rule {N : Type} : RG_rule T N → LG_rule T N
  | .cons A a B   => .cons A a B
  | .single A a   => .single A a
  | .epsilon A    => .epsilon A

/-- Convert a left-regular grammar to a right-regular grammar. -/
def RG_of_LG (g : LG_grammar T) : RG_grammar T where
  nt := g.nt
  initial := g.initial
  rules := g.rules.map RG_rule_of_LG_rule

/-- Convert a right-regular grammar to a left-regular grammar. -/
def LG_of_RG (g : RG_grammar T) : LG_grammar T where
  nt := g.nt
  initial := g.initial
  rules := g.rules.map LG_rule_of_RG_rule

-- ============================================================================
-- Part 2: Correspondence between rule properties
-- ============================================================================

lemma RG_rule_of_LG_rule_lhs {N : Type} (r : LG_rule T N) :
    (RG_rule_of_LG_rule r).lhs = r.lhs := by
  cases r <;> rfl

lemma LG_rule_of_RG_rule_lhs {N : Type} (r : RG_rule T N) :
    (LG_rule_of_RG_rule r).lhs = r.lhs := by
  cases r <;> rfl

lemma RG_rule_of_LG_rule_output {N : Type} (r : LG_rule T N) :
    (RG_rule_of_LG_rule r).output = r.output.reverse := by
  cases r <;> simp [RG_rule_of_LG_rule, RG_rule.output, LG_rule.output]

lemma LG_rule_of_RG_rule_output {N : Type} (r : RG_rule T N) :
    (LG_rule_of_RG_rule r).output = r.output.reverse := by
  cases r <;> simp [LG_rule_of_RG_rule, LG_rule.output, RG_rule.output]

-- ============================================================================
-- Part 3: Step correspondence via list reversal
-- ============================================================================

/-- An LG transformation step corresponds to an RG transformation step on the
reversed sentential form. -/
lemma RG_transforms_reverse_of_LG_transforms {g : LG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : LG_transforms g w₁ w₂) :
    RG_transforms (RG_of_LG g) w₁.reverse w₂.reverse := by
  obtain ⟨r, hr, u, v, hw1, hw2⟩ := h
  refine ⟨RG_rule_of_LG_rule r, List.mem_map.mpr ⟨r, hr, rfl⟩, v.reverse, u.reverse, ?_, ?_⟩
  · rw [hw1, RG_rule_of_LG_rule_lhs]
    simp [List.reverse_append]
  · rw [hw2, RG_rule_of_LG_rule_output]
    simp [List.reverse_append]

/-- An RG transformation step on `RG_of_LG g` corresponds to an LG transformation
step on the reversed sentential form. -/
lemma LG_transforms_of_RG_transforms_reverse {g : LG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : RG_transforms (RG_of_LG g) w₁ w₂) :
    LG_transforms g w₁.reverse w₂.reverse := by
  obtain ⟨r, hr, u, v, hw1, hw2⟩ := h
  simp [RG_of_LG] at hr
  obtain ⟨r', hr', rfl⟩ := hr
  refine ⟨r', hr', v.reverse, u.reverse, ?_, ?_⟩
  · rw [hw1, RG_rule_of_LG_rule_lhs]
    simp [List.reverse_append]
  · rw [hw2, RG_rule_of_LG_rule_output]
    simp [List.reverse_append]

/-- LG derivation corresponds to RG derivation on reversed sentential forms. -/
lemma RG_derives_reverse_of_LG_derives {g : LG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : LG_derives g w₁ w₂) :
    RG_derives (RG_of_LG g) w₁.reverse w₂.reverse := by
  induction h with
  | refl => exact RG_deri_self _
  | tail _ hs ih =>
    exact RG_deri_of_deri_tran ih (RG_transforms_reverse_of_LG_transforms hs)

/-- RG derivation on `RG_of_LG g` corresponds to LG derivation on reversed sentential forms. -/
lemma LG_derives_of_RG_derives_reverse {g : LG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : RG_derives (RG_of_LG g) w₁ w₂) :
    LG_derives g w₁.reverse w₂.reverse := by
  induction h with
  | refl => exact LG_deri_self _
  | tail _ hs ih =>
    exact LG_deri_of_deri_tran ih (LG_transforms_of_RG_transforms_reverse hs)

-- ============================================================================
-- Part 4: Language relationship
-- ============================================================================

lemma LG_generates_iff_RG_generates_reverse (g : LG_grammar T) (w : List T) :
    LG_generates g w ↔ RG_generates (RG_of_LG g) w.reverse := by
  constructor
  · intro h
    unfold LG_generates at h
    unfold RG_generates
    have := RG_derives_reverse_of_LG_derives h
    simp [List.map_reverse] at this ⊢
    convert this using 1
  · intro h
    unfold RG_generates at h
    unfold LG_generates
    have := LG_derives_of_RG_derives_reverse h
    simp [List.map_reverse] at this ⊢
    convert this using 1

lemma LG_language_eq_RG_language_reverse (g : LG_grammar T) :
    LG_language g = (RG_language (RG_of_LG g)).reverse := by
  ext w
  simp only [LG_language, RG_language, Language.reverse]
  exact LG_generates_iff_RG_generates_reverse g w

-- ============================================================================
-- Symmetric results: RG → LG via LG_of_RG
-- ============================================================================

/-- An RG transformation step corresponds to an LG transformation step on the
reversed sentential form. -/
lemma LG_transforms_reverse_of_RG_transforms {g : RG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : RG_transforms g w₁ w₂) :
    LG_transforms (LG_of_RG g) w₁.reverse w₂.reverse := by
  obtain ⟨r, hr, u, v, hw1, hw2⟩ := h
  refine ⟨LG_rule_of_RG_rule r, List.mem_map.mpr ⟨r, hr, rfl⟩, v.reverse, u.reverse, ?_, ?_⟩
  · rw [hw1, LG_rule_of_RG_rule_lhs]
    simp [List.reverse_append]
  · rw [hw2, LG_rule_of_RG_rule_output]
    simp [List.reverse_append]

lemma RG_transforms_of_LG_transforms_reverse {g : RG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : LG_transforms (LG_of_RG g) w₁ w₂) :
    RG_transforms g w₁.reverse w₂.reverse := by
  obtain ⟨r, hr, u, v, hw1, hw2⟩ := h
  simp [LG_of_RG] at hr
  obtain ⟨r', hr', rfl⟩ := hr
  refine ⟨r', hr', v.reverse, u.reverse, ?_, ?_⟩
  · rw [hw1, LG_rule_of_RG_rule_lhs]
    simp [List.reverse_append]
  · rw [hw2, LG_rule_of_RG_rule_output]
    simp [List.reverse_append]

lemma LG_derives_reverse_of_RG_derives {g : RG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : RG_derives g w₁ w₂) :
    LG_derives (LG_of_RG g) w₁.reverse w₂.reverse := by
  induction h with
  | refl => exact LG_deri_self _
  | tail _ hs ih =>
    exact LG_deri_of_deri_tran ih (LG_transforms_reverse_of_RG_transforms hs)

lemma RG_derives_of_LG_derives_reverse {g : RG_grammar T}
    {w₁ w₂ : List (symbol T g.nt)}
    (h : LG_derives (LG_of_RG g) w₁ w₂) :
    RG_derives g w₁.reverse w₂.reverse := by
  induction h with
  | refl => exact RG_deri_self _
  | tail _ hs ih =>
    exact RG_deri_of_deri_tran ih (RG_transforms_of_LG_transforms_reverse hs)

lemma RG_generates_iff_LG_generates_reverse (g : RG_grammar T) (w : List T) :
    RG_generates g w ↔ LG_generates (LG_of_RG g) w.reverse := by
  constructor
  · intro h
    unfold RG_generates at h
    unfold LG_generates
    have := LG_derives_reverse_of_RG_derives h
    simp [List.map_reverse] at this ⊢
    convert this using 1
  · intro h
    unfold LG_generates at h
    unfold RG_generates
    have := RG_derives_of_LG_derives_reverse h
    simp [List.map_reverse] at this ⊢
    convert this using 1

lemma RG_language_eq_LG_language_reverse (g : RG_grammar T) :
    RG_language g = (LG_language (LG_of_RG g)).reverse := by
  ext w
  simp only [RG_language, LG_language, Language.reverse]
  exact RG_generates_iff_LG_generates_reverse g w

-- ============================================================================
-- Part 5: Main equivalence theorems
-- ============================================================================

variable [Fintype T]

/-- Every left-regular language is right-regular. -/
theorem is_RG_of_is_LG {L : Language T} (h : is_LG L) : is_RG L := by
  obtain ⟨g, rfl⟩ := h
  rw [LG_language_eq_RG_language_reverse]
  have hRG : is_RG (RG_language (RG_of_LG g)) :=
    is_RG_via_rg_implies_is_RG ⟨RG_of_LG g, rfl⟩
  have hIR := isRegular_of_is_RG hRG
  have hIR_rev : (RG_language (RG_of_LG g)).reverse.IsRegular :=
    Language.isRegular_reverse_iff.mpr hIR
  exact is_RG_of_isRegular hIR_rev

/-- Every right-regular language is left-regular. -/
theorem is_LG_of_is_RG {L : Language T} (h : is_RG L) : is_LG L := by
  obtain ⟨g, rfl⟩ := is_RG_implies_is_RG_via_rg h
  have hIR := isRegular_of_is_RG (is_RG_via_rg_implies_is_RG ⟨g, rfl⟩)
  have hIR_rev : (RG_language g).reverse.IsRegular :=
    Language.isRegular_reverse_iff.mpr hIR
  obtain ⟨g', hg'⟩ := is_RG_implies_is_RG_via_rg (is_RG_of_isRegular hIR_rev)
  refine ⟨LG_of_RG g', ?_⟩
  rw [RG_language_eq_LG_language_reverse] at hg'
  have : (LG_language (LG_of_RG g')).reverse = (RG_language g).reverse := hg'
  have := congr_arg Language.reverse this
  simp [Language.reverse_reverse] at this
  exact this

/-- A language is left-regular if and only if it is right-regular. -/
theorem is_LG_iff_is_RG {L : Language T} : is_LG L ↔ is_RG L :=
  ⟨is_RG_of_is_LG, is_LG_of_is_RG⟩

/-- The class of left-regular languages equals the class of right-regular languages. -/
theorem LG_eq_RG : (LG : Set (Language T)) = RG := by
  ext L
  exact is_LG_iff_is_RG

end
