module

public import Langlib.Classes.Indexed.Closure.Injection
public import Langlib.Classes.Indexed.Examples.AbnAbStarPowPredN
public import Langlib.Classes.Indexed.Examples.AbnPowM
public import Langlib.Classes.Indexed.Examples.AbnPowN
public import Langlib.Utilities.ClosurePredicates
import Mathlib.Data.Fintype.EquivFin

@[expose]
public section

/-!
# Indexed languages are not closed under intersection

Over `Bool`, the indexed languages

* `abnPowM = {(a b^n)^m | n,m > 0}` and
* `abnAbStarPowPredN = {a b^n (a b*)^(n-1) | n > 0}`

intersect in the non-indexed diagonal language
`abnPowN = {(a b^n)^n | n > 0}`.  Injective terminal relabelling
transports this counterexample to every alphabet containing two distinct
symbols.
-/

namespace IndexedIntersectionNonclosure

private lemma map_inf_of_injective {α β : Type} {f : α → β}
    (hf : Function.Injective f) (L₁ L₂ : Language α) :
    Language.map f (L₁ ⊓ L₂) = Language.map f L₁ ⊓ Language.map f L₂ := by
  ext w
  constructor
  · rintro ⟨v, ⟨hv₁, hv₂⟩, rfl⟩
    exact ⟨⟨v, hv₁, rfl⟩, ⟨v, hv₂, rfl⟩⟩
  · rintro ⟨⟨v₁, hv₁, hmap₁⟩, ⟨v₂, hv₂, hmap₂⟩⟩
    have hv : v₁ = v₂ := by
      apply List.map_injective_iff.mpr hf
      rw [hmap₁, hmap₂]
    subst v₂
    exact ⟨v₁, ⟨hv₁, hv₂⟩, hmap₁⟩

/-- Indexed languages over the binary alphabet are not closed under
intersection. -/
public theorem Indexed_notClosedUnderIntersection :
    ¬ ClosedUnderIntersection (α := Bool) is_Indexed := by
  intro hclosed
  apply abnPowN_not_is_Indexed
  rw [← abnPowM_inter_abnAbStarPowPredN]
  exact hclosed abnPowM abnAbStarPowPredN
    abnPowM_is_Indexed abnAbStarPowPredN_is_Indexed

/-- An injection of the binary alphabet transports failure of indexed
intersection closure to the target alphabet. -/
public theorem Indexed_notClosedUnderIntersection_of_embedding {α : Type}
    (e : Bool ↪ α) :
    ¬ ClosedUnderIntersection (α := α) is_Indexed := by
  intro hclosed
  apply Indexed_notClosedUnderIntersection
  intro L₁ L₂ hL₁ hL₂
  have hmap₁ : is_Indexed (Language.map e L₁) :=
    Indexed_of_map_injective_Indexed e.injective L₁ hL₁
  have hmap₂ : is_Indexed (Language.map e L₂) :=
    Indexed_of_map_injective_Indexed e.injective L₂ hL₂
  have hinter : is_Indexed (Language.map e L₁ ⊓ Language.map e L₂) :=
    hclosed (Language.map e L₁) (Language.map e L₂) hmap₁ hmap₂
  rw [← map_inf_of_injective e.injective] at hinter
  exact Indexed_of_map_injective_Indexed_rev e.injective (L₁ ⊓ L₂) hinter

/-- Indexed languages are not closed under intersection over any alphabet
containing two specified distinct symbols. -/
public theorem Indexed_notClosedUnderIntersection_of_two {α : Type}
    (a b : α) (hab : a ≠ b) :
    ¬ ClosedUnderIntersection (α := α) is_Indexed := by
  let e : Bool ↪ α :=
    { toFun := fun bit => if bit then b else a
      inj' := by
        intro x y hxy
        cases x <;> cases y <;> simp_all }
  exact Indexed_notClosedUnderIntersection_of_embedding e

/-- Indexed languages are not closed under intersection over any finite
alphabet with at least two symbols. -/
public theorem Indexed_notClosedUnderIntersection_of_card {α : Type}
    [Fintype α] (hα : 2 ≤ Fintype.card α) :
    ¬ ClosedUnderIntersection (α := α) is_Indexed := by
  have hcard : Fintype.card Bool ≤ Fintype.card α := by
    simpa using hα
  obtain ⟨e : Bool ↪ α⟩ :=
    Function.Embedding.nonempty_of_card_le hcard
  exact Indexed_notClosedUnderIntersection_of_embedding e

end IndexedIntersectionNonclosure
