Theory Pre_Star

(* Authors: Tassilo Lemke, Tobias Nipkow *)

section ‹$Pre^*$›

theory Pre_Star
imports
  Context_Free_Grammar.Context_Free_Grammar
  LTS_Automata
  "HOL-Library.While_Combinator"
begin

(* Internal polishing: One could get rid of ‹reachable_from› and use ‹states_lts ⊆ Q› instead. *)

text‹This theory defines pre*(L)› (pre_star› below) and verifies a simple saturation algorithm
pre_star_auto› that computes pre*(M)› given an NFA M› and a finite set of context-free productions.
Most of the work is on the level of finite LTS (via pre_star_lts›).

A closely related formalization is AFP/Pushdown_Systems›where pre* is computed for pushdown systems instead of CFGs.
›

definition pre_star :: "('n,'t)Prods  ('n,'t) syms set  ('n,'t) syms set" where
"pre_star P L  {α. β  L. P  α ⇒* β}"


subsection ‹Definition on LTS as Fixpoint›

text‹
  The algorithm works by repeatedly adding transitions to the LTS, such that at after every step,
  the LTS accepts the original language and its \textbf{direct} predecessors.

  Since no new states are added, the number of transitions that can be added is bounded,
  which allow to both prove termination and the property of a fixpoint:
  At some point, adding another layer of direct predecessors no-longer changes anything,
  i.e.\ the LTS is saturated and pre* has been reached.›

definition pre_lts :: "('n,'t) Prods  's set  ('s, ('n,'t) sym) lts  ('s, ('n,'t) sym) lts"
  where
"pre_lts P Q T =
  { (q, Nt A, q') | q q' A. q  Q  (β. (A, β)  P  q'  steps_lts T β q)}"

lemma pre_lts_code[code]: "pre_lts P Q T =
   (q  Q. (A,β)  P. q'  steps_lts T β q. {(q, Nt A, q')})"
  unfolding pre_lts_def image_def by(auto)

definition pre_star_lts :: "('n, 't) Prods  's set
     ('s, ('n, 't) sym) lts  ('s, ('n, 't) sym) lts option" where
"pre_star_lts P Q  while_option (λT. T  pre_lts P Q T  T) (λT. T  pre_lts P Q T)"

lemma pre_star_lts_rule:
  assumes "T. H T  T  pre_lts P Q T  T  H (T  pre_lts P Q T)"
    and "pre_star_lts P Q T = Some T'" and "H T"
  shows "H T'"
  using assms unfolding pre_star_lts_def by (rule while_option_rule)

lemma pre_star_lts_fp: "pre_star_lts P Q T = Some T'  T'  (pre_lts P Q T') = T'"
  unfolding pre_star_lts_def using while_option_stop by fast

lemma pre_star_lts_mono: "pre_star_lts P Q T = Some T'  T  T'"
  by (rule pre_star_lts_rule) blast+


subsection‹Propagation of Reachability›

text‹
  No new states are added. Expressing this fact within the auto› model is to show that the
  set of reachable states from any given start state remains unaltered.
›

lemma pre_lts_reachable:
  "reachable_from T q = reachable_from (T  pre_lts P Q T) q"
  unfolding pre_lts_def by (rule reachable_add_trans) blast

lemma pre_star_lts_reachable:
  assumes "pre_star_lts P Q T = Some T'"
  shows "reachable_from T q = reachable_from T' q"
  by (rule pre_star_lts_rule; use assms pre_lts_reachable in fast)

lemma states_pre_lts: assumes "states_lts T  Q" shows "states_lts (pre_lts P Q T)  Q"
using steps_states_lts[OF assms] unfolding pre_lts_def states_lts_def by auto

lemma states_pre_star_lts:
  assumes "pre_star_lts P Q T = Some T'" and "states_lts T  Q"
  shows "states_lts T'  Q"
apply (rule pre_star_lts_rule[OF _ assms(1)])
 apply (simp add: states_lts_Un states_pre_lts)
by(fact assms(2))


subsection‹Correctness›

lemma pre_lts_keeps:
  assumes "q'  steps_lts T β q"
  shows "q'  steps_lts (T  pre_lts P Q T) β q"
  using assms steps_lts_mono by (metis insert_absorb insert_subset sup_ge1)

lemma pre_lts_prod:
  assumes "(A, β)  P" and "q  Q" and "q'  Q" and "q'  steps_lts T β q"
  shows "q'  steps_lts (T  pre_lts P Q T) [Nt A] q"
  using assms unfolding pre_lts_def Steps_lts_def Step_lts_def step_lts_def by force

lemma pre_lts_pre:
  assumes "P  wα  wβ" and "reachable_from T q  Q" and "q'  steps_lts T wβ q"
  shows "q'  steps_lts (T  pre_lts P Q T) wα q"
proof -
  obtain wp ws A β where prod: "(A, β)  P"
      and wα_split: "wα = wp@[Nt A]@ws"
      and wβ_split: "wβ = wp@β@ws"
    using assms(1) by (meson derive.cases)

  obtain q1 q2 where step_wp: "q1  steps_lts T wp q"
      and step_β: "q2  steps_lts T β q1"
      and step_ws: "q'  steps_lts T ws q2"
    using Steps_lts_split3 assms(3)[unfolded wβ_split] by fast
  then have q1_reach: "q1  reachable_from T q" and "q2  reachable_from T q1"
    using assms(2) unfolding reachable_from_def by blast+
  then have q2_reach: "q2  reachable_from T q"
    using assms(2) reachable_from_trans by fast

  have "q2  steps_lts (T  pre_lts P Q T) [Nt A] q1"
    by (rule pre_lts_prod; use q1_reach q2_reach assms(2) prod step_β in blast)
  moreover have "q1  steps_lts (T  pre_lts P Q T) wp q"
      and "q'  steps_lts (T  pre_lts P Q T) ws q2"
    using step_wp step_ws pre_lts_keeps by fast+
  ultimately have "q'  steps_lts (T  pre_lts P Q T) wα q"
    unfolding wα_split using Steps_lts_join3 by fast
  then show ?thesis .
qed

lemma pre_lts_fp:
  assumes "P  wα ⇒* wβ" and "reachable_from T q  Q" and "q'  steps_lts T wβ q"
    and fp: "T  pre_lts P Q T = T"
  shows "q'  steps_lts T wα q"
proof (insert assms, induction rule: converse_rtranclp_induct[where r="derive P"])
  case base thus ?case by simp
next
  case (step y z)
  then show ?case
    using pre_lts_pre by fastforce
qed

lemma pre_lts_while:
  assumes "P  wα ⇒* wβ" and "reachable_from T q  Q" and "q'  steps_lts T wβ q"
    and "pre_star_lts P Q T = Some T'"
  shows "q'  steps_lts T' wα q"
proof -
  have "T'  pre_lts P Q T' = T'"
    using assms(4) by (rule pre_star_lts_fp)
  moreover have "reachable_from T' q  Q"
    using assms(2,4) pre_star_lts_reachable by fast
  moreover have "q'  steps_lts T' wβ q"
    by (rule steps_lts_mono'[where T1=T]; use assms(3,4) pre_star_lts_mono in blast)
  ultimately show ?thesis
    using assms(1) pre_lts_fp by fast
qed

lemma pre_lts_sub_aux:
  assumes "q'  steps_lts (T  pre_lts P Q T) w q"
  shows "w'. P  w ⇒* w'  q'  steps_lts T w' q"
proof (insert assms, induction w arbitrary: q)
  case Nil
  then show ?case
    by (simp add: Steps_lts_def)
next
  case (Cons c w)
  then obtain q1 where step_w: "q'  steps_lts (T  pre_lts P Q T) w q1"
      and step_c: "q1  steps_lts (T  pre_lts P Q T) [c] q"
    using Steps_lts_split by (metis (no_types, lifting) append_Cons append_Nil)

  obtain w' where "q'  steps_lts T w' q1" and "P  w ⇒* w'"
    using Cons step_w by blast

  have "c'. q1  steps_lts T c' q  P  [c] ⇒* c'"
  proof (cases "q1  steps_lts T [c] q")
    case True
    then show ?thesis
      by blast
  next
    case False
    then have "q1  steps_lts (pre_lts P Q T) [c] q"
      using step_c unfolding Steps_lts_def Step_lts_def step_lts_def by force
    then have "(q, c, q1)  pre_lts P Q T"
      by (auto simp: Steps_lts_def Step_lts_def step_lts_def)
    then obtain A β where "(A, β)  P" and "c = Nt A" and "q1  steps_lts T β q"
      unfolding pre_lts_def by blast
    moreover have "P  [c] ⇒* β"
      using calculation by (simp add: derive_singleton r_into_rtranclp)
    ultimately show ?thesis
      by blast
  qed
  then obtain c' where "q1  steps_lts T c' q" and "P  [c] ⇒* c'"
    by blast

  have "q'  steps_lts T (c'@w') q"
    using q1  steps_lts T c' q q'  steps_lts T w' q1 Steps_lts_join by fast
  moreover have "P  (c#w) ⇒* (c'@w')"
    using P  [c] ⇒* c' P  w ⇒* w'
    by (metis (no_types, opaque_lifting) Cons_eq_appendI derives_append_decomp self_append_conv2)
  ultimately show ?case
    by blast
qed

lemma pre_lts_sub:
  assumes "w. (q'  steps_lts T' w q)  (w'. P  w ⇒* w'  q'  steps_lts T w' q)"
    and "q'  steps_lts (T'  pre_lts P Q T') w q"
  shows "w'. P  w ⇒* w'  q'  steps_lts T w' q"
proof -
  obtain w' where "P  w ⇒* w'" and "q'  steps_lts T' w' q"
    using pre_lts_sub_aux assms by fast
  then obtain w'' where "P  w' ⇒* w''" and "q'  steps_lts T w'' q"
    using assms(1) by blast
  moreover have "P  w ⇒* w''"
    using P  w ⇒* w' calculation(1) by simp
  ultimately show ?thesis
    by blast
qed

lemma pre_star_lts_sub:
  assumes "pre_star_lts P Q T = Some T'"
  shows "(q'  steps_lts T' w q)  (w'. P  w ⇒* w'  q'  steps_lts T w' q)"
proof -
  let ?I = "λT'. w. (q'  steps_lts T' w q)  (w'. P  w ⇒* w'  q'  steps_lts T w' q)"
  have "T'. ?I T'  ?I (T'  pre_lts P Q T')"
    by (simp add: pre_lts_sub[where T=T])
  then have "?I T'"
    by (rule pre_star_lts_rule[where T=T and T'=T']; use assms in blast)
  then show "(q'  steps_lts T' w q)  (w'. P  w ⇒* w'  q'  steps_lts T w' q)"
    by simp
qed

lemma pre_star_lts_correct:                 
  assumes "reachable_from T q0  Q" and "pre_star_lts P Q T = Some T'"
  shows "Lang_lts T' q0 F = pre_star P (Lang_lts T q0 F)"
proof (standard; standard)
  fix w
  assume "w  Lang_lts T' q0 F"
  then obtain qf where "qf  steps_lts T' w q0" and "qf  F"
    by blast
  then obtain w' where "P  w ⇒* w'" and "qf  steps_lts T w' q0"
    using pre_star_lts_sub assms by fast
  moreover have "w'  Lang_lts T q0 F"
    using calculation qf  F by blast
  ultimately show "w  pre_star P (Lang_lts T q0 F)"
    unfolding pre_star_def by blast
next
  fix w
  assume "w  pre_star P (Lang_lts T q0 F)"
  then obtain w' where "P  w ⇒* w'" and "w'  Lang_lts T q0 F"
    unfolding pre_star_def by blast
  then obtain qf where "qf  steps_lts T w' q0" and "qf  F"
    by blast
  then have "qf  steps_lts T' w' q0"
    using steps_lts_mono pre_star_lts_mono assms by (metis in_mono)
  moreover have "reachable_from T' q0  Q"
    using assms pre_star_lts_reachable by fast
  moreover have "T'  pre_lts P Q T' = T'"
    by (rule pre_star_lts_fp; use assms(2) in simp)
  moreover note P  w ⇒* w'
  ultimately have "qf  steps_lts T' w q0"
    by (elim pre_lts_fp) simp+
  with qf  F show "w  Lang_lts T' q0 F"
    by blast
qed


subsection‹Termination›

lemma while_option_finite_subset_Some':
  fixes C :: "'a set"
  assumes "mono f" and "X. X  C  f X  C" and "finite C" and "S  C" and "X. X  f X"
  shows "P. while_option (λA. f A  A) f S = Some P"
proof (rule measure_while_option_Some[where
    f= "%A::'a set. card C - card A" and P= "%A. A  C  A  f A" and s=S])
  fix A assume A: "A  C  A  f A" "f A  A"
  show "(f A  C  f A  f (f A))  card C - card (f A) < card C - card A"
    (is "?L  ?R")
  proof
    show ?L by (metis A(1) assms(2) monoD[OF mono f])
    show ?R by (metis A assms(2,3) card_seteq diff_less_mono2 equalityI linorder_le_less_linear rev_finite_subset)
  qed
qed (simp add: assms)

lemma pre_star_lts_terminates:
  fixes P :: "('n, 't) Prods" and Q :: "'s set" and T0 :: "('s, ('n, 't) sym) lts"
  assumes "finite P" and "finite Q" and "finite T0" and "states_lts T0  Q"
  shows "T. pre_star_lts P Q T0 = Some T"
proof -
  define b :: "('s, ('n, 't) sym) lts  bool" where
    [simp]: "b = (λT. T  pre_lts P Q T  T)"
  define f :: "('s, ('n, 't) sym) lts  ('s, ('n, 't) sym) lts" where
    [simp]: "f = (λT. T  pre_lts P Q T)"
  then have "mono f"
    unfolding mono_def pre_lts_def
    by (smt (verit, ccfv_threshold) UnCI UnE in_mono mem_Collect_eq Steps_lts_mono2 subsetI)

  define U :: "('s, ('n, 't) sym) lts" where
    "U = { (q, Nt A, q') | q q' A. q  Q  (β. (A, β)  P  q'  Q)}  T0"
  have "p a q. (p,a,q)  T0  p  Q  q  Q"
    using assms(4) unfolding states_lts_def by auto
  then have "pre_lts P Q T  U" if asm: "T  U" for T
    using asm steps_states_lts[of T Q] unfolding U_def pre_lts_def states_lts_def
    by fastforce
  then have U_bounds: "X. X  U  f X  U"
    by simp

  have "finite U"
  proof -
    define U' :: "('s, ('n, 't) sym) lts" where
      [simp]: "U' = Q × ((λ(A,_). Nt A) ` P) × Q"
    have "finite ((λ(A,_). Nt A) ` P)"
      using assms(1) by simp
    then have "finite U'"
      using assms(2) U'_def by blast

    define T' :: "('s, ('n, 't) sym) lts" where
      [simp]: "T' = { (q,Nt A,q') | q q' A. q  Q  (β. (A, β)  P  q'  Q)}"
    then have "T'  U'"
      unfolding T'_def U'_def using assms(1) by fast
    moreover note finite U'
    ultimately have "finite T'"
      using rev_finite_subset[of U' T'] by blast
    then show "finite U"
      by (simp add: U_def assms)
  qed

  note criteria = finite U U_def f_def U_bounds mono f
  have "P. while_option (λA. f A  A) f T0 = Some P"
    by (rule while_option_finite_subset_Some'[where C=U]; use criteria in blast)
  then show ?thesis
    by (simp add: pre_star_lts_def)
qed

subsection ‹The Automaton Level›

definition pre_star_auto :: "('n, 't) Prods  ('s, ('n, 't) sym) auto  ('s, ('n, 't) sym) auto" where
  "pre_star_auto P M  (
    let Q = {auto.start M}  states_lts (auto.lts M) in
    case pre_star_lts P Q (auto.lts M) of
      Some T'  M  auto.lts := T' 
  )"

lemma pre_star_auto_correct:
  assumes "finite P" and "finite (auto.lts M)"
  shows "Lang_auto (pre_star_auto P M) = pre_star P (Lang_auto M)"
proof -
  define T where "T  auto.lts M"
  define Q where "Q  {auto.start M}  states_lts T"
  then have "finite Q"
    unfolding T_def states_lts_def using assms(2) by auto
  have MQ: "states_lts (auto.lts M)  Q" unfolding Q_def T_def by (force)
  have "reachable_from T (auto.start M)  Q"
    using reachable_from_computable unfolding Q_def states_lts_def by fastforce
  moreover obtain T' where T'_def: "pre_star_lts P Q T = Some T'"
    using pre_star_lts_terminates[OF assms(1) finite Q assms(2) MQ] T_def by blast
  ultimately have "Lang_lts T' (auto.start M) (auto.finals M)
    = pre_star P (Lang_lts T (auto.start M) (auto.finals M))"
    by (rule pre_star_lts_correct)
  then have "Lang_auto (M  auto.lts := T' ) = pre_star P (Lang_auto M)"
    by (simp add: T_def)
  then show ?thesis
    unfolding pre_star_auto_def using Q_def T'_def T_def
    by(force)
qed

lemma pre_star_lts_refl:
  assumes "pre_star_lts P Q T = Some T'" and "(A, [])  P" and "q  Q"
  shows "(q, Nt A, q)  T'"
proof -
  have "q  steps_lts T' [] q"
    unfolding Steps_lts_def using assms by force
  then have "(q, Nt A, q)  pre_lts P Q T'"
    unfolding pre_lts_def using assms by blast
  moreover have "T' = T'  pre_lts P Q T'"
    using pre_star_lts_fp assms(1) by blast
  ultimately show ?thesis
    by blast
qed

lemma pre_star_lts_singleton:
  assumes "pre_star_lts P Q T = Some T'" and "(A, [B])  P"
    and "(q, B, q')  T'" and "q  Q" and "q'  Q"
  shows "(q, Nt A, q')  T'"
proof -
  have "q'  steps_lts T' [B] q"
    unfolding steps_lts_defs using assms by force
  then have "(q, Nt A, q')  pre_lts P Q T'"
    unfolding pre_lts_def using assms by blast
  moreover have "T' = T'  (pre_lts P Q T')"
    using pre_star_lts_fp assms(1) by blast
  ultimately show ?thesis
    by blast
qed

lemma pre_star_lts_impl:
  assumes "pre_star_lts P Q T = Some T'" and "(A, [B, C])  P"
    and "(q, B, q')  T'" and "(q', C, q'')  T'"
    and "q  Q" and "q'  Q" and "q''  Q"
  shows "(q, Nt A, q'')  T'"
proof -
  have "q''  steps_lts T' [B, C] q"
    unfolding steps_lts_defs using assms by force
  then have "(q, Nt A, q'')  pre_lts P Q T'"
    unfolding pre_lts_def using assms by blast
  moreover have "T' = T'  pre_lts P Q T'"
    using pre_star_lts_fp assms(1) by blast
  ultimately show ?thesis
    by blast
qed

end