import Langlib.Classes.ContextFree.Closure.Substitution
import Langlib.Classes.ContextFree.Definition
import Langlib.Utilities.ClosurePredicates

/-! # Context-Free Closure Under Concatenation

This file derives closure under concatenation from closure under substitution.

## Main declarations

- `CF_of_CF_c_CF`
-/

variable {T : Type}

/-- The class of context-free languages is closed under concatenation. -/
theorem CF_of_CF_c_CF (L₁ : Language T) (L₂ : Language T) :
    is_CF L₁ ∧ is_CF L₂ → is_CF (L₁ * L₂) := by
  classical
  rintro ⟨h₁, h₂⟩
  let f : Bool → Language T := fun b => if b then L₂ else L₁
  have hsubst : is_CF (({[false, true]} : Language Bool).subst f) := by
    apply CF_of_subst_CF ({[false, true]} : Language Bool) f
    · exact (is_CF_iff_isContextFree).mpr (isContextFree_singleton [false, true])
    · intro b
      cases b with
      | false => simpa [f]
      | true => simpa [f]
  simpa [f] using (Language.subst_pair_eq_mul (f := f) ▸ hsubst)

/-- The class of context-free languages is closed under concatenation. -/
theorem CF_closedUnderConcatenation : ClosedUnderConcatenation (α := T) is_CF :=
  fun L₁ L₂ h₁ h₂ => CF_of_CF_c_CF L₁ L₂ ⟨h₁, h₂⟩
