(*  Title:       A theory of Semi-Automata
    Authors:     Thomas Tuerk <tuerk@in.tum.de>
*)

section \<open> Semi Automata \<close>

theory SemiAutomaton
imports Main LTS "../Accessible"
        "HOL-Library.Nat_Bijection"
begin

text \<open> Semi Automata are finite automata without an acceptance condition. By adding an
acceptance condition one can get the classical nondeterministic, universal and deterministic
automata on finite words as well as omega automata like Buechi automata. \<close>

subsection \<open> Basic Definitions \<close>

text \<open> The type of states of an automaton is unimportant most of the time. However,
it is important that one can always get a fresh state. This means that the type has to be infinite
and one can easily get access to new elements. This demand is expressed by the following
class. \<close>

class automaton_states =
  fixes states_enumerate :: "nat \<Rightarrow> 'a"
  assumes states_enumerate_inj: "inj states_enumerate"
begin
  lemma states_enumerate_eq: "states_enumerate n = states_enumerate m \<longleftrightarrow> n = m"
    using states_enumerate_inj
    unfolding inj_on_def by auto

  lemma not_finite_automaton_states_UNIV : "~(finite (UNIV::'a set))"
  proof
    assume fin_UNIV: "finite (UNIV::'a set)"
    hence "finite (states_enumerate ` UNIV)"
      by (rule_tac finite_subset[of _ UNIV], simp_all)
    hence "finite (UNIV::nat set)"
      using states_enumerate_inj
      by (rule finite_imageD)
    thus "False" using infinite_UNIV_nat by simp
  qed
end

instantiation nat :: automaton_states
begin
  definition "states_enumerate q = q"

  instance proof
    show "inj (states_enumerate::nat \<Rightarrow> nat)"
      unfolding states_enumerate_nat_def[abs_def]
      by (simp add: inj_on_def)
  qed
end

text \<open> This theory defines finite semi-automata.
  These automata are represented as records containing a transtion relation,
  a set of initial states.\<close>

record ('q,'a) SemiAutomaton_rec =
  \<Q> :: "'q set"          \<comment>\<open>The set of states\<close>
  \<Sigma> :: "'a set"          \<comment>\<open>The set of labels\<close>
  \<Delta> :: "('q,'a) LTS"     \<comment>\<open>The transition relation\<close>
  \ :: "'q set"           \<comment>\<open>The set of initial states\<close>

text \<open> Semi-Automata define sets of runs \<close>
definition SemiAutomaton_is_fin_run where
  "SemiAutomaton_is_fin_run \ w r \<longleftrightarrow>
   (hd r \<in> \ \) \<and> LTS_is_fin_run (\<Delta> \) w r"

lemma SemiAutomaton_is_fin_run_simps[simp] :
  "SemiAutomaton_is_fin_run \ w [] = False"
  "SemiAutomaton_is_fin_run \ w [q] = (w = [] \<and> q \<in> (\ \))"
  "SemiAutomaton_is_fin_run \ [] r = (\<exists>q. r = [q] \<and> q \<in> (\ \))"
unfolding SemiAutomaton_is_fin_run_def by (auto simp add: length_Suc_conv)

definition SemiAutomaton_is_inf_run where
  "SemiAutomaton_is_inf_run \ w r \<longleftrightarrow>
   (r 0 \<in> \ \) \<and> LTS_is_inf_run (\<Delta> \) w r"

subsubsection \<open> Wellformedness \<close>

text \<open> The following locales capture, whether a Semi-Automaton are well-formed. \<close>
locale SemiAutomaton =
  fixes \::"('q,'a, 'SemiAutomaton_more) SemiAutomaton_rec_scheme"
  assumes \<Delta>_consistent: "\<And>q \<sigma> q'. (q,\<sigma>,q') \<in> \<Delta> \ \<Longrightarrow> (q \<in> \<Q> \) \<and> (\<sigma> \<in> \<Sigma> \) \<and> (q' \<in> \<Q> \)"
      and \_consistent: "\ \ \<subseteq> \<Q> \"

locale FinSemiAutomaton = SemiAutomaton \
  for \::"('q,'a, 'SemiAutomaton_more) SemiAutomaton_rec_scheme" +
  assumes finite_\<Q>: "finite (\<Q> \)"
      and finite_\<Delta>: "finite (\<Delta> \)"

lemma FinSemiAutomaton_alt_def :
  "FinSemiAutomaton \ \<longleftrightarrow> SemiAutomaton \ \<and>  finite (\<Q> \) \<and> finite (\<Delta> \)"
unfolding FinSemiAutomaton_def FinSemiAutomaton_axioms_def by simp

lemma FinSemiAutomaton_full_def :
  "FinSemiAutomaton \ \<longleftrightarrow>
   ((\<forall>q \<sigma> q'. (q, \<sigma>, q') \<in> \<Delta> \ \<longrightarrow> q \<in> \<Q> \ \<and> \<sigma> \<in> \<Sigma> \ \<and> q' \<in> \<Q> \) \<and>
     \ \ \<subseteq> \<Q> \ \<and> finite (\<Q> \) \<and> finite (\<Delta> \))"
unfolding FinSemiAutomaton_def FinSemiAutomaton_axioms_def SemiAutomaton_def
by simp

lemma SemiAutomaton_truncate[simp]:
 "SemiAutomaton (SemiAutomaton.truncate \) \<longleftrightarrow> SemiAutomaton \"
unfolding SemiAutomaton_def by (simp add: SemiAutomaton_rec.defs)

lemma FinSemiAutomaton_truncate[simp]:
 "FinSemiAutomaton (SemiAutomaton.truncate \) \<longleftrightarrow> FinSemiAutomaton \"
unfolding FinSemiAutomaton_full_def by (simp add: SemiAutomaton_rec.defs)

lemma FinSemiAutomaton_is_Semiautomaton :
  "FinSemiAutomaton \ \<Longrightarrow> SemiAutomaton \"
unfolding FinSemiAutomaton_def by simp

lemma SemiAutomaton_intro [intro!] :
  " \<lbrakk>\<And>q \<sigma> q'. (q,\<sigma>,q') \<in> \<Delta> \ \<Longrightarrow> (q \<in> \<Q> \) \<and> (\<sigma> \<in> \<Sigma> \) \<and> (q' \<in> \<Q> \);
     \ \ \<subseteq> \<Q> \; finite (\<Q> \); finite (\<Delta> \)\<rbrakk> \<Longrightarrow> SemiAutomaton \"
by (simp add: SemiAutomaton_def)

lemma (in FinSemiAutomaton) finite_\ :
  "finite (\ \)"
using \_consistent finite_\<Q>
by (metis finite_subset)

lemma (in SemiAutomaton) \<Delta>_subset :
"\<Delta> \ \<subseteq> \<Q> \ \<times> \<Sigma> \ \<times> \<Q> \"
using \<Delta>_consistent
by (simp add: subset_iff)

lemma (in SemiAutomaton) SemiAutomaton_\<Q>_not_empty:
  assumes "SemiAutomaton_is_inf_run \ w r"
    shows "\<Q>(\) \<noteq> {}"
using assms SemiAutomaton_is_inf_run_def[of \ w r] \_consistent by auto

lemma (in SemiAutomaton) SemiAutomaton_\<Delta>_cons___is_fin_run_LTS :
assumes run: "LTS_is_fin_run (\<Delta> \) w r"
    and wr_OK: "w \<noteq> [] \<or> hd r \<in> \<Q> \"
shows "w \<in> lists (\<Sigma> \)" "r \<in> lists (\<Q> \)"
proof -
  from run \<Delta>_consistent have
    wr_in: "\<And>i. i < length w \<Longrightarrow> w ! i \<in> \<Sigma> \ \<and> r ! i \<in> \<Q> \ \<and> r ! (Suc i) \<in> \<Q> \" and
    len_eq: "length r = Suc (length w)"
    unfolding LTS_is_fin_run_def by auto

  from wr_in show "w \<in> lists (\<Sigma> \)"
    unfolding in_lists_conv_set Ball_def in_set_conv_nth by metis

  have "\<And>i. i < length r \<Longrightarrow> r ! i \<in> \<Q> \"
  proof -
    fix i
    assume i_less: "i < length r"
    show "r ! i \<in> \<Q> \"
    proof (cases "i < length w")
      case True with wr_in show ?thesis by simp
    next
      case False with i_less len_eq
      have i_eq: "i = length w" by auto

      show ?thesis
      proof (cases w)
        case Nil with i_eq wr_OK len_eq show ?thesis by (cases r, simp_all)
      next
        case (Cons q w')
        with wr_in[of "length w'"] i_eq show ?thesis by simp
      qed
    qed
  qed
  thus "r \<in> lists (\<Q> \)"
    unfolding in_lists_conv_set Ball_def in_set_conv_nth by metis
qed

lemma (in SemiAutomaton) SemiAutomaton_\<Delta>_cons___is_fin_run :
assumes run: "SemiAutomaton_is_fin_run \ w r"
shows "w \<in> lists (\<Sigma> \)" "r \<in> lists (\<Q> \)"
using SemiAutomaton_\<Delta>_cons___is_fin_run_LTS[of w r] run
      \_consistent
unfolding SemiAutomaton_is_fin_run_def by auto

lemma (in SemiAutomaton) SemiAutomaton_\<Delta>_cons___LTS_is_reachable :
  "\<lbrakk>LTS_is_reachable (\<Delta> \) q w q'\<rbrakk> \<Longrightarrow> (q \<in> \<Q> \ \<longrightarrow> q' \<in> \<Q> \) \<and> w \<in> lists (\<Sigma> \)"
using \<Delta>_consistent
apply (induct w arbitrary: q)
apply auto
done

lemma (in SemiAutomaton) LTS_is_reachable___labels :
  "LTS_is_reachable (\<Delta> \) q w q' \<Longrightarrow> w \<in> lists (\<Sigma> \)"
by (simp add: SemiAutomaton_\<Delta>_cons___LTS_is_reachable)

lemma (in SemiAutomaton) SemiAutomaton_\<Delta>_cons___is_inf_run_LTS :
assumes run: "LTS_is_inf_run (\<Delta> \) w r"
shows "w i \<in> (\<Sigma> \)" "r i \<in> (\<Q> \)"
using run \<Delta>_consistent
unfolding LTS_is_inf_run_def by auto

lemma (in SemiAutomaton) SemiAutomaton_\<Delta>_cons___is_inf_run :
assumes run: "SemiAutomaton_is_inf_run \ w r"
shows "w i \<in> (\<Sigma> \)" "r i \<in> (\<Q> \)"
using SemiAutomaton_\<Delta>_cons___is_inf_run_LTS[of w r i] run
unfolding SemiAutomaton_is_inf_run_def by simp_all

lemma (in SemiAutomaton) LTS_is_reachable_from_initial_alt_def :
  "accessible (LTS_forget_labels (\<Delta> \)) (\ \) =
   {q'. (\<exists> q\<in> (\ \). \<exists>w. LTS_is_reachable (\<Delta> \) q w q')}"
by (simp add: accessible_def rtrancl_LTS_forget_labels)

lemma (in SemiAutomaton) LTS_is_reachable_from_initial_subset :
  "accessible (LTS_forget_labels (\<Delta> \)) (\ \) \<subseteq> \<Q> \"
using SemiAutomaton_\<Delta>_cons___LTS_is_reachable \_consistent
unfolding LTS_is_reachable_from_initial_alt_def
by auto

lemma (in FinSemiAutomaton) LTS_is_reachable_from_initial_finite :
  "finite (accessible (LTS_forget_labels (\<Delta> \)) (\ \))"
using LTS_is_reachable_from_initial_subset finite_\<Q>
by (rule finite_subset)

subsection \<open> Determistic, complete Semiautomata \<close>

definition SemiAutomaton_is_deterministic where
  "SemiAutomaton_is_deterministic \ \<equiv> (LTS_is_deterministic (\<Delta> \) \<and> (\<exists>q0. \ \ = {q0}))"

definition SemiAutomaton_is_complete_deterministic where
  "SemiAutomaton_is_complete_deterministic \ \<equiv>
   (LTS_is_complete_deterministic (\<Q> \) (\<Sigma> \) (\<Delta> \) \<and> (\<exists>q0. \ \ = {q0}))"

lemma SemiAutomaton_is_complete_deterministic_alt_def :
  "SemiAutomaton_is_complete_deterministic \ = (LTS_is_complete (\<Q> \) (\<Sigma> \) (\<Delta> \) \<and> SemiAutomaton_is_deterministic \)"
  unfolding SemiAutomaton_is_complete_deterministic_def LTS_is_complete_deterministic_def SemiAutomaton_is_deterministic_def
  by blast

locale DetSemiAutomaton = SemiAutomaton \
  for \::"('q,'a, 'SemiAutomaton_more) SemiAutomaton_rec_scheme" +
  assumes complete_deterministic_SemiAutomaton: "SemiAutomaton_is_complete_deterministic \"

lemma DetSemiAutomaton_alt_def :
  "DetSemiAutomaton \ = (SemiAutomaton \ \<and> SemiAutomaton_is_complete_deterministic \)"
unfolding DetSemiAutomaton_def DetSemiAutomaton_axioms_def by simp

lemma SemiAutomaton_is_complete_deterministic_truncate [simp] :
  "SemiAutomaton_is_complete_deterministic (SemiAutomaton.truncate \) =
   SemiAutomaton_is_complete_deterministic \"
unfolding SemiAutomaton_is_complete_deterministic_def
by (simp add: SemiAutomaton_rec.defs)

lemma DetSemiAutomaton_truncate [simp] :
  "DetSemiAutomaton (SemiAutomaton.truncate \) = DetSemiAutomaton \"
unfolding DetSemiAutomaton_alt_def by simp

locale DetFinSemiAutomaton = FinSemiAutomaton \ +
                             DetSemiAutomaton \
  for \::"('q,'a, 'SemiAutomaton_more) SemiAutomaton_rec_scheme"

lemma DetFinSemiAutomaton_alt_def :
  "DetFinSemiAutomaton \ = (FinSemiAutomaton \ \<and> DetSemiAutomaton \)"
unfolding DetFinSemiAutomaton_def by simp

lemma DetFinSemiAutomaton_truncate [simp] :
  "DetFinSemiAutomaton (SemiAutomaton.truncate \) = DetFinSemiAutomaton \"
unfolding DetFinSemiAutomaton_alt_def
by simp

lemma (in DetSemiAutomaton) complete_LTS: "LTS_is_complete (\<Q> \) (\<Sigma> \) (\<Delta> \)"
  using complete_deterministic_SemiAutomaton
  unfolding SemiAutomaton_is_complete_deterministic_alt_def
  by simp

lemma (in DetSemiAutomaton) deterministic_SemiAutomaton: "SemiAutomaton_is_deterministic \"
  using complete_deterministic_SemiAutomaton
  unfolding SemiAutomaton_is_complete_deterministic_alt_def
  by simp

lemma (in DetSemiAutomaton) complete_deterministic_LTS: "LTS_is_complete_deterministic (\<Q> \) (\<Sigma> \) (\<Delta> \)"
  using complete_deterministic_SemiAutomaton
  unfolding SemiAutomaton_is_complete_deterministic_alt_def
            LTS_is_complete_deterministic_def SemiAutomaton_is_deterministic_def
  by simp

lemma (in DetSemiAutomaton) deterministic_LTS: "LTS_is_deterministic (\<Delta> \)"
  using complete_deterministic_LTS
  unfolding LTS_is_complete_deterministic_def
  by simp

lemma (in DetSemiAutomaton) unique_initial : "\<exists>! x. x \<in> \ \"
using deterministic_SemiAutomaton unfolding SemiAutomaton_is_deterministic_def by (auto)

lemma (in DetFinSemiAutomaton) finite_\<Sigma>I:
  assumes "\<Q> \ \<noteq> {}"
  shows "finite (\<Sigma> \)"
proof -
  note complete_deterministic_LTS
  hence "LTS_is_complete (\<Q> \) (\<Sigma> \) (\<Delta> \)"
    by (simp add: LTS_is_complete_deterministic_def)
  with assms have "\<Sigma> \ \<subseteq> (\<lambda>(q,a,q'). a) ` \<Delta> \"
    unfolding LTS_is_complete_def
    by (fastforce split: prod.split)
  with finite_subset finite_\<Delta> show ?thesis by blast
qed

definition \ where "\ \ \<equiv> SOME q. q \<in> \ \"

lemma (in DetSemiAutomaton) \_is_set_\ [simp] : "\ \ = {\ \}"
proof -
  obtain x where x: "\ \ = {x}" using unique_initial by blast
  then show "\ \ = {\ \}" unfolding \_def by simp
qed

lemma (in DetSemiAutomaton) \_is_state : "\ \ \<in> \<Q> \" using \_consistent by simp
lemma (in DetSemiAutomaton) \<Q>_not_Emp: "\<Q> \ \<noteq> {}" using \_is_state by auto

subsubsection \<open> The unique transition function \<close>

definition \<delta> where "\<delta> \ \<equiv> LTS_to_DLTS (\<Delta> \)"

lemma (in DetSemiAutomaton) \<delta>_to_\<Delta> [simp] : "DLTS_to_LTS (\<delta> \) = \<Delta> \"
  by (simp add: \<delta>_def LTS_to_DLTS_inv deterministic_LTS)

lemma (in DetSemiAutomaton) \<delta>_in_\<Delta>_iff :
  "(q, \<sigma>, q') \<in> \<Delta> \ \<longleftrightarrow> (\<delta> \) (q, \<sigma>) = Some q'"
unfolding \<delta>_def
by (simp add: LTS_to_DLTS_is_some_det deterministic_LTS)

lemma (in DetSemiAutomaton) \<delta>_wf :
  assumes "\<delta> \ (q,\<sigma>) = Some q'"
  shows "q \<in> \<Q> \ \<and> (\<sigma> \<in> \<Sigma> \) \<and> (q' \<in> \<Q> \)"
using assms
by (simp add: \<delta>_in_\<Delta>_iff [symmetric] \<Delta>_consistent)

subsubsection \<open> Lemmas about deterministic semiautomata \<close>

lemma (in DetSemiAutomaton) DetSemiAutomaton_LTS_is_reachable_DLTS_reach_simp :
  "LTS_is_reachable (\<Delta> \) q w q' \<longleftrightarrow> (DLTS_reach (\<delta> \) q w = Some q')"
by (simp add: LTS_is_reachable_determistic deterministic_LTS \<delta>_def)

lemma (in DetSemiAutomaton) DetSemiAutomaton_LTS_is_reachable_unique :
  "\<lbrakk>LTS_is_reachable (\<Delta> \) q w q'; LTS_is_reachable (\<Delta> \) q w q''\<rbrakk> \<Longrightarrow> q'' = q'"
by (simp add: DetSemiAutomaton_LTS_is_reachable_DLTS_reach_simp)

lemma (in DetSemiAutomaton) DetSemiAutomaton_\<delta>_is_none_iff :
  "(\<delta> \ (q, \<sigma>) = None) \<longleftrightarrow> \<not> (q \<in> \<Q> \ \<and> \<sigma> \<in> \<Sigma> \)"
apply (insert complete_LTS)
apply (simp add: \<delta>_def LTS_to_DLTS_def LTS_is_complete_def)
apply (auto simp add: \<Delta>_consistent)
done

lemma (in DetSemiAutomaton) DetSemiAutomaton_\<delta>_is_some :
  "\<lbrakk>q \<in> \<Q> \; \<sigma> \<in> \<Sigma> \\<rbrakk> \<Longrightarrow> \<not> (\<delta> \ (q, \<sigma>) = None)"
by (simp add: DetSemiAutomaton_\<delta>_is_none_iff)

lemma (in DetSemiAutomaton) DetSemiAutomaton_reach_is_none_iff :
  "DLTS_reach (\<delta> \) q w = None \<longleftrightarrow> \<not>((w \<noteq> [] --> q \<in> \<Q> \) \<and> w \<in> lists (\<Sigma> \))"
proof (induct w arbitrary: q)
  case Nil thus ?case by simp
next
  case (Cons \<sigma> w)
  note ind_hyp = this

  show ?case
  proof (cases "\<delta> \ (q, \<sigma>)")
    case None thus ?thesis by (simp, auto simp add: DetSemiAutomaton_\<delta>_is_none_iff)
  next
    case (Some q') note \<delta>_eq = this
    hence "q \<in> \<Q> \ \<and> \<sigma> \<in> \<Sigma> \ \<and> q' \<in> \<Q> \" by (simp add: \<delta>_wf)
    with \<delta>_eq show ?thesis by (simp add: ind_hyp)
  qed
qed

primrec DetSemiAutomaton_fin_run where
  "DetSemiAutomaton_fin_run \ q [] = [q]"
| "DetSemiAutomaton_fin_run \ q (a # w) =
   q # DetSemiAutomaton_fin_run \ (the (\<delta> \ (q, a))) w"

lemma (in DetSemiAutomaton) DetSemiAutomaton_fin_run_alt_def :
assumes w_OK: "w \<in> lists (\<Sigma> \)"
    and q_OK: "q \<in> \<Q> \"
shows "DetSemiAutomaton_fin_run \ q w = CLTS_fin_run (\<Q> \) (\<Delta> \) q w"
using w_OK q_OK
proof (induct w arbitrary: q)
  case Nil thus ?case unfolding DetSemiAutomaton_fin_run_def by simp
next
  case (Cons a w)
  note aw_OK = Cons(1,2)
  note indhyp = Cons(3)
  note q_OK = Cons(4)

  from complete_LTS aw_OK(1) q_OK \<delta>_in_\<Delta>_iff [of q a] obtain q' where
    \<delta>_eq: "\<delta> \ (q, a) = Some q'" and
    q'_in: "q' \<in> \<Q> \"
    unfolding LTS_is_complete_def by metis

  have \<delta>_eq': "(SOME q'. (q, a, q') \<in> \<Delta> \ \<and> q' \<in> \<Q> \) = q'"
    apply (simp add: \<delta>_in_\<Delta>_iff \<delta>_eq)
    apply (rule some_equality)
    apply (simp_all add: q'_in)
  done

  from indhyp [OF q'_in]
  show ?case by (simp add: \<delta>_eq \<delta>_eq')
qed

primrec DetSemiAutomaton_inf_run where
  "DetSemiAutomaton_inf_run \ q w 0 = q"
| "DetSemiAutomaton_inf_run \ q w (Suc i) =
   the (\<delta> \ (DetSemiAutomaton_inf_run \ q w i, w i))"

lemma (in DetSemiAutomaton) DetSemiAutomaton_inf_run_alt_def :
assumes w_OK: "\<And>i. w i \<in> (\<Sigma> \)"
    and q_OK: "q \<in> \<Q> \"
shows "DetSemiAutomaton_inf_run \ q w = CLTS_inf_run (\<Q> \) (\<Delta> \) q w"
proof -
  { fix i
    have "DetSemiAutomaton_inf_run \ q w i = CLTS_inf_run (\<Q> \) (\<Delta> \) q w i"
    proof (induct i)
      case 0 thus ?case by simp
    next
      case (Suc i) note indhyp = this

      define qq where "qq \<equiv> CLTS_inf_run (\<Q> \) (\<Delta> \) q w i"

      from CLTS_inf_run_correct(2)[OF complete_LTS, of w q i, OF w_OK q_OK]
      have qq_in: "qq \<in> \<Q> \" unfolding qq_def .

      from complete_LTS w_OK[of i] qq_in \<delta>_in_\<Delta>_iff [of qq "w i"] obtain qq' where
          \<delta>_eq: "\<delta> \ (qq, w i) = Some qq'" and
          qq'_in: "qq' \<in> \<Q> \"
        unfolding LTS_is_complete_def by metis

      have \<delta>_eq': "(SOME q'. (qq, w i, q') \<in> \<Delta> \ \<and> q' \<in> \<Q> \) = qq'"
        apply (simp add: \<delta>_in_\<Delta>_iff \<delta>_eq)
        apply (rule some_equality)
        apply (simp_all add: qq'_in)
      done

      show ?case by (simp add: qq_def[symmetric] indhyp \<delta>_eq \<delta>_eq')
    qed
  }
  thus ?thesis by auto
qed

lemma (in DetSemiAutomaton) DetSemiAutomaton_fin_run_correct :
  "SemiAutomaton_is_fin_run \ w r \<longleftrightarrow>
   w \<in> lists (\<Sigma> \) \<and> r = DetSemiAutomaton_fin_run \ (\ \) w"
proof (cases "w \<in> lists (\<Sigma> \)")
  case False thus ?thesis
    by (metis SemiAutomaton_\<Delta>_cons___is_fin_run(1))
next
  case True note w_in = this

  note detSemi_run_eq = DetSemiAutomaton_fin_run_alt_def[OF w_in \_is_state]
  note fin_run_rule = CDLTS_fin_run_correct [OF complete_deterministic_LTS w_in, of r]

  show ?thesis
    unfolding DetSemiAutomaton_fin_run_alt_def detSemi_run_eq
    apply (simp add: w_in SemiAutomaton_is_fin_run_def)
    apply (metis \_is_state fin_run_rule hd_CLTS_fin_run)
  done
qed

lemma (in DetSemiAutomaton) DetSemiAutomaton_inf_run_correct :
  "SemiAutomaton_is_inf_run \ w r \<longleftrightarrow>
   (\<forall>i. w i \<in> (\<Sigma> \)) \<and> r = DetSemiAutomaton_inf_run \ (\ \) w"
proof (cases "\<forall>i. w i \<in> (\<Sigma> \)")
  case False thus ?thesis
    by (metis SemiAutomaton_\<Delta>_cons___is_inf_run(1))
next
  case True note w_in = this

  note detSemi_run_eq = DetSemiAutomaton_inf_run_alt_def[of w, OF _ \_is_state]
  note inf_run_rule = CDLTS_inf_run_correct [OF complete_deterministic_LTS, of w r]

  show ?thesis
    apply (simp add: w_in SemiAutomaton_is_inf_run_def detSemi_run_eq)
    apply (metis CDLTS_inf_run_correct CLTS_inf_run.simps(1) True \_is_state complete_deterministic_LTS)
  done
qed

lemma (in DetSemiAutomaton) DetSemiAutomaton_DLTS_reach_is_some :
  "\<lbrakk>q \<in> \<Q> \; w \<in> lists (\<Sigma> \)\<rbrakk> \<Longrightarrow> \<not> (DLTS_reach (\<delta> \) q w = None)"
by (simp add: DetSemiAutomaton_reach_is_none_iff)

lemma (in DetSemiAutomaton) DetSemiAutomaton_DLTS_reach_wf : "\<lbrakk> q \<in> \<Q> \; DLTS_reach (\<delta> \) q w = Some q'\<rbrakk> \<Longrightarrow>  q' \<in> \<Q> \ \<and>
w \<in> lists (\<Sigma> \)"
by (simp add: DetSemiAutomaton_LTS_is_reachable_DLTS_reach_simp [symmetric] SemiAutomaton_\<Delta>_cons___LTS_is_reachable)

subsection \<open> Constructing from a list representation \<close>

fun construct_SemiAutomaton_aux where
   "construct_SemiAutomaton_aux \ (q1, l, q2) =
    \<lparr> \<Q>=insert q1 (insert q2 (\<Q> \)),
      \<Sigma>=\<Sigma> \ \<union> set l,
      \<Delta>=\<Delta> \ \<union> { (q1,a,q2) | a. a \<in> set l},
      \ = \ \\<rparr>"

fun SemiAutomaton_construct where
   "SemiAutomaton_construct (Q, A, D, I) =
    foldl construct_SemiAutomaton_aux
    \<lparr> \<Q>=set (Q @ I), \<Sigma>=set A, \<Delta>={}, \ =set I \<rparr> D"
declare SemiAutomaton_construct.simps [simp del]

(* TODO: Move to Misc.thy *)
lemma foldl_fun_comm:
  assumes "\<And>x y s. f (f s x) y = f (f s y) x"
  shows "f (foldl f s xs) x = foldl f (f s x) xs"
  by (induct xs arbitrary: s)
    (simp_all add: assms)

lemma SemiAutomaton_construct_alt_def :
  "SemiAutomaton_construct (Q, A, D, I) =
   \<lparr> \<Q>=set Q \<union> set I \<union>
       set (map fst D) \<union>
       set (map (snd \<circ> snd) D), \<Sigma>=set A \<union> \<Union> (set (map (set \<circ> fst \<circ> snd) D)),
       \<Delta> = { (q1,a,q2) . (\<exists>l. (q1, l, q2) \<in> set D \<and> a \<in> set l)}, \ = set I\<rparr>"
proof (induct D)
  case Nil thus ?case by (auto simp add: SemiAutomaton_construct.simps)
next
  case (Cons qlq D)

  have fold_lemma: "\<And>\. foldl construct_SemiAutomaton_aux (construct_SemiAutomaton_aux \ qlq) D =
            construct_SemiAutomaton_aux (foldl construct_SemiAutomaton_aux \ D) qlq"
    by (rule_tac foldl_fun_comm [symmetric], auto)

  obtain q1 l q2 where qlq_eq : "qlq = (q1, l, q2)" by (cases qlq, auto)

  from Cons fold_lemma show ?case
    by (simp add: SemiAutomaton_construct.simps, auto simp add: qlq_eq)
qed

fun SemiAutomaton_construct_simple where
  "SemiAutomaton_construct_simple (Q, A, D, I) =
   SemiAutomaton_construct (Q, A, map (\<lambda>(q1, a, q2). (q1, [a], q2)) D, I)"

lemma SemiAutomaton_construct___is_well_formed :
  "FinSemiAutomaton (SemiAutomaton_construct l)"
proof -
  obtain Q A D I where l_eq[simp]: "l = (Q, A, D, I)"
    using prod_cases4 by blast

  { fix q \<sigma> q'
    assume "(q, \<sigma>, q') \<in> \<Delta> (SemiAutomaton_construct (Q, A, D, I))"
    then obtain l where in_D: "(q, l, q') \<in> set D" and
                       in_l: "\<sigma> \<in> set l" by (auto simp add: SemiAutomaton_construct_alt_def)

    from in_D have p1: "q \<in> fst ` set D" by (metis fst_conv imageI)
    from in_l have p2: "\<sigma> \<in> set (fst (snd (q, l, q')))" by simp
    from in_D have p3: "q' \<in> (snd \<circ> snd) ` set D"
      by (metis imageI image_comp snd_conv)

    note p1 p3
  }
  moreover define f where "f \<equiv> \<lambda>(q::'a,l::'b list,q'::'a). {(q,a,q') | a. a \<in> set l}"
  hence  "\<Delta> (SemiAutomaton_construct (Q, A, D, I)) = \<Union>(f ` set D)"
    unfolding SemiAutomaton_construct_alt_def
    by auto
  hence "finite (\<Delta> (SemiAutomaton_construct (Q, A, D, I)))"
    by (auto simp add: f_def)

  ultimately show ?thesis
    apply (auto simp add: SemiAutomaton_construct_alt_def FinSemiAutomaton_full_def Ball_def)
    apply metis
  done
qed

lemma SemiAutomaton_construct_exists :
fixes \ :: "('q, 'a) SemiAutomaton_rec"
assumes wf_A: "FinSemiAutomaton \"
  and fin\<Sigma>: "finite (\<Sigma> \)"
shows "\<exists>Q A D I. \ = SemiAutomaton_construct (Q, A, D, I)"
proof -
  interpret SemiAutomaton_A: FinSemiAutomaton \ by (fact wf_A)
  from finite_list[OF SemiAutomaton_A.finite_\<Q>] obtain Q where "set Q = \<Q> \" .. note set_Q = this
  from finite_list[OF SemiAutomaton_A.finite_\] obtain I where "set I = \ \" .. note set_I = this
  from finite_list[OF fin\<Sigma>] obtain A where "set A = \<Sigma> \" .. note set_A = this
  from finite_list[OF SemiAutomaton_A.finite_\<Delta>] obtain DD where "set DD = \<Delta> \" .. note set_DD = this

  let ?D = "map (\<lambda>qaq. (fst qaq, [fst (snd qaq)], snd (snd qaq))) DD"

  have "\ = SemiAutomaton_construct (Q, A, ?D, I)"
    apply (rule SemiAutomaton_rec.equality)
    apply (simp_all add: SemiAutomaton_construct_alt_def set_Q set_I set_A set_DD o_def)
        apply (insert SemiAutomaton_A.\<Delta>_consistent SemiAutomaton_A.\_consistent) []
        apply auto[]
      apply (insert SemiAutomaton_A.\<Delta>_consistent) []
      apply auto []
    apply (simp del: ex_simps add: image_iff Bex_def ex_simps[symmetric])
  done
  thus ?thesis by blast
qed

subsection \<open> Removing states \<close>

definition SemiAutomaton_remove_states :: "('q, 'a, 'x) SemiAutomaton_rec_scheme \<Rightarrow> 'q set \<Rightarrow> ('q, 'a) SemiAutomaton_rec" where
"SemiAutomaton_remove_states \ S == \<lparr> \<Q>=\<Q> \ - S, \<Sigma>=\<Sigma> \, \<Delta> = { (s1,a,s2) . (s1,a,s2) \<in> \<Delta> \ \<and> s1 \<notin> S \<and> s2 \<notin> S}, \ = \ \ - S \<rparr>"

lemma [simp] : "\ (SemiAutomaton_remove_states \ S) = \ \ - S" by (simp add: SemiAutomaton_remove_states_def)
lemma [simp] : "\<Q> (SemiAutomaton_remove_states \ S) = \<Q> \ - S" by (simp add: SemiAutomaton_remove_states_def)
lemma [simp] : "\<Sigma> (SemiAutomaton_remove_states \ S) = \<Sigma> \" by (simp add: SemiAutomaton_remove_states_def)
lemma [simp] : "x \<in> \<Delta> (SemiAutomaton_remove_states \ S) \<longleftrightarrow>
                x \<in> \<Delta> \ \<and> fst x \<notin> S \<and> snd (snd x) \<notin> S" by (cases x, simp add: SemiAutomaton_remove_states_def)

lemma SemiAutomaton_remove_states_\<Delta>_subset : "\<Delta> (SemiAutomaton_remove_states \ S) \<subseteq> \<Delta> \" by (simp add: subset_iff)
lemma SemiAutomaton_remove_states___is_well_formed : "SemiAutomaton \ \<Longrightarrow> SemiAutomaton (SemiAutomaton_remove_states \ S)" by (auto simp add: SemiAutomaton_def)

lemma SemiAutomaton_remove_states_empty [simp] : "SemiAutomaton_remove_states \ {} = \"
  by (rule SemiAutomaton_rec.equality, simp_all add: SemiAutomaton_remove_states_def)

lemma SemiAutomaton_remove_states_SemiAutomaton_remove_states [simp] : "SemiAutomaton_remove_states (SemiAutomaton_remove_states \ S1) S2 =
  SemiAutomaton_remove_states \ (S1 \<union> S2)"
  by (rule SemiAutomaton_rec.equality, auto simp add: SemiAutomaton_remove_states_def)

lemma SemiAutomaton_remove_states_fin_run_LTS_impl :
  "LTS_is_fin_run (\<Delta> (SemiAutomaton_remove_states \ S)) w r \<Longrightarrow>
   LTS_is_fin_run (\<Delta> \) w r"
  by (simp add: LTS_is_fin_run_def)

lemma SemiAutomaton_remove_states_reachable_impl :
  "LTS_is_reachable (\<Delta> (SemiAutomaton_remove_states \ S)) q1 w q2 \<Longrightarrow>
   LTS_is_reachable (\<Delta> \) q1 w q2"
  unfolding LTS_is_reachable_alt_def
  by (metis SemiAutomaton_remove_states_fin_run_LTS_impl)

lemma SemiAutomaton_remove_states_inf_run_LTS_impl :
  "LTS_is_inf_run (\<Delta> (SemiAutomaton_remove_states \ S)) w r \<Longrightarrow>
   LTS_is_inf_run (\<Delta> \) w r"
  by (simp add: LTS_is_inf_run_def)

lemma SemiAutomaton_remove_states_fin_run_LTS_eq :
assumes Q_unreach: "\<And>q'. q'\<in>Q \<Longrightarrow> LTS_is_unreachable (\<Delta> \) (hd r) q'"
shows "LTS_is_fin_run (\<Delta> (SemiAutomaton_remove_states \ Q)) w r =
       LTS_is_fin_run (\<Delta> \) w r"
      (is "?ls = ?rs")
proof (rule iffI)
  assume ?ls thus ?rs by (rule SemiAutomaton_remove_states_fin_run_LTS_impl)
next
  assume rs: ?rs
  with Q_unreach show ?ls
  proof (induct w arbitrary: r)
    case Nil thus ?case by simp
  next
    case (Cons a w')
    note indhyp = Cons(1)
    note Q_unreach = Cons(2)
    note inf_run = Cons(3)

    from inf_run obtain r1 r2 r' where r_eq[simp]: "r = r1 # r2 # r'"
      by (cases r, simp, rename_tac qq r'', case_tac r'', auto)
    have hd_r_eq: "hd r = r1" by simp

    from inf_run have step: "(r1, a, r2) \<in> \<Delta> \" and
         inf_run': "LTS_is_fin_run (\<Delta> \) w' (r2 # r')" by simp_all

    have Q_unreach': "\<And>q'. q' \<in> Q \<Longrightarrow> LTS_is_unreachable (\<Delta> \) (hd (r2 # r')) q'"
    proof -
      fix q'
      assume "q' \<in> Q"

      from step have "LTS_is_reachable (\<Delta> \) r1 [a] r2" by simp
      with Q_unreach[OF `q' \<in> Q`, unfolded hd_r_eq] show "LTS_is_unreachable (\<Delta> \) (hd (r2 # r')) q'"
        by (simp, rule LTS_is_unreachable_reachable_start)
    qed

    from Q_unreach[of r1] have q_nin_Q: "r1 \<notin> Q" by auto
    from Q_unreach'[of r2] have r1_nin_Q: "r2 \<notin> Q" by auto

    from indhyp[OF Q_unreach' inf_run']
    show ?case by (simp add: q_nin_Q r1_nin_Q step)
  qed
qed

lemma SemiAutomaton_remove_states_inf_run_LTS_eq :
assumes Q_unreach: "\<And>q'. q'\<in>Q \<Longrightarrow> LTS_is_unreachable (\<Delta> \) (r 0) q'"
shows "LTS_is_inf_run (\<Delta> (SemiAutomaton_remove_states \ Q)) w r =
       LTS_is_inf_run (\<Delta> \) w r"
      (is "?ls = ?rs")
proof (rule iffI)
  assume ?ls thus ?rs by (rule SemiAutomaton_remove_states_inf_run_LTS_impl)
next
  assume rs: ?rs

  have "\<And>i. r i \<notin> Q"
  proof -
    fix i
    from LTS_is_inf_run___is_unreachable[OF rs, of 0 i]
    have "\<not> LTS_is_unreachable (\<Delta> \) (r 0) (r i)" by simp
    with Q_unreach show "r i \<notin> Q" by auto
  qed
  with rs show ?ls
    unfolding LTS_is_inf_run_def by simp
qed

lemma SemiAutomaton_remove_states_reachable_eq :
assumes Q_unreach: "\<And>q'. q'\<in>Q \<Longrightarrow> LTS_is_unreachable (\<Delta> \) q1 q'"
shows "LTS_is_reachable (\<Delta> (SemiAutomaton_remove_states \ Q)) q1 w q2 \<longleftrightarrow>
   LTS_is_reachable (\<Delta> \) q1 w q2"
    unfolding LTS_is_reachable_alt_def
by (metis Q_unreach SemiAutomaton_remove_states_fin_run_LTS_eq)

definition SemiAutomaton_unreachable_states where
  "SemiAutomaton_unreachable_states \ = {q'. \<forall>q\<in>\ \. LTS_is_unreachable (\<Delta> \) q q'}"

lemma SemiAutomaton_unreachable_states_extend :
  "\<lbrakk>x \<notin> SemiAutomaton_unreachable_states \; (x, \<sigma>, q') \<in> \<Delta> \\<rbrakk> \<Longrightarrow> q' \<notin> SemiAutomaton_unreachable_states \"
proof
  assume "x \<notin> SemiAutomaton_unreachable_states \"
  then obtain w q where
     reach_x: "LTS_is_reachable (\<Delta> \) q w x" and
     q_in_I: "q \<in> \ \"
  by (auto simp add: SemiAutomaton_unreachable_states_def LTS_is_unreachable_def)

  assume "(x, \<sigma>, q') \<in> \<Delta> \"
  with reach_x have reach_q' : "LTS_is_reachable (\<Delta> \) q (w @ [\<sigma>]) q'" by auto

  assume "q' \<in> SemiAutomaton_unreachable_states \"
  thus "False"
    by (simp add: SemiAutomaton_unreachable_states_def LTS_is_unreachable_def,
        metis q_in_I reach_q')
qed

definition SemiAutomaton_is_initially_connected where
  "SemiAutomaton_is_initially_connected \ \<longleftrightarrow> \<Q> \ \<inter> SemiAutomaton_unreachable_states \ = {}"

lemma SemiAutomaton_is_initially_connected_truncate [simp]:
  "SemiAutomaton_is_initially_connected (SemiAutomaton.truncate \) \<longleftrightarrow>
   SemiAutomaton_is_initially_connected \"
unfolding SemiAutomaton_is_initially_connected_def
          SemiAutomaton_unreachable_states_def
by (simp add: SemiAutomaton_rec.defs)

lemma SemiAutomaton_is_initially_connected_alt_def :
  "SemiAutomaton_is_initially_connected \ \<longleftrightarrow> (\<forall>q \<in> \<Q> \.
   \<exists>i\<in>\ \. \<exists>w. LTS_is_reachable (\<Delta> \) i w q)"
unfolding SemiAutomaton_is_initially_connected_def
by (auto simp add: set_eq_iff SemiAutomaton_unreachable_states_def LTS_is_unreachable_def)

lemma SemiAutomaton_not_in_unreachable_states_run :
  "q \<notin> SemiAutomaton_unreachable_states \ \<longleftrightarrow>
   (\<exists>w r. SemiAutomaton_is_fin_run \ w r \<and> last r = q)"
unfolding SemiAutomaton_unreachable_states_def LTS_is_unreachable_def LTS_is_reachable_alt_def
          SemiAutomaton_is_fin_run_def
by auto

definition SemiAutomaton_remove_unreachable_states where
  "SemiAutomaton_remove_unreachable_states \ = SemiAutomaton_remove_states \ (SemiAutomaton_unreachable_states \)"

lemma SemiAutomaton_remove_unreachable_states___is_well_formed [simp] :
"SemiAutomaton \ \<Longrightarrow> SemiAutomaton (SemiAutomaton_remove_unreachable_states \)"
by (simp add: SemiAutomaton_remove_unreachable_states_def SemiAutomaton_remove_states___is_well_formed)

lemma SemiAutomaton_unreachable_states_\ :
  "q \<in> \ \ \<Longrightarrow> q \<notin> SemiAutomaton_unreachable_states \"
proof -
   have f1: "LTS_is_reachable (\<Delta> \) q [] q \<and> [] \<in> lists (\<Sigma> \)" by simp
   show "q \<in> \ \ \<Longrightarrow> q \<notin> SemiAutomaton_unreachable_states \"
     by (simp add: SemiAutomaton_unreachable_states_def LTS_is_unreachable_def, metis f1)
qed

lemma SemiAutomaton_remove_unreachable_states_\ [simp] :
  "\ (SemiAutomaton_remove_unreachable_states \) = \ \"
by (auto simp add: SemiAutomaton_remove_unreachable_states_def SemiAutomaton_unreachable_states_\)

lemma SemiAutomaton_remove_unreachable_states_\<Sigma> [simp] : "\<Sigma> (SemiAutomaton_remove_unreachable_states \) = \<Sigma> \"
by (simp add: SemiAutomaton_remove_unreachable_states_def)

lemma SemiAutomaton_remove_unreachable_states_fin_run_eq [simp] :
  "SemiAutomaton_is_fin_run (SemiAutomaton_remove_unreachable_states \) w r \<longleftrightarrow>
   SemiAutomaton_is_fin_run \ w r"
proof -
  have I_prop: "\ (SemiAutomaton_remove_unreachable_states \) = \ \" by simp

  { fix q'
    assume "q' \<in> SemiAutomaton_unreachable_states \" "hd r \<in> \ \"
    hence "LTS_is_unreachable (\<Delta> \) (hd r) q'"
      unfolding SemiAutomaton_unreachable_states_def by simp
  } note hd_r_unreach = this

  from SemiAutomaton_remove_states_fin_run_LTS_eq [of
     "SemiAutomaton_unreachable_states \" \ r w, OF hd_r_unreach]
  show ?thesis
    unfolding SemiAutomaton_is_fin_run_def
    by (metis I_prop SemiAutomaton_remove_unreachable_states_def)
qed

lemma SemiAutomaton_remove_unreachable_states_inf_run_eq [simp] :
  "SemiAutomaton_is_inf_run (SemiAutomaton_remove_unreachable_states \) w r \<longleftrightarrow>
   SemiAutomaton_is_inf_run \ w r"
proof -
  have I_prop: "\ (SemiAutomaton_remove_unreachable_states \) = \ \" by simp

  { fix q'
    assume "q' \<in> SemiAutomaton_unreachable_states \" "r 0 \<in> \ \"
    hence "LTS_is_unreachable (\<Delta> \) (r 0) q'"
      unfolding SemiAutomaton_unreachable_states_def by simp
  } note r0_unreach = this
  from SemiAutomaton_remove_states_inf_run_LTS_eq [of
     "SemiAutomaton_unreachable_states \" \ r w, OF r0_unreach]
  show ?thesis
    unfolding SemiAutomaton_is_inf_run_def
    by (metis I_prop SemiAutomaton_remove_unreachable_states_def)
qed

lemma SemiAutomaton_unreachable_states_SemiAutomaton_remove_unreachable_states :
  "SemiAutomaton_unreachable_states (SemiAutomaton_remove_unreachable_states \) =
   SemiAutomaton_unreachable_states \"
proof -
  have "\<And>q q' w. q \<in> \ \ \<Longrightarrow>
        LTS_is_reachable (\<Delta> (SemiAutomaton_remove_unreachable_states \)) q w q' \<longleftrightarrow>
        LTS_is_reachable (\<Delta> \) q w q'"
  proof -
    fix q q' w
    assume q_in_I: "q \<in> \ \"

    with q_in_I have
      Q_OK: "\<And>q'. q' \<in> SemiAutomaton_unreachable_states \ \<Longrightarrow> LTS_is_unreachable (\<Delta> \) q q'"
      by (simp add: SemiAutomaton_unreachable_states_def)

    note SemiAutomaton_remove_states_reachable_eq [of "SemiAutomaton_unreachable_states \" \ q w q']
    with Q_OK show "LTS_is_reachable (\<Delta> (SemiAutomaton_remove_unreachable_states \)) q w q' \<longleftrightarrow>
          LTS_is_reachable (\<Delta> \) q w q'"
      by (simp add: SemiAutomaton_remove_unreachable_states_def)
  qed
  thus ?thesis by (simp add: SemiAutomaton_unreachable_states_def LTS_is_unreachable_def)
qed

lemma SemiAutomaton_remove_unreachable_states_SemiAutomaton_remove_unreachable_states [simp] :
fixes \ :: "('q, 'a, _) SemiAutomaton_rec_scheme"
shows  "SemiAutomaton_remove_unreachable_states (SemiAutomaton_remove_unreachable_states \) =
        SemiAutomaton_remove_unreachable_states \"
apply (simp add: SemiAutomaton_remove_unreachable_states_def)
apply (simp add: SemiAutomaton_remove_unreachable_states_def [symmetric]
                 SemiAutomaton_unreachable_states_SemiAutomaton_remove_unreachable_states)
done

lemma SemiAutomaton_remove_unreachable_states___SemiAutomaton_is_initially_connected :
  "SemiAutomaton_is_initially_connected (SemiAutomaton_remove_unreachable_states \)"
apply (simp add: SemiAutomaton_is_initially_connected_def SemiAutomaton_unreachable_states_SemiAutomaton_remove_unreachable_states)
apply (simp add: SemiAutomaton_remove_unreachable_states_def)
apply fastforce
done

definition SemiAutomaton_reachable_states where
  "SemiAutomaton_reachable_states \ = \<Q> \ - SemiAutomaton_unreachable_states \"

lemma (in SemiAutomaton) SemiAutomaton_reachable_states_run :
  "SemiAutomaton_reachable_states \ = {q | q w r. SemiAutomaton_is_fin_run \ w r \<and> last r = q}"
unfolding SemiAutomaton_reachable_states_def
apply (auto simp add: SemiAutomaton_not_in_unreachable_states_run set_eq_iff)
apply (metis LTS_is_reachable_alt_def SemiAutomaton_\<Delta>_cons___LTS_is_reachable SemiAutomaton_is_fin_run_def \_consistent subsetD)
done

lemma (in SemiAutomaton) SemiAutomaton_reachable_states_reachable :
  "SemiAutomaton_reachable_states \ = {q . \<exists>i w. i \<in> \ \ \<and> LTS_is_reachable (\<Delta> \) i w q}"
unfolding SemiAutomaton_reachable_states_run SemiAutomaton_is_fin_run_def LTS_is_reachable_alt_def
by auto

lemma (in SemiAutomaton) SemiAutomaton_in_reachable_states_eq :
  "q \<in> SemiAutomaton_reachable_states \ \<longleftrightarrow>
   q \<notin> SemiAutomaton_unreachable_states \"
unfolding SemiAutomaton_reachable_states_run SemiAutomaton_not_in_unreachable_states_run
by simp

lemmas (in SemiAutomaton) SemiAutomaton_not_in_unreachable_states_eq = SemiAutomaton_in_reachable_states_eq[symmetric]

lemma (in SemiAutomaton) SemiAutomaton_remove_unreachable_states_alt_def :
  "SemiAutomaton_remove_unreachable_states \ =
   \<lparr>\<Q> = SemiAutomaton_reachable_states \, \<Sigma> = \<Sigma> \,
    \<Delta> = {(s1, a, s2).
            (s1, a, s2) \<in> \<Delta> \ \<and>
            s1 \<in> SemiAutomaton_reachable_states \ \<and>
            s2 \<in> SemiAutomaton_reachable_states \},
    \ = \ \ \<inter> SemiAutomaton_reachable_states \\<rparr>"
unfolding SemiAutomaton_remove_unreachable_states_def SemiAutomaton_remove_states_def
          SemiAutomaton_not_in_unreachable_states_eq
          SemiAutomaton_reachable_states_def[symmetric]
using \_consistent
by (simp add: set_eq_iff SemiAutomaton_not_in_unreachable_states_eq)

subsection \<open> Rename States / Combining \<close>

definition SemiAutomaton_rename_states_ext ::
"(('q1 \<Rightarrow> 'q2) \<Rightarrow> ('q1, 'a, 'more1) SemiAutomaton_rec_scheme \<Rightarrow> 'more2) \<Rightarrow>
 ('q1, 'a, 'more1) SemiAutomaton_rec_scheme \<Rightarrow>
 ('q1 \<Rightarrow> 'q2) \<Rightarrow> ('q2, 'a, 'more2) SemiAutomaton_rec_scheme" where
"SemiAutomaton_rename_states_ext more_f \ f \<equiv>
\<lparr> \<Q>=f ` (\<Q> \), \<Sigma>=\<Sigma> \, \<Delta> = { (f s1, a, f s2) | s1 a s2. (s1,a,s2) \<in> \<Delta> \},
  \=f ` (\ \), \<dots> = more_f f \ \<rparr>"

abbreviation "SemiAutomaton_rename_states \<equiv> SemiAutomaton_rename_states_ext (\<lambda>_ _. ())"

lemma SemiAutomaton_rename_states_ext_\ [simp] : "\ (SemiAutomaton_rename_states_ext more_f \ f) = f ` \ \" by (simp add: SemiAutomaton_rename_states_ext_def)
lemma SemiAutomaton_rename_states_ext_\<Q> [simp] : "\<Q> (SemiAutomaton_rename_states_ext more_f \ f) = f ` \<Q> \" by (simp add: SemiAutomaton_rename_states_ext_def)
lemma SemiAutomaton_rename_states_ext_\<Sigma> [simp] : "\<Sigma> (SemiAutomaton_rename_states_ext more_f \ f) = \<Sigma> \" by (simp add: SemiAutomaton_rename_states_ext_def)
lemma SemiAutomaton_rename_states_ext_\<Delta> [simp] : "(fq, \<sigma>, fq') \<in> \<Delta> (SemiAutomaton_rename_states_ext more_f \ f) \<longleftrightarrow>
                (\<exists>q q'. (q, \<sigma>, q') \<in> \<Delta> \ \<and> (fq = f q) \<and> (fq' = f q'))"
  by (auto simp add: SemiAutomaton_rename_states_ext_def)
lemma SemiAutomaton_rename_states_ext_more [simp] : "more (SemiAutomaton_rename_states_ext more_f \ f) = more_f f \" by (simp add: SemiAutomaton_rename_states_ext_def)

lemma SemiAutomaton_rename_states_ext__finite_\<Delta>:
  assumes inj: "inj_on f (\<Q> \)"
  and wf: "SemiAutomaton \"
  shows "finite (\<Delta> \) \<longleftrightarrow> finite (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f))"
proof -
  define \<Delta>_f where "\<Delta>_f \<equiv> \<lambda>(q,a::'c,q'). (f q, a, f q')"
  with inj SemiAutomaton.\<Delta>_consistent[OF wf] have inj\<Delta>: "inj_on \<Delta>_f (\<Delta> \)" by (auto simp: inj_on_def)

  have \<Delta>_eq: "\<Delta> (SemiAutomaton_rename_states_ext more_f \ f) = \<Delta>_f ` \<Delta> \"
    unfolding SemiAutomaton_rename_states_ext_def image_def
    by (fastforce split: prod.splits simp: \<Delta>_f_def)

  from inj\<Delta> show ?thesis
    unfolding \<Delta>_eq
    by (auto intro: finite_imageD)
qed

lemma SemiAutomaton_rename_states___is_well_formed :
 "SemiAutomaton \ \<Longrightarrow> SemiAutomaton (SemiAutomaton_rename_states_ext more_f \ f)"
by (auto simp add: SemiAutomaton_def image_iff Bex_def)

lemma SemiAutomaton_rename_states_ext_id[simp] :
  "SemiAutomaton_rename_states_ext more_f \ id = \ \<longleftrightarrow> more_f id \ = (more \)"
  by (cases \, auto simp add: SemiAutomaton_rename_states_ext_def)

lemma SemiAutomaton_rename_states_ext_truncate :
  "SemiAutomaton_rename_states_ext more_f \1 f = \2 \<Longrightarrow>
   SemiAutomaton_rename_states (SemiAutomaton.truncate \1) f = SemiAutomaton.truncate \2"
unfolding SemiAutomaton_rename_states_ext_def
by (auto simp add: SemiAutomaton.truncate_def)  metis

lemma SemiAutomaton_rename_states_ext_truncate_sym :
  "\2 = SemiAutomaton_rename_states_ext more_f \1 f \<Longrightarrow>
   SemiAutomaton.truncate \2 = SemiAutomaton_rename_states (SemiAutomaton.truncate \1) f"
by (metis SemiAutomaton_rename_states_ext_truncate)

lemma SemiAutomaton_rename_states_SemiAutomaton_rename_states [simp] :
   "SemiAutomaton_rename_states (SemiAutomaton_rename_states \ f1) f2 =
    SemiAutomaton_rename_states \ (f2 \<circ> f1)"
by (auto simp add: SemiAutomaton_rename_states_ext_def image_comp, metis)

lemma SemiAutomaton_rename_states_ext_SemiAutomaton_rename_states_ext :
assumes more21_OK: "\<And>\'. more \' = more_f1 f1 \ \<Longrightarrow> more_f2 f2 \' = more_f21 (f2 \<circ> f1) \"
shows "SemiAutomaton_rename_states_ext more_f2 (SemiAutomaton_rename_states_ext more_f1 \ f1) f2 =
       SemiAutomaton_rename_states_ext more_f21 \ (f2 \<circ> f1)"
apply (auto simp add: SemiAutomaton_rename_states_ext_def image_comp)
apply metis
apply (rule more21_OK)
apply simp
done

lemma (in SemiAutomaton) SemiAutomaton_rename_states_ext_agree_on_Q :
assumes f12_agree: "\<And>q. q \<in> \<Q> \ \<Longrightarrow> f1 q = f2 q"
    and more_f_eq: "more_f1 f1 \ = more_f2 f2 \"
shows "SemiAutomaton_rename_states_ext more_f1 \ f1 = SemiAutomaton_rename_states_ext more_f2 \ f2"
unfolding SemiAutomaton_rename_states_ext_def
proof (rule SemiAutomaton_rec.equality, simp_all)
  have image_Q: "\<And>Q. Q \<subseteq> \<Q> \ \<Longrightarrow> f1 ` Q = f2 ` Q"
    using f12_agree
    by (simp add: subset_iff set_eq_iff image_iff)

  from image_Q show "f1 ` (\<Q> \<A>) = f2 ` (\<Q> \)" by simp
  from \_consistent image_Q show "f1 ` (\<I> \<A>) = f2 ` (\ \)" by simp
next
  from \<Delta>_consistent f12_agree
  show "{(f1 s1, a, f1 s2) |s1 a s2. (s1, a, s2) \<in> \<Delta> \} =
        {(f2 s1, a, f2 s2) |s1 a s2. (s1, a, s2) \<in> \<Delta> \}"
    by (simp add: set_eq_iff) metis
next
  show "more_f1 f1 \ = more_f2 f2 \" by fact
qed

lemma LTS_is_reachable___SemiAutomaton_rename_statesE :
 "LTS_is_reachable (\<Delta> \) q w q' \<Longrightarrow>
  LTS_is_reachable (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f)) (f q) w (f q')"
proof (induct w arbitrary: q)
  case Nil thus ?case by simp
next
  case (Cons \<sigma> w)
  note ind_hyp = Cons(1)
  note reach_\<sigma>w = Cons(2)

  from reach_\<sigma>w obtain q'' where
     q''_\<Delta>: "(q, \<sigma>, q'') \<in> \<Delta> \" and
     reach_w: "LTS_is_reachable (\<Delta> \) q'' w q'" by auto

  from ind_hyp reach_w
    have reach_w': "LTS_is_reachable (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f)) (f q'') w (f q')"
    by blast

  from q''_\<Delta> have q''_\<Delta>' : "(f q, \<sigma>, f q'') \<in> (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f))" by auto

  from q''_\<Delta>' reach_w' show ?case by auto
qed

lemma SemiAutomaton_rename_states_ext_fin_run_LTS :
  "LTS_is_fin_run (\<Delta> \) w r \<Longrightarrow>
   LTS_is_fin_run (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f)) w (map f r)"
  by (simp add: LTS_is_fin_run_def) metis

lemma SemiAutomaton_rename_states_ext_fin_run :
  "SemiAutomaton_is_fin_run \ w r \<Longrightarrow>
   SemiAutomaton_is_fin_run (SemiAutomaton_rename_states_ext more_f \ f) w (map f r)"
  apply (simp add: SemiAutomaton_is_fin_run_def SemiAutomaton_rename_states_ext_fin_run_LTS)
  apply (cases r)
  apply (auto)
done

lemma SemiAutomaton_rename_states_ext_inf_run_LTS :
  "LTS_is_inf_run (\<Delta> \) w r \<Longrightarrow>
   LTS_is_inf_run (\<Delta> (SemiAutomaton_rename_states_ext more_f \ f)) w (f \<circ> r)"
  by (simp add: LTS_is_inf_run_def) metis

lemma SemiAutomaton_rename_states_ext_inf_run :
  "SemiAutomaton_is_inf_run \ w r \<Longrightarrow>
   SemiAutomaton_is_inf_run (SemiAutomaton_rename_states_ext more_f \ f) w (f \<circ> r)"
  by (simp add: SemiAutomaton_is_inf_run_def SemiAutomaton_rename_states_ext_inf_run_LTS)

lemma (in DetSemiAutomaton) SemiAutomaton_is_complete_deterministic___inj_rename :
assumes inj_f: "inj_on f (\<Q> \)"
shows "SemiAutomaton_is_complete_deterministic (SemiAutomaton_rename_states_ext more_f \ f)"
proof -
  have step1:
    "\<And>\<sigma> s1 s1' s2 s2'.
       \<lbrakk>f s1 = f s1'; (s1, \<sigma>, s2) \<in> \<Delta> \; (s1', \<sigma>, s2') \<in> \<Delta> \\<rbrakk> \<Longrightarrow> f s2 = f s2'"
  proof -
    fix \<sigma> s1 s1' s2 s2'
    assume in_D: "(s1, \<sigma>, s2) \<in> \<Delta> \" and in_D': "(s1', \<sigma>, s2') \<in> \<Delta> \"

    from \<Delta>_consistent in_D have s1_in: "s1 \<in> \<Q> \" by fast
    from \<Delta>_consistent in_D' have s1'_in: "s1' \<in> \<Q> \" by fast

    assume "f s1 = f s1'"
    with s1_in s1'_in inj_f have s1_eq: "s1 = s1'"
      unfolding inj_on_def by simp

    from s1_eq in_D in_D' deterministic_LTS
    have "s2 = s2'"
      by (auto simp add: LTS_is_deterministic_def)
    thus "f s2 = f s2'" by simp
  qed

  show "SemiAutomaton_is_complete_deterministic (SemiAutomaton_rename_states_ext more_f \ f)"
    apply (simp add: SemiAutomaton_is_complete_deterministic_def LTS_is_complete_deterministic_def
       SemiAutomaton_rename_states_ext_def LTS_is_deterministic_def LTS_is_complete_def)
    apply (rule conjI)
      apply (metis step1)
    apply (metis complete_LTS[unfolded LTS_is_complete_def])
  done
qed

subsection \<open> Isomorphy \<close>

definition SemiAutomaton_isomorphic_ext where
  "SemiAutomaton_isomorphic_ext more_f \1 \2 \<equiv>
   (\<exists>f. inj_on f (\<Q> \1) \<and> \2 = SemiAutomaton_rename_states_ext more_f \1 f)"

abbreviation SemiAutomaton_isomorphic where
  "SemiAutomaton_isomorphic \<equiv> SemiAutomaton_isomorphic_ext (\<lambda>_ _. ())"

lemma SemiAutomaton_isomorphic_ext_truncate :
  "SemiAutomaton_isomorphic_ext more_f \1 \2 \<Longrightarrow>
   SemiAutomaton_isomorphic (SemiAutomaton_rec.truncate \1) (SemiAutomaton_rec.truncate \2)"
unfolding SemiAutomaton_isomorphic_ext_def SemiAutomaton_rename_states_ext_def
by (cases \2) (auto simp add: SemiAutomaton_rec.defs)

lemma SemiAutomaton_isomorphic_ext_truncate1 :
  "SemiAutomaton_isomorphic_ext more_f \1 \2 \<Longrightarrow>
   SemiAutomaton_isomorphic (SemiAutomaton_rec.truncate \1) \2"
unfolding SemiAutomaton_isomorphic_ext_def SemiAutomaton_rename_states_ext_def
by (cases \2) (auto simp add: SemiAutomaton_rec.defs)

lemma SemiAutomaton_isomorphic_ext_truncate2 :
  "SemiAutomaton_isomorphic_ext more_f \1 \2 \<Longrightarrow>
   SemiAutomaton_isomorphic \1 (SemiAutomaton_rec.truncate \2)"
unfolding SemiAutomaton_isomorphic_ext_def SemiAutomaton_rename_states_ext_def
by (cases \2) (auto simp add: SemiAutomaton_rec.defs)

lemma SemiAutomaton_isomorphic_ext_refl :
assumes more_f_id: "more_f id \ = more \"
shows "SemiAutomaton_isomorphic_ext more_f \ \"
proof -
  have "inj_on id (\<Q> \)" by simp
  moreover
  have "SemiAutomaton_rename_states_ext more_f \ id = \"
    by (simp add: more_f_id)
  ultimately
  show ?thesis unfolding SemiAutomaton_isomorphic_ext_def by metis
qed

lemma SemiAutomaton_isomorphic_refl [simp] :
  "SemiAutomaton_isomorphic \ \"
apply (rule SemiAutomaton_isomorphic_ext_refl)
apply (simp add: fun_eq_iff)
done

lemma SemiAutomaton_isomorphic_ext_bijections :
assumes wf_SemiAutomaton1 : "SemiAutomaton \1"
    and equiv_\1_\2: "SemiAutomaton_isomorphic_ext more_f1 \1 \2"
    and more_eq: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                             \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                             more \ = more_f1 f1 \1\<rbrakk> \<Longrightarrow>
           more \1 = more_f2 f2 \"
shows "(\<exists>f1 f2. \2 = SemiAutomaton_rename_states_ext more_f1 \1 f1 \<and>
                \1 = SemiAutomaton_rename_states_ext more_f2 \2 f2 \<and>
                (\<forall>q \<in> \<Q> \1. f2 (f1 q) = q) \<and>
                (\<forall>q \<in> \<Q> \2. f1 (f2 q) = q) \<and>
                (inj_on f1 (\<Q> \1)) \<and> (inj_on f2 (\<Q> \2)))"
proof -
  from equiv_\1_\2
  obtain f where inj_f : "inj_on f (\<Q> \1)" and
                 \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f1 \1 f"
    unfolding SemiAutomaton_isomorphic_ext_def by auto

  obtain f' where f'_def : "f' = inv_into (\<Q> \1) f" by auto
  with inj_f have f'_f: "\<And>q. q \<in> (\<Q> \1) \<Longrightarrow> f' (f q) = q" by simp

  from \2_eq have Q_A2_eq: "\<Q> \2 = f ` (\<Q> \1)"
    by (simp add: SemiAutomaton_rename_states_ext_def)
  with f'_f have inj_f' : "inj_on f' (\<Q> \2)"
    by (simp add: inj_on_def)

  have f_f': "\<And>q. q \<in> (\<Q> \2) \<Longrightarrow> f (f' q) = q"
    by (auto simp add: Q_A2_eq image_iff f'_f)

  have \1_eq : "\1 = SemiAutomaton_rename_states_ext more_f2 \2 f'"
  proof (rule SemiAutomaton_rec.equality)
    show "\<Sigma> \1 = \<Sigma> (SemiAutomaton_rename_states_ext more_f2 \2 f')"
      by (simp add: \2_eq SemiAutomaton_remove_states_def)
  next
    from f'_f
    show "\<Q> \1 = \<Q> (SemiAutomaton_rename_states_ext more_f2 \2 f')"
      by (auto simp add: \2_eq image_iff)
  next
    from wf_SemiAutomaton1 have "\ \1 \<subseteq> \<Q> \1" by (simp add: SemiAutomaton_def)
    with f'_f have "\<And>q. q \<in> (\ \1) \<Longrightarrow> f' (f q) = q" by auto
    thus "\ \1 = \ (SemiAutomaton_rename_states_ext more_f2 \2 f')"
      by (auto simp add: \2_eq image_iff)
  next
    from wf_SemiAutomaton1 f'_f
    have "\<And>q1 a q2. (q1, a, q2) \<in> (\<Delta> \1) \<Longrightarrow> f' (f q1) = q1 \<and>  f' (f q2) = q2"
       unfolding SemiAutomaton_def by auto
    thus "\<Delta> \1 = \<Delta> (SemiAutomaton_rename_states_ext more_f2 \2 f')"
      by (auto simp add: \2_eq, metis)
  next
    from \2_eq
    show "more \1 = more (SemiAutomaton_rename_states_ext more_f2 \2 f')"
      apply (simp add: SemiAutomaton_rename_states_ext_def)
      apply (rule_tac more_eq[of f' f])
      apply (auto simp add: f'_f)
    done
  qed

  show ?thesis
    apply (rule exI[where x = f])
    apply (rule exI[where x = f'])
    apply (intro conjI)
    apply fact
    apply fact
    apply (simp add: f'_f)
    apply (simp add: f_f')
    apply fact
    apply fact
  done
qed

lemma SemiAutomaton_isomorphic_bijections :
assumes wf_SemiAutomaton1 : "SemiAutomaton \1"
    and iso_\1_\2: "SemiAutomaton_isomorphic \1 \2"
shows "(\<exists>f1 f2. \2 = SemiAutomaton_rename_states \1 f1 \<and>
                \1 = SemiAutomaton_rename_states \2 f2 \<and>
                (\<forall>q \<in> \<Q> \1. f2 (f1 q) = q) \<and>
                (\<forall>q \<in> \<Q> \2. f1 (f2 q) = q) \<and>
                (inj_on f1 (\<Q> \1)) \<and> (inj_on f2 (\<Q> \2)))"
apply (rule SemiAutomaton_isomorphic_ext_bijections)
apply (simp_all add: fun_eq_iff wf_SemiAutomaton1 iso_\1_\2)
done

lemma SemiAutomaton_isomorphic_ext_sym_impl :
assumes wf_SemiAutomaton1 : "SemiAutomaton \1"
    and equiv_\1_\2: "SemiAutomaton_isomorphic_ext more_f1 \1 \2"
    and more_eq: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                             \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                             more \ = more_f1 f1 \1\<rbrakk> \<Longrightarrow>
           more \1 = more_f2 f2 \"
shows "SemiAutomaton_isomorphic_ext more_f2 \2 \1"
using SemiAutomaton_isomorphic_ext_bijections [OF wf_SemiAutomaton1 equiv_\1_\2, of more_f2] more_eq
unfolding SemiAutomaton_isomorphic_ext_def by blast

lemma SemiAutomaton_isomorphic_sym_impl :
assumes wf_SemiAutomaton1 : "SemiAutomaton \1"
    and equiv_\1_\2: "SemiAutomaton_isomorphic \1 \2"
shows "SemiAutomaton_isomorphic \2 \1"
apply (rule SemiAutomaton_isomorphic_ext_sym_impl [OF wf_SemiAutomaton1 equiv_\1_\2])
apply simp
done

lemma SemiAutomaton_isomorphic_ext_sym :
assumes wf_SemiAutomaton1: "SemiAutomaton \1"
    and wf_SemiAutomaton2: "SemiAutomaton \2"
    and more_eq1: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                             \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                             more \ = more_f1 f1 \1\<rbrakk> \<Longrightarrow>
           more \1 = more_f2 f2 \"
    and more_eq2: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                               \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                               more \ = more_f2 f2 \2\<rbrakk> \<Longrightarrow>
           more \2 = more_f1 f1 \"
shows "SemiAutomaton_isomorphic_ext more_f1 \1 \2 \<longleftrightarrow> SemiAutomaton_isomorphic_ext more_f2 \2 \1"
using SemiAutomaton_isomorphic_ext_sym_impl[of \1 more_f1 \2 more_f2]
      SemiAutomaton_isomorphic_ext_sym_impl[of \2 more_f2 \1 more_f1]
      assms
by blast

lemma SemiAutomaton_isomorphic_sym :
assumes wf_SemiAutomaton1: "SemiAutomaton \1"
    and wf_SemiAutomaton2: "SemiAutomaton \2"
shows "SemiAutomaton_isomorphic \1 \2 \<longleftrightarrow> SemiAutomaton_isomorphic \2 \1"
using assms
by (metis SemiAutomaton_isomorphic_sym_impl)

lemma SemiAutomaton_isomorphic___implies_well_formed :
assumes wf_SemiAutomaton1: "SemiAutomaton \1"
    and equiv_\1_\2: "SemiAutomaton_isomorphic_ext more_f \1 \2"
shows "SemiAutomaton \2"
using assms
by (metis SemiAutomaton_rename_states___is_well_formed SemiAutomaton_isomorphic_ext_def)

lemma SemiAutomaton_isomorphic_ext_trans :
assumes equiv_\1_\2: "SemiAutomaton_isomorphic_ext more_f1 \1 \2"
    and equiv_\2_\3: "SemiAutomaton_isomorphic_ext more_f2 \2 \3"
assumes more21_OK: "\<And>\' f1 f2. more \' = more_f1 f1 \1 \<Longrightarrow> more_f2 f2 \' = more_f21 (f2 \<circ> f1) \1"
shows "SemiAutomaton_isomorphic_ext more_f21 \1 \3"
proof -
  from equiv_\1_\2
  obtain f1 where inj_f1 : "inj_on f1 (\<Q> \1)" and
                  \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f1 \1 f1"
    unfolding SemiAutomaton_isomorphic_ext_def by auto

  from equiv_\2_\3
  obtain f2 where inj_f2 : "inj_on f2 (\<Q> \2)" and
                  \3_eq: "\3 = SemiAutomaton_rename_states_ext more_f2 \2 f2"
    unfolding SemiAutomaton_isomorphic_ext_def by auto

  from \2_eq \3_eq have \3_eq' : "\3 = SemiAutomaton_rename_states_ext more_f21 \1 (f2 \<circ> f1)"
    apply (simp)
    apply (rule SemiAutomaton_rename_states_ext_SemiAutomaton_rename_states_ext)
    apply (rule_tac more21_OK)
    apply simp
  done

  from \2_eq have "\<Q> \2 = f1 ` (\<Q> \1)" by (simp add: SemiAutomaton_remove_states_def)
  with inj_f1 inj_f2 have f2_f1_inj: "inj_on (f2 \<circ> f1) (\<Q> \1)"
    by (simp add: inj_on_def)

  from \3_eq' f2_f1_inj show "SemiAutomaton_isomorphic_ext more_f21 \1 \3" unfolding SemiAutomaton_isomorphic_ext_def by auto
qed

lemma SemiAutomaton_isomorphic_trans :
assumes equiv_\1_\2: "SemiAutomaton_isomorphic \1 \2"
    and equiv_\2_\3: "SemiAutomaton_isomorphic \2 \3"
shows "SemiAutomaton_isomorphic \1 \3"
apply (rule SemiAutomaton_isomorphic_ext_trans[OF equiv_\1_\2 equiv_\2_\3])
apply simp
done

text \<open> Normaly, one is interested in only well-formed automata. This simplifies
        reasoning about isomorphy. \<close>
definition SemiAutomaton_isomorphic_wf_ext where
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<equiv> SemiAutomaton_isomorphic_ext more_f \1 \2 \<and> SemiAutomaton \1"
abbreviation "SemiAutomaton_isomorphic_wf \<equiv> SemiAutomaton_isomorphic_wf_ext (\<lambda>_ _. ())"

definition SemiAutomaton_isomorphic_wf_fin_ext where
  "SemiAutomaton_isomorphic_wf_fin_ext more_f \1 \2 \<equiv> SemiAutomaton_isomorphic_ext more_f \1 \2 \<and> FinSemiAutomaton \1"
abbreviation "SemiAutomaton_isomorphic_wf_fin \<equiv> SemiAutomaton_isomorphic_wf_fin_ext (\<lambda>_ _. ())"

lemma SemiAutomaton_isomorphic_wf_\<Sigma> :
assumes equiv: "SemiAutomaton_isomorphic_wf_ext more_f \1 \2"
shows "\<Sigma> \1 = \<Sigma> \2"
proof -
  from equiv
  obtain f where \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f \1 f"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  with \2_eq show ?thesis by simp
qed

lemma SemiAutomaton_isomorphic_wf_ext_alt_def :
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<longleftrightarrow>
   SemiAutomaton_isomorphic_ext more_f \1 \2 \<and> SemiAutomaton \1 \<and> SemiAutomaton \2"
unfolding SemiAutomaton_isomorphic_wf_ext_def
by (metis SemiAutomaton_isomorphic___implies_well_formed)

lemma SemiAutomaton_isomorphic_wf_alt_def :
  "SemiAutomaton_isomorphic_wf \1 \2 \<longleftrightarrow>
   SemiAutomaton_isomorphic \1 \2 \<and> SemiAutomaton \1 \<and> SemiAutomaton \2"
by (simp add: SemiAutomaton_isomorphic_wf_ext_alt_def)

lemma SemiAutomaton_isomorphic_wf_ext_truncate :
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<Longrightarrow>
   SemiAutomaton_isomorphic_wf (SemiAutomaton_rec.truncate \1) (SemiAutomaton_rec.truncate \2)"
unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def SemiAutomaton_rename_states_ext_def
apply (cases \2)
apply simp
apply (simp add: SemiAutomaton_rec.defs)
apply auto
done

lemma SemiAutomaton_isomorphic_wf_ext___fin_iff :
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<Longrightarrow>
   FinSemiAutomaton \1 \<longleftrightarrow> FinSemiAutomaton \2"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def FinSemiAutomaton_def FinSemiAutomaton_axioms_def
          SemiAutomaton_isomorphic_ext_def
using SemiAutomaton_rename_states_ext__finite_\<Delta>[where more_f = more_f]
by (auto dest: finite_imageD)

lemma SemiAutomaton_isomorphic_wf_fin_ext_alt_def :
  "SemiAutomaton_isomorphic_wf_fin_ext more_f \1 \2 =
   (SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<and> FinSemiAutomaton \1 \<and> FinSemiAutomaton \2)"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_def FinSemiAutomaton_axioms_def
by (metis FinSemiAutomaton_is_Semiautomaton SemiAutomaton_isomorphic_wf_ext___fin_iff
          SemiAutomaton_isomorphic_wf_ext_def)

lemma SemiAutomaton_isomorphic_wf_fin_alt_def :
  "SemiAutomaton_isomorphic_wf_fin \1 \2 \<longleftrightarrow>
   SemiAutomaton_isomorphic_wf \1 \2 \<and> FinSemiAutomaton \1 \<and> FinSemiAutomaton \2"
by (simp add: SemiAutomaton_isomorphic_wf_fin_ext_alt_def)

lemma SemiAutomaton_isomorphic_wf_fin_ext_truncate :
  "SemiAutomaton_isomorphic_wf_fin_ext more_f \1 \2 \<Longrightarrow>
   SemiAutomaton_isomorphic_wf_fin (SemiAutomaton_rec.truncate \1) (SemiAutomaton_rec.truncate \2)"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_alt_def
using SemiAutomaton_isomorphic_wf_ext_truncate[of more_f \1 \2]
by simp

lemma SemiAutomaton_isomorphic_wf_ext_sym :
assumes more_eq1: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                             \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                             more \ = more_f1 f1 \1\<rbrakk> \<Longrightarrow>
           more \1 = more_f2 f2 \"
    and more_eq2: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                               \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                               more \ = more_f2 f2 \2\<rbrakk> \<Longrightarrow>
           more \2 = more_f1 f1 \"
shows "SemiAutomaton_isomorphic_wf_ext more_f1 \1 \2 = SemiAutomaton_isomorphic_wf_ext more_f2 \2 \1"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def
using SemiAutomaton_isomorphic_ext_sym[of \1 \2 more_f1 more_f2] more_eq1 more_eq2
by auto blast+

lemma SemiAutomaton_isomorphic_wf_sym :
  "SemiAutomaton_isomorphic_wf \1 \2 = SemiAutomaton_isomorphic_wf \2 \1"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def
by (metis SemiAutomaton_isomorphic_sym)

lemma SemiAutomaton_isomorphic_wf_ext_fin_sym :
assumes more_eq1: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                             \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                             more \ = more_f1 f1 \1\<rbrakk> \<Longrightarrow>
           more \1 = more_f2 f2 \"
    and more_eq2: "\<And>f1 f2 \. \<lbrakk>\<And>q. q \<in> \<Q> \1 \<Longrightarrow> f2 (f1 q) = q;
                               \<And>q. q \<in> \<Q> \2 \<Longrightarrow> f1 (f2 q) = q;
                               more \ = more_f2 f2 \2\<rbrakk> \<Longrightarrow>
           more \2 = more_f1 f1 \"
shows "SemiAutomaton_isomorphic_wf_fin_ext more_f1 \1 \2 = SemiAutomaton_isomorphic_wf_fin_ext more_f2 \2 \1"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_alt_def
using SemiAutomaton_isomorphic_wf_ext_sym[of \1 \2 more_f1 more_f2] more_eq1 more_eq2
by auto blast+

lemma SemiAutomaton_isomorphic_wf_fin_sym :
  "SemiAutomaton_isomorphic_wf_fin \1 \2 = SemiAutomaton_isomorphic_wf_fin \2 \1"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_alt_def
by (metis SemiAutomaton_isomorphic_wf_sym)

lemma SemiAutomaton_isomorphic_wf_ext_trans :
assumes equiv_\1_\2: "SemiAutomaton_isomorphic_wf_ext more_f1 \1 \2"
    and equiv_\2_\3: "SemiAutomaton_isomorphic_wf_ext more_f2 \2 \3"
assumes more21_OK: "\<And>\' f1 f2. more \' = more_f1 f1 \1 \<Longrightarrow> more_f2 f2 \' = more_f21 (f2 \<circ> f1) \1"
shows "SemiAutomaton_isomorphic_wf_ext more_f21 \1 \3"
using SemiAutomaton_isomorphic_ext_trans[of more_f1 \1 \2 more_f2 \3 more_f21, OF _ _ more21_OK]
      equiv_\1_\2 equiv_\2_\3
unfolding SemiAutomaton_isomorphic_wf_ext_def
by simp

lemma SemiAutomaton_isomorphic_wf_trans :
  "\<lbrakk>SemiAutomaton_isomorphic_wf \1 \2; SemiAutomaton_isomorphic_wf \2 \3\<rbrakk> \<Longrightarrow>
   SemiAutomaton_isomorphic_wf \1 \3"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def
by (metis SemiAutomaton_isomorphic_trans)

lemma SemiAutomaton_isomorphic_wf_fin_ext_trans :
assumes equiv_\1_\2: "SemiAutomaton_isomorphic_wf_fin_ext more_f1 \1 \2"
    and equiv_\2_\3: "SemiAutomaton_isomorphic_wf_fin_ext more_f2 \2 \3"
assumes more21_OK: "\<And>\' f1 f2. more \' = more_f1 f1 \1 \<Longrightarrow> more_f2 f2 \' = more_f21 (f2 \<circ> f1) \1"
shows "SemiAutomaton_isomorphic_wf_fin_ext more_f21 \1 \3"
using SemiAutomaton_isomorphic_ext_trans[of more_f1 \1 \2 more_f2 \3 more_f21, OF _ _ more21_OK]
      equiv_\1_\2 equiv_\2_\3
unfolding SemiAutomaton_isomorphic_wf_fin_ext_def
by simp

lemma SemiAutomaton_isomorphic_wf_fin_trans :
  "\<lbrakk>SemiAutomaton_isomorphic_wf_fin \1 \2; SemiAutomaton_isomorphic_wf_fin \2 \3\<rbrakk> \<Longrightarrow>
   SemiAutomaton_isomorphic_wf_fin \1 \3"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_alt_def
by (metis SemiAutomaton_isomorphic_wf_trans)

lemma SemiAutomaton_isomorphic_wf_ext_refl :
assumes more_f_id: "more_f id \ = more \"
shows "SemiAutomaton \ \<Longrightarrow> SemiAutomaton_isomorphic_wf_ext more_f \ \"
unfolding SemiAutomaton_isomorphic_wf_ext_def
using SemiAutomaton_isomorphic_ext_refl[of more_f \, OF more_f_id] by simp

lemma SemiAutomaton_isomorphic_wf_refl :
  "SemiAutomaton \1 \<Longrightarrow> SemiAutomaton_isomorphic_wf \1 \1"
unfolding SemiAutomaton_isomorphic_wf_ext_def
by simp

lemma SemiAutomaton_isomorphic_wf_fin_ext_refl :
assumes more_f_id: "more_f id \ = more \"
shows "FinSemiAutomaton \ \<Longrightarrow> SemiAutomaton_isomorphic_wf_fin_ext more_f \ \"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_def
using SemiAutomaton_isomorphic_ext_refl[of more_f \, OF more_f_id] by simp

lemma SemiAutomaton_isomorphic_wf_fin_refl :
  "FinSemiAutomaton \1 \<Longrightarrow> SemiAutomaton_isomorphic_wf_fin \1 \1"
unfolding SemiAutomaton_isomorphic_wf_fin_ext_def
by simp

lemma SemiAutomaton_isomorphic_wf_ext_intro :
  "\<lbrakk>SemiAutomaton \1; SemiAutomaton_isomorphic_ext more_f \1 \2\<rbrakk> \<Longrightarrow>
    SemiAutomaton_isomorphic_wf_ext more_f \1 \2"
unfolding SemiAutomaton_isomorphic_wf_ext_def by simp

lemma SemiAutomaton_isomorphic_wf_ext_D :
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<Longrightarrow> SemiAutomaton \1"
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<Longrightarrow> SemiAutomaton \2"
  "SemiAutomaton_isomorphic_wf_ext more_f \1 \2 \<Longrightarrow> SemiAutomaton_isomorphic_ext more_f \1 \2"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def
by simp_all

lemma SemiAutomaton_isomorphic_wf_D :
  "SemiAutomaton_isomorphic_wf \1 \2 \<Longrightarrow> SemiAutomaton \1"
  "SemiAutomaton_isomorphic_wf \1 \2 \<Longrightarrow> SemiAutomaton \2"
  "SemiAutomaton_isomorphic_wf \1 \2 \<Longrightarrow> SemiAutomaton_isomorphic \1 \2"
  "SemiAutomaton_isomorphic_wf \1 \2 \<Longrightarrow> SemiAutomaton_isomorphic \2 \1"
unfolding SemiAutomaton_isomorphic_wf_ext_alt_def
by (simp_all, metis SemiAutomaton_isomorphic_sym)

lemma SemiAutomaton_isomorphic___SemiAutomaton_rename_states_ext :
"inj_on f (\<Q> \) \<Longrightarrow> SemiAutomaton_isomorphic_ext more_f \ (SemiAutomaton_rename_states_ext more_f \ f)"
unfolding SemiAutomaton_isomorphic_ext_def
by blast

lemma SemiAutomaton_isomorphic___SemiAutomaton_rename_states :
"inj_on f (\<Q> \) \<Longrightarrow> SemiAutomaton_isomorphic \ (SemiAutomaton_rename_states \ f)"
by (simp add: SemiAutomaton_isomorphic___SemiAutomaton_rename_states_ext)

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states_ext :
"\<lbrakk>inj_on f (\<Q> \); SemiAutomaton \\<rbrakk> \<Longrightarrow>
 SemiAutomaton_isomorphic_wf_ext more_f \ (SemiAutomaton_rename_states_ext more_f \ f)"
unfolding SemiAutomaton_isomorphic_ext_def SemiAutomaton_isomorphic_wf_ext_def
by blast

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states :
"\<lbrakk>inj_on f (\<Q> \); SemiAutomaton \\<rbrakk> \<Longrightarrow> SemiAutomaton_isomorphic_wf \ (SemiAutomaton_rename_states \ f)"
by (simp add: SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states_ext)

lemma SemiAutomaton_isomorphic_wf___rename_states_cong :
fixes \1 :: "('q1, 'a) SemiAutomaton_rec"
fixes \2 :: "('q2, 'a) SemiAutomaton_rec"
assumes inj_f1 : "inj_on f1 (\<Q> \1)" and
        inj_f2 : "inj_on f2 (\<Q> \2)" and
        equiv: "SemiAutomaton_isomorphic_wf \1 \2"
shows "SemiAutomaton_isomorphic_wf (SemiAutomaton_rename_states \1 f1) (SemiAutomaton_rename_states \2 f2)"
proof -
  note wf_\1 = SemiAutomaton_isomorphic_wf_D(1)[OF equiv]
  note wf_\2 = SemiAutomaton_isomorphic_wf_D(2)[OF equiv]

  note eq_1 = SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states [OF inj_f1 wf_\1]
  note eq_2 = SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states [OF inj_f2 wf_\2]
  from eq_1 eq_2 equiv show ?thesis
    by (metis SemiAutomaton_isomorphic_wf_trans SemiAutomaton_isomorphic_wf_sym)
qed

lemma SemiAutomaton_rename_states___unreachable_states :
assumes A2_eq: "\2 = SemiAutomaton_rename_states_ext more_f \1 f"
    and f_q_in: "f q \<in> SemiAutomaton_unreachable_states \2"
shows "q \<in> SemiAutomaton_unreachable_states \1"
proof (rule ccontr)
  assume "q \<notin> SemiAutomaton_unreachable_states \1"
  then obtain w r where
    "SemiAutomaton_is_fin_run \1 w r" "last r = q"
    by (auto simp add: SemiAutomaton_not_in_unreachable_states_run)
  with SemiAutomaton_rename_states_ext_fin_run [of \1 w r more_f f, folded A2_eq]
  have "SemiAutomaton_is_fin_run \2 w (map f r)" and "last (map f r) = f q"
    apply (simp_all, cases r, simp)
    apply hypsubst
    apply (subst last_map)
    apply simp_all
  done
  with SemiAutomaton_not_in_unreachable_states_run[of "f q" "\2"]
  have "f q \<notin> SemiAutomaton_unreachable_states \2" by auto
  with f_q_in show False by simp
qed

lemma SemiAutomaton_rename_states___unreachable_states_iff :
fixes \1 :: "('q1, 'a) SemiAutomaton_rec"
fixes \2 :: "('q2, 'a) SemiAutomaton_rec"
assumes \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f1 \1 f1" and
        \1_eq: "\1 = SemiAutomaton_rename_states_ext more_f2 \2 f2" and
        wf_\1: "SemiAutomaton \1" and
        f2_f1: "\<And>q. q\<in>\<Q> \1 \<Longrightarrow> f2 (f1 q) = q" and
        q_in: "q \<in> \<Q> \1"
shows "f1 q \<in> SemiAutomaton_unreachable_states \2 \<longleftrightarrow>
       q \<in> (SemiAutomaton_unreachable_states \1 \<inter> \<Q> \1)"
       (is "?ls \<longleftrightarrow> ?rs")
proof (rule iffI)
  assume "?ls"
  with SemiAutomaton_rename_states___unreachable_states[OF \2_eq, of q] q_in
  show "?rs" by simp
next
  assume "?rs"
  with SemiAutomaton_rename_states___unreachable_states[OF \1_eq, of "f1 q"] f2_f1[of q]
  show "?ls" by simp
qed

lemma SemiAutomaton_rename_states___SemiAutomaton_remove_unreachable_states :
fixes \1 :: "('q1, 'a) SemiAutomaton_rec"
fixes \2 :: "('q2, 'a) SemiAutomaton_rec"
assumes \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f1 \1 f1" and
        \1_eq: "\1 = SemiAutomaton_rename_states_ext more_f2 \2 f2" and
        wf_\1: "SemiAutomaton \1" and
        f2_f1: "\<And>q. q\<in>\<Q> \1 \<Longrightarrow> f2 (f1 q) = q"
shows "SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1 = (SemiAutomaton_remove_unreachable_states \2)"
proof -
  note unreach_lem = SemiAutomaton_rename_states___unreachable_states_iff[OF \2_eq \1_eq wf_\1, OF f2_f1]

  have diff_lem :
   "\<And>Q. Q \<subseteq> \<Q> \1 \<Longrightarrow>
    f1 ` Q - SemiAutomaton_unreachable_states \2 =
    f1 ` (Q - SemiAutomaton_unreachable_states \1)"
  apply (rule set_eqI)
  apply (insert unreach_lem)
  apply (auto)
  done

  show "SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1 =
        SemiAutomaton_remove_unreachable_states \2"
    proof (rule  SemiAutomaton_rec.equality)
      show "\<Sigma> (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1) =
            \<Sigma> (SemiAutomaton_remove_unreachable_states \2)"
           "SemiAutomaton.more (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1) =
            SemiAutomaton.more (SemiAutomaton_remove_unreachable_states \2)"

        unfolding SemiAutomaton_remove_unreachable_states_def \2_eq
        by simp_all
    next
      from diff_lem [of "\<Q> \1"]
      show "\<Q> (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1) =
            \<Q> (SemiAutomaton_remove_unreachable_states \2)"
        unfolding SemiAutomaton_remove_unreachable_states_def \2_eq
        by simp
    next
      from diff_lem [OF SemiAutomaton.\_consistent [OF wf_\1]]
      show "\ (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1) =
            \ (SemiAutomaton_remove_unreachable_states \2)"
        unfolding SemiAutomaton_remove_unreachable_states_def \2_eq
        by simp
    next
      from unreach_lem[unfolded \2_eq] SemiAutomaton.\<Delta>_consistent [OF wf_\1]
      have "\<And>q a q'. (q, a, q') \<in> \<Delta> \1 \<and>
                     f1 q \<notin> SemiAutomaton_unreachable_states (SemiAutomaton_rename_states_ext more_f1 \1 f1) \<and>
                     f1 q' \<notin> SemiAutomaton_unreachable_states (SemiAutomaton_rename_states_ext more_f1 \1 f1) \<longleftrightarrow>
                     (q, a, q') \<in> \<Delta> \1 \<and>
                     q \<notin> SemiAutomaton_unreachable_states \1 \<and>
                     q' \<notin> SemiAutomaton_unreachable_states \1"
        by auto
      hence "\<And>q a q'. (q, a, q') \<in> \<Delta> (SemiAutomaton_remove_unreachable_states \2) \<longleftrightarrow>
                     (q, a, q') \<in> \<Delta> (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1)"
        unfolding SemiAutomaton_remove_unreachable_states_def \2_eq
        by (simp, blast)
      thus "\<Delta> (SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1) =
            \<Delta> (SemiAutomaton_remove_unreachable_states \2)"
        by auto
    qed
qed

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_remove_unreachable_states :
fixes \1 :: "('q1, 'a) SemiAutomaton_rec"
fixes \2 :: "('q2, 'a) SemiAutomaton_rec"
assumes equiv: "SemiAutomaton_isomorphic_wf \1 \2"
shows "SemiAutomaton_isomorphic_wf (SemiAutomaton_remove_unreachable_states \1) (SemiAutomaton_remove_unreachable_states \2)"
proof -
  from equiv
  obtain f1 f2 where
     inj_f1 : "inj_on f1 (\<Q> \1)" and
     \2_eq: "\2 = SemiAutomaton_rename_states \1 f1" and
     inj_f2 : "inj_on f2 (\<Q> \2)" and
     \1_eq: "\1 = SemiAutomaton_rename_states \2 f2" and
     wf_\1: "SemiAutomaton \1" and
     wf_\2: "SemiAutomaton \2" and
     f2_f1: "\<And>q. q\<in>\<Q> \1 \<Longrightarrow> f2 (f1 q) = q" and
     f1_f2: "\<And>q. q\<in>\<Q> \2 \<Longrightarrow> f1 (f2 q) = q"
    unfolding SemiAutomaton_isomorphic_wf_alt_def
    using SemiAutomaton_isomorphic_bijections[of \1 \2]
    by blast

  have "\<Q> (SemiAutomaton_remove_unreachable_states \1) \<subseteq> \<Q> \1"
    unfolding SemiAutomaton_remove_unreachable_states_def by auto
  with inj_f1 have inj_f1' : "inj_on f1 (\<Q> (SemiAutomaton_remove_unreachable_states \1))"
    by (rule subset_inj_on)

  have \2_eq' :
    "SemiAutomaton_remove_unreachable_states \2 = SemiAutomaton_rename_states (SemiAutomaton_remove_unreachable_states \1) f1"
    using SemiAutomaton_rename_states___SemiAutomaton_remove_unreachable_states[OF \2_eq \1_eq wf_\1]
          f2_f1
    by simp

  from inj_f1' \2_eq' SemiAutomaton_remove_unreachable_states___is_well_formed[OF wf_\1]
  show ?thesis
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def
    by blast
qed

lemma SemiAutomaton_is_initially_connected___SemiAutomaton_rename_states :
assumes connected: "SemiAutomaton_is_initially_connected \"
shows "SemiAutomaton_is_initially_connected (SemiAutomaton_rename_states_ext more_f \ f)"
unfolding SemiAutomaton_is_initially_connected_alt_def
proof (intro ballI)
  fix q2
  let ?A2 = "SemiAutomaton_rename_states_ext more_f \ f"
  assume q2_in: "q2 \<in> \<Q> ?A2"
  from q2_in obtain q1 where q1_in: "q1 \<in> \<Q> \" and q2_eq: "q2 = f q1" by auto

  from connected q1_in obtain i1 w where
     i1_in: "i1 \<in> \ \" and
     reach1: "LTS_is_reachable (\<Delta> \) i1 w q1"
    unfolding SemiAutomaton_is_initially_connected_alt_def
    by blast

  have i2_in: "f i1 \<in> \ ?A2"
    using i1_in by auto

  have reach2 : "LTS_is_reachable (\<Delta> ?A2) (f i1) w (f q1)"
    using LTS_is_reachable___SemiAutomaton_rename_statesE [OF reach1, of more_f f] .

  from i2_in reach2
  show "\<exists>i\<in>\ ?A2. \<exists>w. LTS_is_reachable (\<Delta> ?A2) i w q2"
    by (simp add: Bex_def q2_eq) fastforce
qed

lemma SemiAutomaton_is_initially_connected___SemiAutomaton_isomorphic :
assumes equiv: "SemiAutomaton_isomorphic_ext more_f \1 \2"
    and connected_A1: "SemiAutomaton_is_initially_connected \1"
shows "SemiAutomaton_is_initially_connected \2"
proof -
  from equiv obtain f where \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f \1 f"
    unfolding SemiAutomaton_isomorphic_ext_def by auto

  from SemiAutomaton_is_initially_connected___SemiAutomaton_rename_states [OF connected_A1, of more_f f, folded \2_eq]
  show ?thesis .
qed

lemma SemiAutomaton_is_initially_connected___SemiAutomaton_isomorphic_wf :
fixes \1 :: "('q1, 'a) SemiAutomaton_rec"
fixes \2 :: "('q2, 'a) SemiAutomaton_rec"
assumes iso: "SemiAutomaton_isomorphic_wf_ext more_f \1 \2"
shows "SemiAutomaton_is_initially_connected \1 = SemiAutomaton_is_initially_connected \2"
proof -
  from SemiAutomaton_isomorphic_wf_ext_truncate[OF iso]
  have "SemiAutomaton_isomorphic_wf (truncate \1) (truncate \2)" by simp
  hence "SemiAutomaton_is_initially_connected (truncate \1) =
         SemiAutomaton_is_initially_connected (truncate \2)"
    by (metis SemiAutomaton_is_initially_connected___SemiAutomaton_isomorphic
              SemiAutomaton_isomorphic_wf_sym SemiAutomaton_isomorphic_wf_ext_def)
  thus ?thesis by simp
qed

lemma DetSemiAutomaton___SemiAutomaton_isomorphic :
assumes equiv: "SemiAutomaton_isomorphic_ext more_f \1 \2"
    and det_A1: "DetSemiAutomaton \1"
shows "DetSemiAutomaton \2"
proof -
  from equiv obtain f where \2_eq: "\2 = SemiAutomaton_rename_states_ext more_f \1 f"
                        and inj_f: "inj_on f (\<Q> \1)"
    unfolding SemiAutomaton_isomorphic_ext_def by auto

  from DetSemiAutomaton.SemiAutomaton_is_complete_deterministic___inj_rename [OF det_A1
          inj_f, of more_f] \2_eq
  have det_A2: "SemiAutomaton_is_complete_deterministic \2" by simp

  from SemiAutomaton_isomorphic___implies_well_formed[OF _ equiv] det_A1
  have wf_A2: "SemiAutomaton \2" unfolding DetSemiAutomaton_alt_def by simp

  from det_A2 wf_A2
  show ?thesis unfolding DetSemiAutomaton_alt_def by simp
qed

lemma DetSemiAutomaton___SemiAutomaton_isomorphic_wf :
assumes iso: "SemiAutomaton_isomorphic_wf_ext more_f \1 \2"
shows "DetSemiAutomaton \1 \<longleftrightarrow> DetSemiAutomaton \2"
proof -
  from SemiAutomaton_isomorphic_wf_ext_truncate[OF iso]
  have "SemiAutomaton_isomorphic_wf (truncate \1) (truncate \2)" by simp
  with DetSemiAutomaton___SemiAutomaton_isomorphic[of "\<lambda>_ _. ()" "truncate \1" "truncate \2"]
       DetSemiAutomaton___SemiAutomaton_isomorphic[of "\<lambda>_ _. ()" "truncate \2" "truncate \1"]
  have "DetSemiAutomaton (truncate \1) = DetSemiAutomaton (truncate \2)"
    by (metis SemiAutomaton_isomorphic_wf_sym SemiAutomaton_isomorphic_wf_ext_def)
  thus ?thesis by simp
qed

subsection \<open> Renaming letters (i.e. elements of the alphabet) \<close>

definition SemiAutomaton_rename_labels :: "('q, 'a, 'more) SemiAutomaton_rec_scheme \<Rightarrow> ('a \<Rightarrow> 'b) \<Rightarrow>
                              ('q, 'b, 'more) SemiAutomaton_rec_scheme" where
"SemiAutomaton_rename_labels \ f \<equiv>
  \<lparr> \<Q> = \<Q> \, \<Sigma> = f ` (\<Sigma> \), \<Delta> = { (p, f \<sigma>, q) | p \<sigma> q. (p,\<sigma>,q) \<in> \<Delta> \}, \ = \ \, \<dots> = more \ \<rparr>"

lemma [simp] : "\<Q> (SemiAutomaton_rename_labels \ f) = \<Q> \" by (simp add: SemiAutomaton_rename_labels_def)
lemma [simp] : "\<Sigma> (SemiAutomaton_rename_labels \ f) = f ` (\<Sigma> \)" by (simp add: SemiAutomaton_rename_labels_def)
lemma [simp] : "(p, f\<sigma>, q) \<in> \<Delta> (SemiAutomaton_rename_labels \ f) \<longleftrightarrow>
                (\<exists> \<sigma>. (p, \<sigma>, q) \<in> \<Delta> \ \<and> (f\<sigma> = f \<sigma>))"
  by (auto simp add: SemiAutomaton_rename_labels_def)
lemma [simp] : "\ (SemiAutomaton_rename_labels \ f) = \ \" by (simp add: SemiAutomaton_rename_labels_def)

lemma (in SemiAutomaton) SemiAutomaton_rename_labels___is_well_formed :
"SemiAutomaton (SemiAutomaton_rename_labels \ f)"
using SemiAutomaton_axioms
by (auto simp add: SemiAutomaton_def image_iff Bex_def)

lemma SemiAutomaton_rename_labels_id [simp] : "SemiAutomaton_rename_labels \ id = \"
  by (rule SemiAutomaton_rec.equality, auto simp add: SemiAutomaton_rename_labels_def)

lemma SemiAutomaton_rename_labels_fin_run_LTS :
 "LTS_is_fin_run (\<Delta> \) w r \<Longrightarrow>
  LTS_is_fin_run (\<Delta> (SemiAutomaton_rename_labels \ f)) (map f w) r"
unfolding LTS_is_fin_run_def by auto

lemma SemiAutomaton_rename_labels_fin_run_LTS2 :
 "LTS_is_fin_run (\<Delta> (SemiAutomaton_rename_labels \ f)) w r \<Longrightarrow>
 \<exists>w'. w = map f w' \<and> LTS_is_fin_run (\<Delta> \) w' r"
proof (induct w arbitrary: r)
  case Nil thus ?case by simp
next
  case (Cons a w)
  note indhyp = Cons(1)

  note run = Cons(2)
  from run obtain r1 r2 r' where
     r_eq: "r = r1 # r2 # r'" by (cases r, simp, rename_tac r1 r'', case_tac r'', auto)

  from run r_eq obtain a' where
     step: "(r1, a', r2) \<in> \<Delta> \" and
     a_eq: "f a' = a" and
     run': "LTS_is_fin_run (\<Delta> (SemiAutomaton_rename_labels \ f)) w (r2 # r')"
  by auto

  from indhyp[OF run']
  obtain w' where w'_props: "w = map f w' \<and> LTS_is_fin_run (\<Delta> \) w' (r2 # r')" by auto

  show ?case
    apply (rule exI[where x = "a' # w'"])
    apply (simp add: r_eq a_eq w'_props step)
  done
qed

lemma LTS_is_reachable___SemiAutomaton_rename_labels :
 "LTS_is_reachable (\<Delta> \) q w q' \<Longrightarrow>
  LTS_is_reachable (\<Delta> (SemiAutomaton_rename_labels \ f)) q (map f w) q'"
unfolding LTS_is_reachable_alt_def
by (metis SemiAutomaton_rename_labels_fin_run_LTS)

lemma LTS_is_reachable___SemiAutomaton_rename_labelsE :
  "LTS_is_reachable (\<Delta> (SemiAutomaton_rename_labels \ f)) q w q' \<Longrightarrow>
   \<exists> w'. w = (map f w') \<and> LTS_is_reachable (\<Delta> \) q w' q'"
unfolding LTS_is_reachable_alt_def
by (metis SemiAutomaton_rename_labels_fin_run_LTS2)

lemma SemiAutomaton_rename_labels_inf_run_LTS :
 "LTS_is_inf_run (\<Delta> \) w r \<Longrightarrow>
  LTS_is_inf_run (\<Delta> (SemiAutomaton_rename_labels \ f)) (f \<circ> w) r"
unfolding LTS_is_inf_run_def by auto

lemma SemiAutomaton_rename_labels_inf_run_LTS2 :
 "LTS_is_inf_run (\<Delta> (SemiAutomaton_rename_labels \ f)) w r \<Longrightarrow>
 \<exists>w'. w = f \<circ> w' \<and> LTS_is_inf_run (\<Delta> \) w' r"
unfolding LTS_is_inf_run_def o_def fun_eq_iff by simp metis

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_labels_cong :
assumes equiv: "SemiAutomaton_isomorphic_wf \1 \2"
shows "SemiAutomaton_isomorphic_wf (SemiAutomaton_rename_labels \1 fl) (SemiAutomaton_rename_labels \2 fl)"
proof -
  from equiv
  obtain f where inj_f : "inj_on f (\<Q> \1)" and
                 \2_eq: "\2 = SemiAutomaton_rename_states \1 f" and
                 wf_\1: "SemiAutomaton \1"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  note wf_rename = SemiAutomaton.SemiAutomaton_rename_labels___is_well_formed [OF wf_\1, of fl]

  have "SemiAutomaton_rename_labels \2 fl = SemiAutomaton_rename_states (SemiAutomaton_rename_labels \1 fl) f"
    unfolding \2_eq SemiAutomaton_rename_labels_def SemiAutomaton_rename_states_ext_def
    by auto metis
  with wf_rename inj_f show ?thesis
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def
    by auto
qed

lemma SemiAutomaton_is_initially_connected___SemiAutomaton_rename_labels :
assumes connected: "SemiAutomaton_is_initially_connected \"
shows "SemiAutomaton_is_initially_connected (SemiAutomaton_rename_labels \ f)"
unfolding SemiAutomaton_is_initially_connected_alt_def
proof (intro ballI)
  fix q
  let ?A2 = "SemiAutomaton_rename_labels \ f"
  assume q2_in_A2: "q \<in> \<Q> ?A2"
  hence q2_in_A1: "q \<in> \<Q> \" by simp

  from connected q2_in_A1 obtain i w1 where
     i_in: "i \<in> \ \" and
     reach1: "LTS_is_reachable (\<Delta> \) i w1 q"
    unfolding SemiAutomaton_is_initially_connected_alt_def
    by blast

  from LTS_is_reachable___SemiAutomaton_rename_labels[OF reach1, of f]
  have reach2: "LTS_is_reachable (\<Delta> ?A2) i (map f w1) q" .

  from i_in reach2
  show "\<exists>i\<in>\ ?A2. \<exists>w. LTS_is_reachable (\<Delta> ?A2) i w q"
     by (simp add: Bex_def) blast
qed

subsection \<open> Normalise states \<close>

text \<open> Operations like building the product of two automata, naturally lead to a different type
of automaton states. When combining several operations, one often needs to stay in the same
type, however. The following definitions allows to transfer an automaton to an isomorphic
one with a different type of states. \<close>

definition SemiAutomaton_normalise_states :: "('q, 'a, _) SemiAutomaton_rec_scheme \<Rightarrow>
                                              ('q2::{automaton_states}, 'a) SemiAutomaton_rec" where
  "SemiAutomaton_normalise_states \ = (SemiAutomaton_rename_states \ (SOME (f::'q \<Rightarrow> 'q2). inj_on f (\<Q> \)))"

lemma ex_inj_on_finite :
assumes inf_univ: "~(finite (UNIV::'b set))"
    and fin_A: "finite (A::'a set)"
shows "\<exists>f::('a \<Rightarrow> 'b). inj_on f A"
using fin_A
proof (induct rule: finite_induct)
  case empty show ?case by simp
next
  case (insert a A)
  note fin_A = insert(1)
  note a_nin = insert(2)
  from insert(3) obtain f where inj_f: "inj_on (f::'a\<Rightarrow>'b) A" by blast

  from fin_A have "finite (f ` A)" by (rule finite_imageI)
  then obtain b where b_nin: "b \<notin> (f ` A)" by (metis ex_new_if_finite inf_univ)

  let ?f' = "\<lambda>x. if (x = a) then b else f x"
  from inj_f a_nin b_nin have "inj_on ?f' (insert a A)"
    unfolding inj_on_def by auto
  thus ?case by blast
qed

lemma SemiAutomaton_isomorphic_wf_normalise_states :
fixes \:: "('q, 'a, _) SemiAutomaton_rec_scheme"
assumes wf_A: "FinSemiAutomaton \"
shows "SemiAutomaton_isomorphic_wf \ ((SemiAutomaton_normalise_states \)::('q2::{automaton_states},'a) SemiAutomaton_rec)"
unfolding SemiAutomaton_normalise_states_def
  apply (rule SemiAutomaton_isomorphic_wf___SemiAutomaton_rename_states [OF _ FinSemiAutomaton_is_Semiautomaton[OF wf_A]])
  apply (rule someI_ex [where P = "\<lambda>f. inj_on f (\<Q> \)"])
  apply (rule ex_inj_on_finite)
  apply (simp_all add: FinSemiAutomaton.finite_\<Q>[OF wf_A] not_finite_automaton_states_UNIV)
done

lemma SemiAutomaton_normalise_states_truncate[simp]:
  "SemiAutomaton_normalise_states (SemiAutomaton.truncate \) =
   SemiAutomaton_normalise_states \"
by (simp add: SemiAutomaton_normalise_states_def SemiAutomaton_rename_states_ext_def
              SemiAutomaton_rec.defs)

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_normalise_states :
assumes iso: "SemiAutomaton_isomorphic_wf_fin_ext more_f1 \1 \2"
shows "SemiAutomaton_isomorphic_wf_fin \1 (SemiAutomaton_normalise_states \2)"
proof -
  from SemiAutomaton_isomorphic_wf_fin_ext_truncate[OF iso]
  have "SemiAutomaton_isomorphic_wf_fin (truncate \1) (truncate \2)" by simp
  hence "SemiAutomaton_isomorphic_wf_fin (truncate \1) (SemiAutomaton_normalise_states (truncate \2))"
    by (metis SemiAutomaton_isomorphic_wf_ext___fin_iff
              SemiAutomaton_isomorphic_wf_fin_ext_alt_def SemiAutomaton_isomorphic_wf_fin_trans
              SemiAutomaton_isomorphic_wf_normalise_states)
  thus ?thesis
    unfolding SemiAutomaton_isomorphic_wf_fin_alt_def
    apply simp
    apply (simp add: SemiAutomaton_isomorphic_wf_ext_def
                     SemiAutomaton_isomorphic_ext_def
                     SemiAutomaton_rename_states_ext_def)
    apply (simp add: SemiAutomaton_rec.defs)
  done
qed

lemma SemiAutomaton_isomorphic_wf___SemiAutomaton_normalise_states_cong :
fixes \1::"('q1, 'a) SemiAutomaton_rec"
  and \2::"('q2, 'a) SemiAutomaton_rec"
shows "SemiAutomaton_isomorphic_wf_fin \1 \2 \<Longrightarrow>
 SemiAutomaton_isomorphic_wf (SemiAutomaton_normalise_states \1) (SemiAutomaton_normalise_states \2)"
unfolding SemiAutomaton_normalise_states_def
  apply (rule SemiAutomaton.SemiAutomaton_isomorphic_wf___rename_states_cong [of _ \1])
  apply (rule someI_ex [where P = "\<lambda>f. inj_on f (\<Q> \1)"])
  apply (rule ex_inj_on_finite)
  defer defer
  apply (rule someI_ex [where P = "\<lambda>f. inj_on f (\<Q> \2)"])
  apply (rule ex_inj_on_finite)
  apply (simp_all add: FinSemiAutomaton.finite_\<Q>
                       not_finite_automaton_states_UNIV SemiAutomaton_isomorphic_wf_fin_alt_def)
done

lemma SemiAutomaton_normalise_states_\<Sigma> [simp] :
"\<Sigma> (SemiAutomaton_normalise_states \) = \<Sigma> \"
unfolding SemiAutomaton_normalise_states_def by simp

lemma SemiAutomaton_is_initially_connected___normalise_states :
assumes connected: "SemiAutomaton_is_initially_connected \"
shows "SemiAutomaton_is_initially_connected (SemiAutomaton_normalise_states \)"
unfolding SemiAutomaton_normalise_states_def
by (intro connected SemiAutomaton_is_initially_connected___SemiAutomaton_rename_states)

subsection \<open> Product automata \<close>

definition product_SemiAutomaton ::
  "('q1, 'a, 'x1) SemiAutomaton_rec_scheme \<Rightarrow>
   ('q2, 'a, 'x2) SemiAutomaton_rec_scheme \<Rightarrow> ('q1 \<times> 'q2, 'a) SemiAutomaton_rec" where
"product_SemiAutomaton \1 \2 == \<lparr>
   \<Q>=\<Q> \1 \<times> \<Q> \2,
   \<Sigma>=\<Sigma> \1 \<inter> \<Sigma> \2,
   \<Delta> = LTS_product (\<Delta> \1) (\<Delta> \2),
   \ = \ \1 \<times> \ \2\<rparr>"

lemma [simp] : "\ (product_SemiAutomaton \1 \2) = \ \1 \<times> \ \2" by (simp add: product_SemiAutomaton_def)
lemma [simp] : "\<Q> (product_SemiAutomaton \1 \2) = \<Q> \1 \<times> \<Q> \2" by (simp add: product_SemiAutomaton_def)
lemma [simp] : "\<Sigma> (product_SemiAutomaton \1 \2) = \<Sigma> \1 \<inter> \<Sigma> \2" by (simp add: product_SemiAutomaton_def)
lemma [simp] : "\<Delta> (product_SemiAutomaton \1 \2) = LTS_product (\<Delta> \1) (\<Delta> \2)" by (simp add: product_SemiAutomaton_def)

lemma product_SemiAutomaton___is_well_formed :
  "\<lbrakk>SemiAutomaton \1;  SemiAutomaton \2\<rbrakk> \<Longrightarrow> SemiAutomaton (product_SemiAutomaton \1 \2)"
unfolding SemiAutomaton_def by (auto simp add: product_SemiAutomaton_def)

lemma product_SemiAutomaton___is_well_formed_fin :
  "\<lbrakk> FinSemiAutomaton \1; FinSemiAutomaton \2\<rbrakk> \<Longrightarrow> FinSemiAutomaton (product_SemiAutomaton \1 \2)"
unfolding FinSemiAutomaton_alt_def
by (simp add: product_SemiAutomaton___is_well_formed LTS_product_finite)

lemma product_SemiAutomaton___is_well_formed_det :
  "\<lbrakk> DetSemiAutomaton \1; DetSemiAutomaton \2\<rbrakk> \<Longrightarrow> DetSemiAutomaton (product_SemiAutomaton \1 \2)"
unfolding DetSemiAutomaton_alt_def
   apply (auto simp add:
             product_SemiAutomaton___is_well_formed
             SemiAutomaton_is_complete_deterministic_def LTS_is_complete_deterministic_def
             LTS_is_complete_def LTS_is_deterministic_def)
done

lemma product_SemiAutomaton___is_well_formed_det_fin :
  assumes "DetFinSemiAutomaton \1"
  and "DetFinSemiAutomaton \2"
  shows "DetFinSemiAutomaton (product_SemiAutomaton \1 \2)"
proof -
  from assms interpret DetSemiAutomaton "product_SemiAutomaton \1 \2"
    using product_SemiAutomaton___is_well_formed_det
    unfolding DetFinSemiAutomaton_def
    by blast
  from assms interpret FinSemiAutomaton "product_SemiAutomaton \1 \2"
    using product_SemiAutomaton___is_well_formed_fin
    unfolding DetFinSemiAutomaton_def
    by blast
  show ?thesis by intro_locales
qed

lemma product_SemiAutomaton_is_fin_run :
  "SemiAutomaton_is_fin_run (product_SemiAutomaton \1 \2) w r \<longleftrightarrow>
   SemiAutomaton_is_fin_run \1 w (map fst r) \<and>
   SemiAutomaton_is_fin_run \2 w (map snd r)"
apply (simp add: product_SemiAutomaton_def SemiAutomaton_is_fin_run_def
                 LTS_is_fin_run_product_iff)
apply (cases r)
apply auto
done

lemma product_SemiAutomaton_is_inf_run :
  "SemiAutomaton_is_inf_run (product_SemiAutomaton \1 \2) w r \<longleftrightarrow>
   SemiAutomaton_is_inf_run \1 w (fst \<circ> r) \<and>
   SemiAutomaton_is_inf_run \2 w (snd \<circ> r)"
apply (simp add: product_SemiAutomaton_def SemiAutomaton_is_inf_run_def
                 LTS_is_inf_run_product_iff)
apply (cases "r 0")
apply auto
done

lemma SemiAutomaton_isomorphic_product_SemiAutomaton :
assumes equiv_1: "SemiAutomaton_isomorphic_wf \1 \1'"
    and equiv_2: "SemiAutomaton_isomorphic_wf \2 \2'"
shows "SemiAutomaton_isomorphic_wf (product_SemiAutomaton \1 \2) (product_SemiAutomaton \1' \2')"
proof -
  from equiv_1 obtain f1 where inj_f1 : "inj_on f1 (\<Q> \1)" and
                 \1'_eq: "\1' = SemiAutomaton_rename_states \1 f1" and
                 wf_\1 : "SemiAutomaton \1"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  from equiv_2 obtain f2 where inj_f2 : "inj_on f2 (\<Q> \2)" and
                 \2'_eq: "\2' = SemiAutomaton_rename_states \2 f2" and
                 wf_\2: "SemiAutomaton \2"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  define f where "f \<equiv> \<lambda>(q1, q2). (f1 q1, f2 q2)"
  with inj_f1 inj_f2 have inj_f : "inj_on f (\<Q> (product_SemiAutomaton \1 \2))"
    by (simp add: inj_on_def)

  have f_image: "\<And>S1 S2. f ` (S1 \<times> S2) = (f1 ` S1) \<times> (f2 ` S2)"
    unfolding f_def by auto

  hence prod_eq': "product_SemiAutomaton \1' \2' = SemiAutomaton_rename_states (product_SemiAutomaton \1 \2) f"
    unfolding \1'_eq \2'_eq product_SemiAutomaton_def
    apply (rule_tac SemiAutomaton_rec.equality)
    apply (simp_all add: f_image)
    apply (simp del: ex_simps add: SemiAutomaton_rename_states_ext_def set_eq_iff ex_simps[symmetric] f_def)
    apply (blast)
  done

  from inj_f prod_eq' product_SemiAutomaton___is_well_formed [OF wf_\1 wf_\2]
  show ?thesis
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by blast
qed

lemma SemiAutomaton_isomorphic_product_SemiAutomaton_fin :
assumes equiv_1: "SemiAutomaton_isomorphic_wf_fin \1 \1'"
    and equiv_2: "SemiAutomaton_isomorphic_wf_fin \2 \2'"
shows "SemiAutomaton_isomorphic_wf_fin (product_SemiAutomaton \1 \2) (product_SemiAutomaton \1' \2')"
using assms
by (metis SemiAutomaton_isomorphic_product_SemiAutomaton SemiAutomaton_isomorphic_wf_fin_alt_def
          product_SemiAutomaton___is_well_formed_fin)

(* TODO: Is this the right level of abstraction here or should it go to
  LTS with finiteness conditions on LTS *)
lemma LTS_product_bound:
  assumes invar: "SemiAutomaton sa" "SemiAutomaton fa"
  shows "LTS_product (\<Delta> sa) (\<Delta> fa) \<subseteq> (\<Q> sa\<times>\<Q> fa) \<times> (\<Sigma> sa \<inter> \<Sigma> fa) \<times> (\<Q> sa\<times>\<Q> fa)"
proof -
  interpret sa: SemiAutomaton sa + fa: SemiAutomaton fa by fact+

  show ?thesis
    unfolding LTS_product_def
    by (auto dest: sa.\<Delta>_consistent fa.\<Delta>_consistent)
qed

subsection \<open> Composition of automata \<close>

definition comb_SemiAutomaton ::
  "('q1, 'a, 'x1) SemiAutomaton_rec_scheme \<Rightarrow>
   ('q2, 'a, 'x2) SemiAutomaton_rec_scheme \<Rightarrow> ('q1 + 'q2, 'a) SemiAutomaton_rec" where
"comb_SemiAutomaton \1 \2 == \<lparr>
   \<Q>=\<Q> \1 <+>  \<Q> \2,
   \<Sigma>=\<Sigma> \1 \<inter> \<Sigma> \2,
   \<Delta> = LTS_comb (\<Delta> \1) (\<Delta> \2),
   \ = \ \1 <+> \ \2 \<rparr>"

lemma [simp] : "\ (comb_SemiAutomaton \1 \2) = \ \1 <+> \ \2" by (simp add: comb_SemiAutomaton_def)
lemma [simp] : "\<Q> (comb_SemiAutomaton \1 \2) = \<Q> \1 <+> \<Q> \2" by (simp add: comb_SemiAutomaton_def)
lemma [simp] : "\<Sigma> (comb_SemiAutomaton \1 \2) = \<Sigma> \1 \<inter> \<Sigma> \2" by (simp add: comb_SemiAutomaton_def)
lemma [simp] : "\<Delta> (comb_SemiAutomaton \1 \2) = LTS_comb (\<Delta> \1) (\<Delta> \2)" by (simp add: comb_SemiAutomaton_def)

lemma comb_SemiAutomaton___is_well_formed :
  "\<lbrakk> SemiAutomaton \1;  SemiAutomaton \2; \<Sigma> \1 = \<Sigma> \2\<rbrakk> \<Longrightarrow> SemiAutomaton (comb_SemiAutomaton \1 \2)"
unfolding SemiAutomaton_def by (auto simp add: comb_SemiAutomaton_def LTS_comb_alt2_def)

lemma comb_SemiAutomaton_is_fin_run :
  "SemiAutomaton_is_fin_run (comb_SemiAutomaton \1 \2) w r \<longleftrightarrow>
   ((\<exists>r1. map Inl r1 = r \<and> SemiAutomaton_is_fin_run \1 w r1) \<or>
    (\<exists>r2. map Inr r2 = r \<and> SemiAutomaton_is_fin_run \2 w r2))"
apply (simp add: comb_SemiAutomaton_def SemiAutomaton_is_fin_run_def
                 LTS_is_fin_run_LTS_comb)
apply (cases r)
apply auto
done

lemma SemiAutomaton_isomorphic_comb_SemiAutomaton :
assumes equiv_1: "SemiAutomaton_isomorphic_wf \1 \1'"
    and equiv_2: "SemiAutomaton_isomorphic_wf \2 \2'"
    and \<Sigma>_eq: "\<Sigma> \1 = \<Sigma> \2"
shows "SemiAutomaton_isomorphic_wf (comb_SemiAutomaton \1 \2) (comb_SemiAutomaton \1' \2')"
proof -
  from equiv_1 obtain f1 where inj_f1 : "inj_on f1 (\<Q> \1)" and
                 \1'_eq: "\1' = SemiAutomaton_rename_states \1 f1" and
                 wf_\1 : "SemiAutomaton \1"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  from equiv_2 obtain f2 where inj_f2 : "inj_on f2 (\<Q> \2)" and
                 \2'_eq: "\2' = SemiAutomaton_rename_states \2 f2" and
                 wf_\2: "SemiAutomaton \2"
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by auto

  define f where "f \<equiv> \<lambda>q. case q of (Inl q1) \<Rightarrow> Inl (f1 q1) | Inr q2 \<Rightarrow> Inr (f2 q2)"
  with inj_f1 inj_f2 have inj_f : "inj_on f (\<Q> (comb_SemiAutomaton \1 \2))"
    by (auto simp add: inj_on_def Ball_def)

  have f_image: "\<And>S1 S2. f ` (S1 <+> S2) = (f1 ` S1) <+> (f2 ` S2)"
    unfolding f_def
      apply (auto simp add: image_iff Bex_def)
      apply auto
    done

  with inj_f1 inj_f2
  have prod_eq': "comb_SemiAutomaton \1' \2' = SemiAutomaton_rename_states (comb_SemiAutomaton \1 \2) f"
    unfolding \1'_eq \2'_eq
    apply (rule_tac SemiAutomaton_rec.equality)
    apply (simp_all add: f_image)
    apply (simp del: ex_simps add: SemiAutomaton_rename_states_ext_def set_eq_iff ex_simps[symmetric] f_def
                LTS_comb_alt2_def)
    apply (intro allI iffI)
    apply auto[]
    apply (rule_tac x="Inl s1" in exI)
    apply simp
    apply (rule_tac x="Inl s2" in exI)
    apply simp
    apply (rule_tac x="Inr s1" in exI)
    apply simp
    apply (rule_tac x="Inr s2" in exI)
    apply simp
    apply auto[]
  done

  from inj_f prod_eq' comb_SemiAutomaton___is_well_formed [OF wf_\1 wf_\2 \<Sigma>_eq]
  show ?thesis
    unfolding SemiAutomaton_isomorphic_wf_ext_def SemiAutomaton_isomorphic_ext_def by blast
qed

subsection \<open> Powerset Construction \<close>

definition powerset_SemiAutomaton where
  "powerset_SemiAutomaton \ =
   \<lparr> \<Q> = {q. q \<subseteq> (\<Q> \)},
     \<Sigma> = (\<Sigma> \),
     \<Delta> = {(Q, \<sigma>, Q')| Q Q' \<sigma>. Q \<subseteq> \<Q> \ \<and> \<sigma> \<in> \<Sigma> \ \<and> Q' = {q'. (\<exists>q\<in>Q. (q, \<sigma>, q') \<in> \<Delta> \)}},
     \ = {\ \} \<rparr>"

lemma powerset_SemiAutomaton_\ [simp] : "\ (powerset_SemiAutomaton \) = {\ \}" by (simp add: powerset_SemiAutomaton_def)
lemma powerset_SemiAutomaton_\<Q> [simp] : "(q \<in> \<Q> (powerset_SemiAutomaton \)) \<longleftrightarrow> q \<subseteq> \<Q> \" by (simp add: powerset_SemiAutomaton_def)
lemma powerset_SemiAutomaton_\<Sigma> [simp] : "\<Sigma> (powerset_SemiAutomaton \) = \<Sigma> \" by (simp add: powerset_SemiAutomaton_def)

lemma powerset_SemiAutomaton_\<Delta> [simp] : "Q\<sigma>Q' \<in> \<Delta> (powerset_SemiAutomaton \) \<longleftrightarrow>
  ((snd (snd Q\<sigma>Q') = {q'. \<exists>q\<in>(fst Q\<sigma>Q'). (q, fst (snd Q\<sigma>Q'), q') \<in> \<Delta> \}) \<and>
   (fst Q\<sigma>Q' \<subseteq> \<Q> \) \<and> ((fst (snd Q\<sigma>Q')) \<in> \<Sigma> \))"
by (cases Q\<sigma>Q') (auto simp add: powerset_SemiAutomaton_def)

lemma (in FinSemiAutomaton) powerset_SemiAutomaton__finite_\<Delta>:
  "finite (\<Sigma> \) \<Longrightarrow> finite (\<Delta> (powerset_SemiAutomaton \))"
proof -
  assume A: "finite (\<Sigma> \)"
  let ?power = " (Pow (\<Q> \)) \<times> \<Sigma> \ \<times> (Pow (\<Q> \))"
  from finite_\<Q> finite_\<Delta> A have "finite ?power" by auto
  moreover have "\<Delta> (powerset_SemiAutomaton \) \<subseteq> ?power"
    by (fastforce simp add: \<Delta>_consistent)
  ultimately show ?thesis using finite_subset by blast
qed

lemma powerset_SemiAutomaton_is_deterministic :
  "SemiAutomaton_is_deterministic (powerset_SemiAutomaton \)"
proof -
  let ?\ = "powerset_SemiAutomaton \"
  have "\<exists>q0. \ ?\ = {q0}" by simp
  moreover
  have "LTS_is_deterministic (\<Delta> ?\)"
    unfolding LTS_is_deterministic_def powerset_SemiAutomaton_def
    by auto
  ultimately show "SemiAutomaton_is_deterministic ?\"
    unfolding SemiAutomaton_is_deterministic_def by simp
qed

lemma (in SemiAutomaton) powerset_SemiAutomaton_is_complete_determistic :
  "SemiAutomaton_is_complete_deterministic (powerset_SemiAutomaton \)"
proof -
  let ?\ = "powerset_SemiAutomaton \"
  have "LTS_is_complete (\<Q> ?\) (\<Sigma> ?\) (\<Delta> ?\)"
    unfolding LTS_is_complete_def powerset_SemiAutomaton_def
    using \<Delta>_consistent by auto
  thus "SemiAutomaton_is_complete_deterministic ?\"
    unfolding SemiAutomaton_is_complete_deterministic_alt_def
    by (simp add: powerset_SemiAutomaton_is_deterministic)
qed

lemma powerset_SemiAutomaton_\ [simp] : "\ (powerset_SemiAutomaton \) = \ \" unfolding \_def by simp

lemma powerset_SemiAutomaton_\<delta> :
  "\<delta> (powerset_SemiAutomaton \) = (\<lambda>(Q,\<sigma>).
     if (Q \<subseteq> (\<Q> \) \<and> \<sigma> \<in> \<Sigma> \) then Some {q' |q q'. q \<in> Q \<and> (q, \<sigma>, q') \<in> \<Delta> \} else None)"
apply (subst fun_eq_iff, clarify)
apply (simp add: powerset_SemiAutomaton_def \<delta>_def LTS_to_DLTS_def Bex_def)
done

lemma powerset_SemiAutomaton___is_well_formed :
  "SemiAutomaton \ \<Longrightarrow> SemiAutomaton (powerset_SemiAutomaton \)"
by (auto simp add: SemiAutomaton_def powerset_SemiAutomaton_def)

lemma powerset_SemiAutomaton___is_well_formed_fin :
  assumes "FinSemiAutomaton \"
  and "finite (\<Sigma> \)"
  shows "FinSemiAutomaton (powerset_SemiAutomaton \)"
using assms
using powerset_SemiAutomaton___is_well_formed[of \]
using FinSemiAutomaton.powerset_SemiAutomaton__finite_\<Delta>[OF assms]
by (auto simp add: powerset_SemiAutomaton_def FinSemiAutomaton_alt_def)

lemma powerset_SemiAutomaton___is_well_formed_det :
  "SemiAutomaton \ \<Longrightarrow> DetSemiAutomaton (powerset_SemiAutomaton \)"
by (simp add: powerset_SemiAutomaton___is_well_formed DetSemiAutomaton_alt_def
              SemiAutomaton.powerset_SemiAutomaton_is_complete_determistic)

lemma powerset_SemiAutomaton___is_well_formed_det_fin :
  assumes "FinSemiAutomaton \" "finite (\<Sigma> \)"
  shows "DetFinSemiAutomaton (powerset_SemiAutomaton \)"
proof -
  from assms interpret F: FinSemiAutomaton "powerset_SemiAutomaton \"
    by (rule powerset_SemiAutomaton___is_well_formed_fin)
  from assms interpret D: DetSemiAutomaton "powerset_SemiAutomaton \"
    using powerset_SemiAutomaton___is_well_formed_det
    unfolding FinSemiAutomaton_def
    by blast
  show ?thesis by intro_locales
qed

lemma (in SemiAutomaton) powerset_SemiAutomaton___DLTS_reach :
shows "Q \<subseteq> \<Q> \ \<Longrightarrow>
  DLTS_reach (\<delta> (powerset_SemiAutomaton \)) Q w =
  (if (w \<in> lists (\<Sigma> \)) then Some {q'. \<exists>q\<in>Q. LTS_is_reachable (\<Delta> \) q w q'} else None)"
proof (induct w arbitrary: Q)
  case Nil thus ?case by simp
next
  case (Cons \<sigma> w)
  note ind_hyp = Cons(1)
  note Q_subset = Cons(2)

  interpret detSemi_detA:  DetSemiAutomaton "(powerset_SemiAutomaton \)"
    using powerset_SemiAutomaton___is_well_formed_det[OF SemiAutomaton_axioms]
    by assumption

  let ?Q' = "{q' |q q'. q \<in> Q \<and> (q, \<sigma>, q') \<in> \<Delta> \}"
  from \<Delta>_consistent have Q'_subset : "?Q' \<subseteq> \<Q> \" by auto

  note ind_hyp' = ind_hyp [OF Q'_subset]
  thus ?case
    by (auto simp add: powerset_SemiAutomaton_\<delta> Q_subset)
qed

lemma (in SemiAutomaton) powerset_SemiAutomaton___LTS_is_reachable :
shows "Q \<subseteq> \<Q> \ \<Longrightarrow>
  LTS_is_reachable (\<Delta> (powerset_SemiAutomaton \)) Q w Q' \<longleftrightarrow>
  (w \<in> lists (\<Sigma> \)) \<and> Q' = {q'. \<exists>q\<in>Q. LTS_is_reachable (\<Delta> \) q w q'}"
using LTS_is_reachable_determistic [of "\<Delta> (powerset_SemiAutomaton \)" Q w Q']
      powerset_SemiAutomaton_is_deterministic[of \]
      powerset_SemiAutomaton___DLTS_reach[of Q w]
by (simp add: SemiAutomaton_is_deterministic_def \<delta>_def) blast

lemma SemiAutomaton_is_complete_deterministic___SemiAutomaton_remove_unreachable_states :
  "SemiAutomaton_is_complete_deterministic \ \<Longrightarrow>
   SemiAutomaton_is_complete_deterministic (SemiAutomaton_remove_unreachable_states \)"
proof -
  have det_impl:
    "LTS_is_complete_deterministic (\<Q> \) (\<Sigma> \) (\<Delta> \) \<Longrightarrow>
     LTS_is_complete_deterministic (\<Q> (SemiAutomaton_remove_unreachable_states \)) (\<Sigma> \)
           (\<Delta> (SemiAutomaton_remove_unreachable_states \))"
    apply (simp add: LTS_is_deterministic_def LTS_is_complete_deterministic_def
                     LTS_is_complete_def SemiAutomaton_remove_unreachable_states_def Ball_def Bex_def)
    apply (metis SemiAutomaton_unreachable_states_extend)
  done

  assume "SemiAutomaton_is_complete_deterministic \"
  with det_impl show ?thesis by (simp add: SemiAutomaton_is_complete_deterministic_def)
qed

end