Theory Expansion

(*<*)
theory Expansion
  imports Context_Free_Grammar.Context_Free_Grammar
begin
(*>*)

section ‹Expansion of Grammars›

lemma conc_eq_empty_iff: "X @@ Y = {}  X = {}  Y = {}"
  by auto

lemma Nts_syms_conc: "(Nts_syms ` (X @@ Y)) =
 (if X = {}  Y = {} then {} else (Nts_syms ` X)  (Nts_syms ` Y))"
  by (force elim!: concE)

text ‹We consider the set of admissible expansions of grammars.

For a symbol,
one option is not to expand it,
and the other option for (expandable) nonterminals is to expand to the all rhss.›

definition Expand_sym_ops :: "('n,'t) Prods  'n set  ('n,'t) sym  ('n,'t) syms set set" where
"Expand_sym_ops P X x =
  insert {[x]} (case x of Nt A  if A  X then {Rhss P A} else {} | _  {})"

lemma Expand_sym_ops_simps:
  "Expand_sym_ops P X (Tm a) = {{[Tm a]}}"
  "Expand_sym_ops P X (Nt A) = insert {[Nt A]} (if A  X then {Rhss P A} else {})"
  by (auto simp: Expand_sym_ops_def)

lemma Expand_sym_ops_self: "{[x]}  Expand_sym_ops P X x"
  by (simp add: Expand_sym_ops_def)

text ‹Mixed words allow all possible combinations of the options for the symbols.›

fun Expand_syms_ops :: "('n,'t) Prods  'n set  ('n,'t) syms  ('n,'t) syms set set" where
  "Expand_syms_ops P X [] = {{[]}}"
| "Expand_syms_ops P X (x#xs) =
   {αs @@ βs | αs βs. αs  Expand_sym_ops P X x  βs  Expand_syms_ops P X xs}"

lemma Expand_syms_ops_self: "{α}  Expand_syms_ops P X α"
proof (induction α)
  case Nil
  show ?case by simp
next
  case (Cons x α)
  have "{x # α} = {[x]} @@ {α}" by (auto simp: insert_conc)
  with Cons show ?case by (auto intro!: Expand_sym_ops_self)
qed

lemma Expand_sym_ops_Lang_of_Cons:
  assumes U: "U  Expand_sym_ops P X x" and X: "Lhss Q  X = {}"
  shows "Lang_of (P  Q) (x#xs) = Lang_of_set (P  Q) U @@ Lang_of (P  Q) xs"
proof-
  { fix A assume "A  X"
    with X have "A  Lhss Q" by auto
    then have "Rhss (P  Q) A = Rhss P A" by (auto simp: Rhss_Un notin_Lhss_iff_Rhss)
    with Lang_of_set_Rhss[of "P  Q" A]
    have "Lang_of_set (P  Q) (Rhss P A) = Lang (P  Q) A" by auto
  } note * = this
  from assms
  show ?thesis
    by (auto simp: Expand_sym_ops_simps Lang_of_Cons * split: if_splits sym.splits)
qed

lemma Expand_syms_ops_Lang_of:
  assumes U: "U  Expand_syms_ops P L α" and L: "Lhss Q  L = {}"
  shows "Lang_of (P  Q) α = Lang_of_set (P  Q) U"
proof (insert U, induction α arbitrary: U)
  case Nil
  then show ?case by simp
next
  case (Cons x α)
  from Cons.prems obtain V W
    where U: "U = V @@ W"
      and V: "V  Expand_sym_ops P L x"
      and W: "W  Expand_syms_ops P L α"
    by auto
  show ?case by (simp add: U Cons.IH[OF W] Expand_sym_ops_Lang_of_Cons[OF V L] Lang_of_set_conc)
qed

definition Expand :: "(('n,'t) syms  ('n,'t) syms set)  ('n,'t) Prods  ('n,'t) Prods" where
"Expand f P = {(A,α') |A α'. α. (A,α)  P  α'  f α}"

lemma Expand_eq_UN[code]:
  "Expand f P = ((A,α)  P. Pair A ` f α)"
  by (auto simp: Expand_def)

lemma Expand_via_Rhss:
  "Expand f P = {(A,α). α  (f ` Rhss P A)}"
  by (auto simp: Expand_def Rhss_def)

lemma Lhss_Expand: "Lhss (Expand f P)  Lhss P"
  by (auto simp: Lhss_def Expand_def split: prod.splits)

lemma Rhss_Expand: "Rhss (Expand f P) A = (f ` Rhss P A)"
  by (auto simp: Rhss_def Expand_def)

lemma Rhs_Nts_Expand: "Rhs_Nts (Expand f P) = ((A,α)  P. β  f α. Nts_syms β)"
  by (auto simp: Expand_def Rhs_Nts_def)

text ‹When each production is expanded in an admissible way,
then the language is preserved.›

definition Expand_ops :: "('n,'t) Prods  'n set  (('n,'t) syms  ('n,'t) syms set) set" where
"Expand_ops P X = {f. α. f α  Expand_syms_ops P X α}"

theorem Lang_Un_Expand:
  assumes f: "f  Expand_ops P X" and X: "X  Lhss Q = {}"
  shows "Lang (P  Expand f Q) = Lang (P  Q)"
  unfolding Lang_eq_iff_Lang_of_eq
proof (safe intro!: ext elim!: Lang_ofE_deriven)
  show "P  Expand f Q  xs ⇒(n) map Tm w  w  Lang_of (P  Q) xs" for xs w n
  proof (induction n arbitrary: xs w rule: less_induct)
    case (less n)
    show ?case
    proof (cases "A. Nt A  set xs")
      case False
      with less.prems have "xs = map Tm w"
        by (metis (no_types, lifting) deriven_from_TmsD destTm.cases ex_map_conv)
      then show ?thesis by (auto simp: Lang_of_map_Tm)
    next
      case True
      then obtain ys zs A where xs: "xs = ys @ Nt A # zs" by (metis split_list) 
      from less.prems[unfolded this deriven_Nt_map_Tm]
      obtain δ m l k v u t where : "(A,δ)  P  Expand f Q"
        and Lys: "P  Expand f Q  ys ⇒(m) map Tm v"
        and : "P  Expand f Q  δ ⇒(l) map Tm u"
        and Lzs: "P  Expand f Q  zs ⇒(k) map Tm t"
        and n: "n = Suc (m + l + k)"
        and w: "w = v @ u @ t" by force
      from n have mn: "m < n" and ln: "l < n" and kn: "k < n" by auto
      with less.IH  Lys Lzs
      have u: "u  Lang_of (P  Q) δ"
        and v: "v  Lang_of (P  Q) ys"
        and t: "t  Lang_of (P  Q) zs" by auto
      show ?thesis
      proof (cases "(A,δ)  P")
        case True
        have "Lang_of (P  Q) δ  Lang (P  Q) A"
          apply (rule Lang_of_prod_subset)
          using True by simp
        with u v t
        show ?thesis by (auto simp: xs w Lang_of_append Lang_of_Nt_Cons)
      next
        case False
        with  obtain α where AQ: "(A,α)  Q" and δ: "δ  f α" by (auto simp: Expand_def)
        from [unfolded δ] less.IH[of l] n
        have "u  Lang_of (P  Q) δ" by auto
        with δ have "u  Lang_of_set (P  Q) (f α)"
          by (auto simp: Lang_of_def)
        also have " = Lang_of (P  Q) α"
          apply (subst Expand_syms_ops_Lang_of)
          using f AQ X by (auto simp: Expand_ops_def)
        also have "  Lang (P  Q) A"
          apply (rule Lang_of_prod_subset)
          using AQ by auto
        finally have u: "u  Lang (P  Q) A" by auto
        with v t
        show ?thesis by (simp add: xs w Lang_of_append Lang_of_Nt_Cons)
      qed
    qed
  qed
next
  show "P  Q  xs ⇒(n) map Tm w  w  Lang_of (P  Expand f Q) xs" for xs w n
  proof (induction n arbitrary: xs w rule: less_induct)
    case (less n)
    show ?case
    proof (cases "A. Nt A  set xs")
      case False
      with less.prems have "xs = map Tm w"
        by (metis (no_types, lifting) deriven_from_TmsD destTm.cases ex_map_conv)
      then show ?thesis by (auto simp: Lang_of_map_Tm)
    next
      case True
      then obtain ys zs A where xs: "xs = ys @ Nt A # zs" by (metis split_list) 
      from less.prems[unfolded this deriven_Nt_map_Tm]
      obtain α m l k v u t where : "(A,α)  P  Q"
        and Rys: "P  Q  ys ⇒(m) map Tm v"
        and : "P  Q  α ⇒(l) map Tm u"
        and Rzs: "P  Q  zs ⇒(k) map Tm t"
        and n: "n = Suc (m + l + k)"
        and w: "w = v @ u @ t" by force
      from n have mn: "m < n" and ln: "l < n" and kn: "k < n" by auto
      with  Rys Rzs
      have u: "u  Lang_of (P  Expand f Q) α"
        and v: "v  Lang_of (P  Expand f Q) ys"
        and t: "t  Lang_of (P  Expand f Q) zs" by (auto simp: less.IH)
      show ?thesis
      proof (cases "(A,α)  P")
        case True
        then have "(A,α)  P  Expand f Q" by simp
        from Lang_of_prod_subset[OF this] u
        have "u  Lang (P  Expand f Q) A" by auto
        with v w t
        show ?thesis by (auto simp: xs w Lang_of_append Lang_of_Nt_Cons)
      next
        case False
        with  have AαQ: "(A,α)  Q" by simp
        with f have : "f α  Expand_syms_ops P X α" by (auto simp: Expand_ops_def)
        from X Lhss_Expand[of f Q]
        have Lhss: "Lhss (Expand f Q)  X = {}" by auto
        from X AαQ have Rhss: "f α  Rhss (Expand f Q) A"
          by (auto simp: Rhss_def Expand_def)
        from u Expand_syms_ops_Lang_of[OF  Lhss]
        have "u  Lang_of_set (P  Expand f Q) (f α)" by auto
        also have "  Lang (P  Expand f Q) A" using Rhss
          by (auto simp flip: Lang_of_set_Rhss simp: Rhss_Un)
        finally have "u  ".
        with v t show ?thesis by (simp add: xs w Lang_of_append Lang_of_Nt_Cons)
      qed
    qed
  qed
qed

corollary Lang_Expand_Un:
  assumes "f  Expand_ops P X" and "X  Lhss Q = {}"
  shows "Lang (Expand f Q  P) = Lang (Q  P)"
  using Lang_Un_Expand[OF assms] by (simp add: ac_simps)

subsection ‹Instances›

text ‹For symbols, we just provide a function to expand it.›

definition Expand_sym :: "('n,'t) Prods  'n set  ('n,'t) sym  ('n,'t) syms set" where
"Expand_sym P X x = (case x of Nt A  if A  X then Rhss P A else {[x]} | _  {[x]})"

lemma Expand_sym_ops: "Expand_sym P L x  Expand_sym_ops P L x"
  by (auto simp: Expand_sym_def Expand_sym_ops_simps split: sym.splits)

subsubsection ‹Expanding all nonterminals›

fun Expand_all_syms :: "('n,'t) Prods  'n set  ('n,'t) syms  ('n,'t) syms set" where
  "Expand_all_syms P X [] = {[]}"
| "Expand_all_syms P X (x#xs) = Expand_sym P X x @@ Expand_all_syms P X xs"

lemma Expand_all_syms_eq_foldr:
  "Expand_all_syms P X α = foldr (@@) (map (Expand_sym P X) α) {[]}"
  by (induction α, simp_all)

lemma Expand_all_syms_append:
  "Expand_all_syms P X (α@β) = Expand_all_syms P X α @@ Expand_all_syms P X β"
  by (induction α arbitrary: β, simp_all add: conc_assoc)

lemma Expand_all_syms_ops: "Expand_all_syms P X α  Expand_syms_ops P X α"
  by (induction α, simp, force simp: Expand_sym_ops)

lemma Expand_all_ops: "Expand_all_syms P X  Expand_ops P X"
  by (auto simp: Expand_ops_def Expand_all_syms_ops)

abbreviation Expand_all :: "('n,'t) Prods  'n set  ('n,'t) Prods  ('n,'t) Prods" where
  "Expand_all P X Q  Expand (Expand_all_syms P X) Q"

corollary Lang_Un_Expand_all:
  assumes "X  Lhss Q = {}"
  shows "Lang (P  Expand_all P X Q) = Lang (P  Q)"
  using Lang_Un_Expand[OF Expand_all_ops assms].

corollary Lang_Expand_all_Un:
  assumes "X  Lhss Q = {}"
  shows "Lang (Expand_all P X Q  P) = Lang (Q  P)"
  using Lang_Expand_Un[OF Expand_all_ops assms].

text @{term Expand_all} removes expanded nonterminals and adds those which the
expanded nonterminals depends.›

lemma Expand_all_syms_eq_empty: "Expand_all_syms P X α = {}  ¬ Nts_syms α  X  Lhss P"
  by (induction α)
    (auto simp: Expand_sym_def conc_eq_empty_iff notin_Lhss_iff_Rhss[symmetric] split: sym.splits)

lemma Nts_syms_Expand_all:
  "(Nts_syms ` Expand_all_syms P X α) =
   (if Nts_syms α  X  Lhss P
    then Nts_syms α - X  (Nts_syms ` (Rhss P ` (Nts_syms α  X)))
    else {})"
proof (induction α)
  case Nil
  show ?case by simp
next
  case (Cons x α)
  then show ?case
    by (auto simp: Expand_sym_def Nts_syms_conc Cons Expand_all_syms_eq_empty
        notin_Lhss_iff_Rhss[symmetric] split: sym.splits)
qed

lemma Rhs_Nts_Expand_all:
  "Rhs_Nts (Expand_all P X Q) =
   ((A,α)  Q. if Nts_syms α  X  Lhss P
    then Nts_syms α - X  (Nts_syms ` (Rhss P ` (Nts_syms α  X)))
    else {})"
  apply (unfold Rhs_Nts_Expand)
  apply (rule SUP_cong[OF refl])
  apply (rule prod.case_cong[OF refl])
  by (rule Nts_syms_Expand_all)

lemma Rhs_Nts_Expand_all_le:
  "Rhs_Nts (Expand_all P X Q)  Rhs_Nts Q - X  (Nts_syms ` (Rhss P ` (Rhs_Nts Q  X)))"
  (is "?l  ?r")
proof-
  have "?l  ((A,α)  Q. Nts_syms α - X  (Nts_syms ` (Rhss P ` (Nts_syms α  X))))"
    apply (unfold Rhs_Nts_Expand_all)
    apply (rule SUP_mono')
    by (auto simp: if_splits)
  also have " = ?r" by (auto simp: Rhs_Nts_def)
  finally show ?thesis.
qed

text ‹Approximately, Expand_all› depends on nonterminals that are not expanded and
those that the expanding grammar depends.›

lemma Rhs_Nts_Expand_all_le_Rhs_Nts:
  "Rhs_Nts (Expand_all P X Q)  Rhs_Nts Q - X  Rhs_Nts P"
  (is "?l  ?r")
proof-
  note Rhs_Nts_Expand_all_le
  also have "Rhs_Nts Q - X  (Nts_syms ` (Rhss P ` (Rhs_Nts Q  X)))  ?r"
    by (auto simp: Rhss_def Rhs_Nts_def)
  finally show ?thesis.
qed

text ‹One can remove a non-recursive part of grammar by expanding others
with respect to it.›

lemma Lang_Expand_all_idem:
  assumes PP: "Rhs_Nts P  Lhss P = {}"
    and PQ: "Lhss P  Lhss Q = {}" and AP: "A  Lhss P"
  shows "Lang (Expand_all P (Lhss P) Q) A = Lang (P  Q) A"
  (is "?l = ?r")
proof-
  have "?l = Lang (P  Expand_all P (Lhss P) Q) A"
    apply (rule Lang_disj_Lhss_Un[symmetric])
    using Rhs_Nts_Expand_all_le_Rhs_Nts[of P "Lhss P" Q] PP AP by auto
  also have " = Lang (P  Q) A" by (simp add: Lang_Un_Expand_all[OF PQ])
  finally show ?thesis.
qed

lemma Lang_of_Expand_all_idem:
  assumes PP: "Rhs_Nts P  Lhss P = {}" and PQ: "Lhss P  Lhss Q = {}"
    and AP: "Nts_syms α  Lhss P = {}"
  shows "Lang_of (Expand_all P (Lhss P) Q) α = Lang_of (P  Q) α"
  apply (insert AP, induction α)
  using Lang_Expand_all_idem[OF PP PQ]
  by (simp_all split: sym.splits add: Lang_of_Cons)

lemma Lang_Expand_all_idem_new:
  assumes PP: "Rhs_Nts P  Lhss P = {}" and PQ: "Lhss P  Lhss Q = {}"
    and AP: "A  Lhss P"
  shows "Lang_of_set (Expand_all P (Lhss P) Q) (Rhss P A) = Lang (P  Q) A"
    (is "?l = ?r")
proof-
  have "α  Rhss P A  Lang_of (Expand_all P (Lhss P) Q) α = Lang_of (P  Q) α" for α
    apply (rule Lang_of_Expand_all_idem[OF PP PQ])
    using PP by (auto simp: Rhss_def Rhs_Nts_def)
  then have "?l = Lang_of_set (P  Q) (Rhss P A)" by auto
  moreover have "Rhss P A = Rhss (P  Q) A" using AP PQ by (auto simp: Rhss_def dest: in_LhssI)
  ultimately show ?thesis by (simp add: Lang_of_set_Rhss)
qed

lemma Lang_idem_Un_via_Expand_all:
  assumes PP: "Rhs_Nts P  Lhss P = {}" and PQ: "Lhss P  Lhss Q = {}"
  shows "Lang (P  Q) A =
    (if A  Lhss P then Lang_of_set (Expand_all P (Lhss P) Q) (Rhss P A)
     else Lang (Expand_all P (Lhss P) Q) A)"
  using Lang_Expand_all_idem[OF assms] Lang_Expand_all_idem_new[OF assms]
  by auto

corollary Lang_Un_idem_via_Expand_all:
  assumes PQ: "Lhss P  Lhss Q = {}" and QQ: "Rhs_Nts Q  Lhss Q = {}"
  shows "Lang (P  Q) A =
    (if A  Lhss Q then Lang_of_set (Expand_all Q (Lhss Q) P) (Rhss Q A)
     else Lang (Expand_all Q (Lhss Q) P) A)"
  using Lang_idem_Un_via_Expand_all[of Q P A] assms by (auto simp: ac_simps)

subsubsection ‹Expanding head nonterminals›

definition Expand_hd_syms :: "('n,'t) Prods  'n set  ('n,'t) syms  ('n,'t) syms set" where
  "Expand_hd_syms P X α = (case α of []  {[]} | x#xs  Expand_sym P X x @@ {xs})"

lemma Expand_hd_ops: "Expand_hd_syms P X  Expand_ops P X"
  by (auto simp: Expand_hd_syms_def Expand_ops_def intro!: Expand_sym_ops Expand_syms_ops_self split: list.split)

abbreviation Expand_hd :: "('n,'t) Prods  'n set  ('n,'t) Prods  ('n,'t) Prods" where
  "Expand_hd P X Q  Expand (Expand_hd_syms P X) Q"

theorem Lang_Expand_hd:
  assumes "X  Lhss Q = {}"
  shows "Lang (Expand_hd P X Q  P) = Lang (Q  P)"
  using Lang_Expand_Un[OF Expand_hd_ops assms].

end