# Theory Observability

```section ‹Observability›

text ‹This theory presents the classical algorithm for transforming FSMs into
language-equivalent observable FSMs in analogy to the determinisation of nondeterministic
finite automata.›

theory Observability
imports FSM
begin

lemma fPow_Pow : "Pow (fset A) = fset (fset |`| fPow A)"
proof (induction A)
case empty
then show ?case by auto
next
case (insert x A)

have "Pow (fset (finsert x A)) = Pow (fset A) ∪ (insert x) ` Pow (fset A)"
moreover have "fset (fset |`| fPow (finsert x A)) = fset (fset |`| fPow A) ∪ (insert x) ` fset (fset |`| fPow A)"
proof -
have "fset |`| ((fPow A) |∪| (finsert x) |`| (fPow A)) = (fset |`| fPow A) |∪| (insert x) |`| (fset |`| fPow A)"
unfolding fimage_funion
by fastforce
moreover have "(fPow (finsert x A)) = (fPow A) |∪| (finsert x) |`| (fPow A)"
ultimately show ?thesis
by auto
qed
ultimately show ?case
using insert.IH by simp
qed

lemma fcard_fsubset: "¬ fcard (A |-| (B |∪| C)) < fcard (A |-| B) ⟹ C |⊆| A ⟹ C |⊆| B"
proof (induction C)
case empty
then show ?case by auto
next
case (insert x C)
then show ?case
unfolding  finsert_fsubset funion_finsert_right not_less
proof -
assume a1: "fcard (A |-| B) ≤ fcard (A |-| finsert x (B |∪| C))"
assume "⟦fcard (A |-| B) ≤ fcard (A |-| (B |∪| C)); C |⊆| A⟧ ⟹ C |⊆| B"
assume a2: "x |∈| A ∧ C |⊆| A"
have "A |-| (C |∪| finsert x B) = A |-| B ∨ ¬ A |-| (C |∪| finsert x B) |⊆| A |-| B"
using a1 by (metis (no_types) fcard_seteq funion_commute funion_finsert_right)
then show "x |∈| B ∧ C |⊆| B"
using a2 by blast
qed
qed

lemma make_observable_transitions_qtrans_helper:
assumes  "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) A;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) nexts)"
shows "⋀ t . t |∈| qtrans ⟷ t_source t |∈| nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
proof -
have "fset qtrans = { (q,x,y,q') | q x y q' . q |∈| nexts ∧ q' ≠ {||} ∧ fset q' = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y}}"
proof -
have "⋀ q . fset (ffilter (λt . t_source t |∈| q) A) = Set.filter (λt . t_source t |∈| q) (fset A)"
using ffilter.rep_eq assms(1) by auto
then have "⋀ q . fset (fimage (λ t . (t_input t, t_output t)) (ffilter (λt . t_source t |∈| q) A)) = image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A))"
by simp
then have *:"⋀ q . fset (fimage (λ(x,y) . (q,x,y, (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))))) (fimage (λ t . (t_input t, t_output t)) (ffilter (λt . t_source t |∈| q) (A))))
= image (λ(x,y) . (q,x,y, (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))))) (image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A)))"
by (metis (no_types, lifting) ffilter.rep_eq fset.set_map)

have **: "⋀ f1 f2 xs ys ys' . (⋀ x . fset (f1 x ys) = (f2 x ys')) ⟹
fset (ffUnion (fimage (λ x . (f1 x ys)) xs)) = (⋃ x ∈ fset xs . (f2 x ys'))"
unfolding ffUnion.rep_eq fimage.rep_eq by force

have "fset (ffUnion (fimage (λ q . (fimage (λ(x,y) . (q,x,y, (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))))) (fimage (λ t . (t_input t, t_output t)) (ffilter (λt . t_source t |∈| q) (A))))) nexts))
= (⋃ q ∈ fset nexts . image (λ(x,y) . (q,x,y, (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))))) (image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A))))"
unfolding ffUnion.rep_eq fimage.rep_eq
using "*" by force

also have "… = { (q,x,y,q') | q x y q' . q |∈| nexts ∧ q' ≠ {||} ∧ fset q' = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y}}"
(is "?A = ?B") proof -
have "⋀ t . t ∈ ?A ⟹ t ∈ ?B"
proof -
fix t assume "t ∈ ?A"
then obtain q where "q ∈ fset nexts"
and   "t ∈ image (λ(x,y) . (q,x,y, (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))))) (image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A)))"
by blast
then obtain x y q' where *: "(x,y) ∈ (image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A)))"
and      "t = (q,x,y,q')"
and   **:"q' = (t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A)))))"
by force

have "q |∈| nexts"
using ‹q ∈ fset nexts›
by simp
moreover have "q' ≠ {||}"
proof -
have ***:"(Set.filter (λt . t_source t |∈| q) (fset A)) = fset (ffilter (λt . t_source t |∈| q) (A))"
by auto
have "∃ t . t |∈| (ffilter (λt. t_source t |∈| q) A) ∧ (t_input t, t_output t) = (x,y)"
using *
by (metis (no_types, lifting) "***" image_iff)
then show ?thesis unfolding **
by force
qed
moreover have "fset q' = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y}"
proof -
have "{t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y} = ((Set.filter (λt . (t_input t, t_output t) = (x,y)) (fset (ffilter (λt . t_source t |∈| q) (A)))))"
by fastforce
also have "… = fset ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))"
by fastforce
finally have "{t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y} = fset ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))" .
then show ?thesis
unfolding **
by simp
qed
ultimately show "t ∈ ?B"
unfolding ‹t = (q,x,y,q')›
by blast
qed
moreover have "⋀ t . t ∈ ?B ⟹ t ∈ ?A"
proof -
fix t assume "t ∈ ?B"
then obtain q x y q' where "t = (q,x,y,q')" and "(q,x,y,q') ∈ ?B" by force
then have "q |∈| nexts"
and  "q' ≠ {||}"
and  *: "fset q' = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y}"
by force+
then have "fset q' ≠ {}"
by (metis bot_fset.rep_eq fset_inject)

have "(x,y) ∈ (image (λ t . (t_input t, t_output t)) (Set.filter (λt . t_source t |∈| q) (fset A)))"
using ‹fset q' ≠ {}› unfolding * Set.filter_def by blast
moreover have "q' = t_target |`| ffilter (λt. (t_input t, t_output t) = (x, y)) (ffilter (λt. t_source t |∈| q) A)"
proof -
have "{t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y} = ((Set.filter (λt . (t_input t, t_output t) = (x,y)) (fset (ffilter (λt . t_source t |∈| q) (A)))))"
by fastforce
also have "… = fset ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))"
by fastforce
finally have ***:"{t' . t' |∈| A ∧ t_source t' |∈| q ∧ t_input t' = x ∧ t_output t' = y} = fset ((ffilter (λt . (t_input t, t_output t) = (x,y)) (ffilter (λt . t_source t |∈| q) (A))))" .

show ?thesis
using *
unfolding ***
by (metis (no_types, lifting) fimage.rep_eq fset_inject)
qed
ultimately show "t ∈ ?A"
using ‹q |∈| nexts›
unfolding ‹t = (q,x,y,q')›
by force
qed
ultimately show ?thesis
by (metis (no_types, lifting) Collect_cong Sup_set_def mem_Collect_eq)
qed
finally show ?thesis
unfolding assms Let_def by blast
qed
then show "⋀ t . t |∈| qtrans ⟷ t_source t |∈| nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| A ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
by force
qed

function make_observable_transitions :: "('a,'b,'c) transition fset ⇒ 'a fset fset ⇒ 'a fset fset ⇒ ('a fset × 'b × 'c × 'a fset) fset ⇒ ('a fset × 'b × 'c × 'a fset) fset" where
"make_observable_transitions base_trans nexts dones ts = (let
qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| (ffilter (λt . (t_input t, t_output t) = (x,y)) qts))) ios)) nexts);
dones' = dones |∪| nexts;
ts' = ts |∪| qtrans;
nexts' = (fimage t_target qtrans) |-| dones'
in if nexts' = {||}
then ts'
else make_observable_transitions base_trans nexts' dones' ts')"
by auto
termination
proof -
{
fix base_trans :: "('a,'b,'c) transition fset"
fix nexts :: "'a fset fset"
fix dones :: "'a fset fset"
fix ts    :: "('a fset × 'b × 'c × 'a fset) fset"
fix q x y q'

assume assm1: "¬ fcard
(fPow (t_source |`| base_trans |∪| t_target |`| base_trans) |-|
(dones |∪| nexts |∪|
t_target |`|
ffUnion
((λq. let qts = ffilter (λt. t_source t |∈| q) base_trans
in ((λ(x, y). (q, x, y, t_target |`| ffilter (λt. t_input t = x ∧ t_output t = y) qts)) ∘ (λt. (t_input t, t_output t))) |`|
qts) |`|
nexts)))
< fcard (fPow (t_source |`| base_trans |∪| t_target |`| base_trans) |-| (dones |∪| nexts))"

and assm2: "(q, x, y, q') |∈|
ffUnion
((λq. let qts = ffilter (λt. t_source t |∈| q) base_trans
in ((λ(x, y). (q, x, y, t_target |`| ffilter (λt. t_input t = x ∧ t_output t = y) qts)) ∘ (λt. (t_input t, t_output t))) |`| qts) |`|
nexts)"

and assm3: "q' |∉| nexts"

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' | t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

have "⋀ t . t |∈| qtrans ⟹ t_target t |∈| fPow (t_target |`| base_trans)"
proof -
fix t assume "t |∈| qtrans"
then have *: "fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using qtrans_prop by blast
then have "fset (t_target t) ⊆ t_target ` (fset base_trans)"
by (metis (mono_tags, lifting) imageI image_Collect_subsetI)
then show "t_target t |∈| fPow (t_target |`| base_trans)"
qed
then have "t_target |`| qtrans |⊆| (fPow (t_source |`| base_trans |∪| t_target |`| base_trans))"
by fastforce
moreover have " ¬ fcard (fPow (t_source |`| base_trans |∪| t_target |`| base_trans) |-| (dones |∪| nexts |∪| t_target |`| qtrans))
< fcard (fPow (t_source |`| base_trans |∪| t_target |`| base_trans) |-| (dones |∪| nexts))"
using assm1 unfolding qtrans_def by force
ultimately have "t_target |`| qtrans |⊆| dones |∪| nexts"
using fcard_fsubset by fastforce
moreover have "q' |∈| t_target |`| qtrans"
using assm2 unfolding qtrans_def by force
ultimately have "q' |∈| dones"
using ‹q' |∉| nexts› by blast
} note t = this

show ?thesis
apply (relation "measure (λ (base_trans, nexts, dones, ts) . fcard ((fPow (t_source |`| base_trans |∪| t_target |`| base_trans)) |-| (dones |∪| nexts)))")
apply auto
by (erule t)
qed

lemma make_observable_transitions_mono: "ts |⊆| (make_observable_transitions base_trans nexts dones ts)"
proof (induction rule: make_observable_transitions.induct[of "λ base_trans nexts dones ts . ts |⊆| (make_observable_transitions base_trans nexts dones ts)"])
case (1 base_trans nexts dones ts)

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' | t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

let ?dones' = "dones |∪| nexts"
let ?ts'    = "ts |∪| qtrans"
let ?nexts' = "(fimage t_target qtrans) |-| ?dones'"

have res_cases: "make_observable_transitions base_trans nexts dones ts = (if ?nexts' = {||}
then ?ts'
else make_observable_transitions base_trans ?nexts' ?dones' ?ts')"
unfolding make_observable_transitions.simps[of base_trans nexts dones ts] qtrans_def Let_def by simp

show ?case proof (cases "?nexts' = {||}")
case True
then show ?thesis using res_cases by simp
next
case False
then have "make_observable_transitions base_trans nexts dones ts = make_observable_transitions base_trans ?nexts' ?dones' ?ts'"
using res_cases by simp
moreover have "ts |∪| qtrans |⊆| make_observable_transitions base_trans ?nexts' ?dones' ?ts'"
using "1"[OF qtrans_def _ _ _ False, of ?dones' ?ts'] by blast
ultimately show ?thesis
by blast
qed
qed

inductive pathlike :: "('state, 'input, 'output) transition fset ⇒ 'state ⇒ ('state, 'input, 'output) path ⇒ bool"
where
nil[intro!] : "pathlike ts q []" |
cons[intro!] : "t |∈| ts ⟹ pathlike ts (t_target t) p ⟹ pathlike ts (t_source t) (t#p)"

inductive_cases pathlike_nil_elim[elim!]: "pathlike ts q []"
inductive_cases pathlike_cons_elim[elim!]: "pathlike ts q (t#p)"

lemma make_observable_transitions_t_source :
assumes "⋀ t . t |∈| ts ⟹ t_source t |∈| dones ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
and     "⋀ q t' . q |∈| dones ⟹ t' |∈| base_trans ⟹ t_source t' |∈| q ⟹ ∃ t . t |∈| ts ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t'"
and     "t |∈| make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts"
and     "t_source t |∈| dones"
shows "t |∈| ts"
using assms proof (induction base_trans "(fimage t_target ts) |-| dones" dones ts rule: make_observable_transitions.induct)
case (1 base_trans dones ts)

let ?nexts = "(fimage t_target ts) |-| dones"

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) ?nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| ?nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

let ?dones' = "dones |∪| ?nexts"
let ?ts'    = "ts |∪| qtrans"
let ?nexts' = "(fimage t_target qtrans) |-| ?dones'"

have res_cases: "make_observable_transitions base_trans ?nexts dones ts = (if ?nexts' = {||}
then ?ts'
else make_observable_transitions base_trans ?nexts' ?dones' ?ts')"
unfolding make_observable_transitions.simps[of base_trans ?nexts dones ts] qtrans_def Let_def by simp

show ?case proof (cases "?nexts' = {||}")
case True

then have "make_observable_transitions base_trans ?nexts dones ts = ?ts'"
using res_cases by auto
then have "t |∈| ts |∪| qtrans"
using ‹t |∈| make_observable_transitions base_trans ?nexts dones ts› ‹t_source t |∈| dones› by blast
then show ?thesis
using qtrans_prop "1.prems"(3,4) by blast
next
case False
then have "make_observable_transitions base_trans ?nexts dones ts = make_observable_transitions base_trans ?nexts' ?dones' ?ts'"
using res_cases by simp

have i1: "(⋀t. t |∈| ts |∪| qtrans ⟹
t_source t |∈| dones |∪| ?nexts ∧
t_target t ≠ {||} ∧
fset (t_target t) =
t_target `
{t' . t' |∈| base_trans ∧
t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t})"
using "1.prems"(1) qtrans_prop by blast

have i3: "t_target |`| qtrans |-| (dones |∪| ?nexts) = t_target |`| (ts |∪| qtrans) |-| (dones |∪| ?nexts)"
unfolding "1.prems"(3) by blast

have i2: "(⋀q t'.
q |∈| dones |∪| ?nexts ⟹
t' |∈| base_trans ⟹
t_source t' |∈| q ⟹
∃t. t |∈| ts |∪| qtrans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t')"
proof -
fix q t' assume "q |∈| dones |∪| ?nexts"
and    *:"t' |∈| base_trans"
and    **:"t_source t' |∈| q"

then consider (a) "q |∈| dones" | (b) "q |∈| ?nexts" by blast
then show "∃t. t |∈| ts |∪| qtrans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t'"
proof cases
case a
then show ?thesis using * **
using "1.prems"(2) by blast
next
case b

let ?tgts = "{t'' . t'' |∈| base_trans ∧ t_source t'' |∈| q ∧ t_input t'' = t_input t' ∧ t_output t'' = t_output t'}"
define tgts where tgts: "tgts = Abs_fset (t_target ` ?tgts)"

have "?tgts ⊆ fset base_trans"
by fastforce
then have "finite (t_target ` ?tgts)"
by (meson finite_fset finite_imageI finite_subset)
then have "fset tgts = (t_target ` ?tgts)"
unfolding tgts
using Abs_fset_inverse
by blast

have "?tgts ≠ {}"
using * ** by blast
then have "t_target ` ?tgts ≠ {}"
by blast
then have "tgts ≠ {||}"
using ‹fset tgts = (t_target ` ?tgts)›
by force

then have "(q, t_input t', t_output t', tgts) |∈| qtrans"
using b
unfolding qtrans_prop[of "(q,t_input t',t_output t',tgts)"]
unfolding fst_conv snd_conv
unfolding ‹fset tgts = (t_target ` ?tgts)›[symmetric]
by blast
then show ?thesis
by auto
qed
qed

have "t |∈| make_observable_transitions base_trans ?nexts dones ts ⟹ t_source t |∈| dones |∪| ?nexts ⟹ t |∈| ts |∪| qtrans"
unfolding ‹make_observable_transitions base_trans ?nexts dones ts = make_observable_transitions base_trans ?nexts' ?dones' ?ts'›
using "1.hyps"[OF qtrans_def _ _ _ _ i1 i2] False i3 by force
then have "t |∈| ts |∪| qtrans"
using ‹t |∈| make_observable_transitions base_trans ?nexts dones ts› ‹t_source t |∈| dones› by blast

then show ?thesis
using qtrans_prop "1.prems"(3,4) by blast
qed
qed

lemma make_observable_transitions_path :
assumes "⋀ t . t |∈| ts ⟹ t_source t |∈| dones ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' ∈ transitions M . t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
and     "⋀ q t' . q |∈| dones ⟹ t' ∈ transitions M ⟹ t_source t' |∈| q ⟹ ∃ t . t |∈| ts ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t'"
and     "⋀ q . q |∈| (fimage t_target ts) |-| dones ⟹ q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M)"
and     "⋀ q . q |∈| dones ⟹ q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M |∪| {|initial M|})"
and     "{||} |∉| dones"
and     "q |∈| dones"
shows "(∃ q' p . q' |∈| q ∧ path M q' p ∧ p_io p = io) ⟷ (∃ p'. pathlike (make_observable_transitions (ftransitions M) ((fimage t_target ts) |-| dones) dones ts) q p' ∧ p_io p' = io)"

using assms proof (induction "ftransitions M" "(fimage t_target ts) |-| dones" dones ts  arbitrary: q io rule: make_observable_transitions.induct)
case (1 dones ts q)

let ?obs = "(make_observable_transitions (ftransitions M) ((fimage t_target ts) |-| dones) dones ts)"
let ?nexts = "(fimage t_target ts) |-| dones"

show ?case proof (cases io)
case Nil

have scheme: "⋀ q q' X . q' |∈| q ⟹ q |∈| fPow X ⟹ q' |∈| X"

obtain q' where "q' |∈| q"
using ‹{||} |∉| dones› ‹q |∈| dones›
by (metis all_not_in_conv bot_fset.rep_eq fset_cong)
have "q' |∈| t_source |`| ftransitions M |∪| t_target |`| ftransitions M |∪| {|FSM.initial M|}"
using scheme[OF ‹q' |∈| q› "1.prems"(4)[OF ‹q |∈| dones›]] .
then have "q' ∈ states M"
using ftransitions_source[of q' M]
using ftransitions_target[of q' M]
by force
then have "∃ q' p . q' |∈| q ∧ path M q' p ∧ p_io p = io"
using ‹q' |∈| q› Nil by auto
moreover have "(∃ p'. pathlike ?obs q p' ∧ p_io p' = io)"
using Nil  by auto
ultimately show ?thesis
by simp
next
case (Cons ioT ioP)

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) (ftransitions M);
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) ?nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| ?nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| (ftransitions M) ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

let ?dones' = "dones |∪| ?nexts"
let ?ts'    = "ts |∪| qtrans"
let ?nexts' = "(fimage t_target qtrans) |-| ?dones'"

have res_cases: "make_observable_transitions (ftransitions M) ?nexts dones ts = (if ?nexts' = {||}
then ?ts'
else make_observable_transitions (ftransitions M) ?nexts' ?dones' ?ts')"
unfolding make_observable_transitions.simps[of "ftransitions M" ?nexts dones ts] qtrans_def Let_def by simp

have i1: "(⋀t. t |∈| ts |∪| qtrans ⟹
t_source t |∈| dones |∪| ?nexts ∧
t_target t ≠ {||} ∧
fset (t_target t) =
t_target `
{t' ∈ FSM.transitions M .
t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t})"
using "1.prems"(1) qtrans_prop
using ftransitions_set[of M]
by (metis (mono_tags, lifting) Collect_cong funion_iff)

have i2: "(⋀q t'.
q |∈| dones |∪| ?nexts ⟹
t' ∈ FSM.transitions M ⟹
t_source t' |∈| q ⟹
∃t. t |∈| ts |∪| qtrans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t')"
proof -
fix q t' assume "q |∈| dones |∪| ?nexts"
and    *:"t' ∈ FSM.transitions M"
and    **:"t_source t' |∈| q"

then consider (a) "q |∈| dones" | (b) "q |∈| ?nexts" by blast
then show "∃t. t |∈| ts |∪| qtrans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t'"
proof cases
case a
then show ?thesis using "1.prems"(2) * ** by blast
next
case b

let ?tgts = "{t'' ∈ FSM.transitions M. t_source t'' |∈| q ∧ t_input t'' = t_input t' ∧ t_output t'' = t_output t'}"
have "?tgts ≠ {}"
using * ** by blast

let ?tgts = "{t'' . t'' |∈| ftransitions M ∧ t_source t'' |∈| q ∧ t_input t'' = t_input t' ∧ t_output t'' = t_output t'}"
define tgts where tgts: "tgts = Abs_fset (t_target ` ?tgts)"

have "?tgts ⊆ transitions M"
using ftransitions_set[of M]
by (metis (no_types, lifting) mem_Collect_eq subsetI)
then have "finite (t_target ` ?tgts)"
by (meson finite_imageI finite_subset fsm_transitions_finite)
then have "fset tgts = (t_target ` ?tgts)"
unfolding tgts
using Abs_fset_inverse
by blast

have "?tgts ≠ {}"
using * **
by (metis (mono_tags, lifting) empty_iff ftransitions_set mem_Collect_eq)
then have "t_target ` ?tgts ≠ {}"
by blast
then have "tgts ≠ {||}"
using ‹fset tgts = (t_target ` ?tgts)›
by force

then have "(q, t_input t', t_output t', tgts) |∈| qtrans"
using b
unfolding qtrans_prop[of "(q,t_input t',t_output t',tgts)"]
unfolding fst_conv snd_conv
unfolding ‹fset tgts = (t_target ` ?tgts)›[symmetric]
by blast
then show ?thesis
by auto
qed
qed

have i3: "t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) = t_target |`| qtrans |-| (dones |∪| (t_target |`| ts |-| dones))"
by blast

have i4: "(⋀q. q |∈| t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) ⟹
q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M))"
proof -
fix q assume "q |∈| t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones))"
then have "q |∈| t_target |`| qtrans"
by auto
then obtain t where "t |∈| qtrans" and "t_target t = q"
by auto
then have "fset q = t_target ` {t'. t' |∈| ftransitions M ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
unfolding qtrans_prop by auto
then have "fset q ⊆ t_target ` transitions M"
by (metis (no_types, lifting) ftransitions_set image_Collect_subsetI image_eqI)
then show "q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M)"
by (metis (no_types, lifting) fPowI fset.set_map fset_inject ftransitions_set le_supI2 sup.orderE sup.orderI sup_fset.rep_eq)
qed

have i5: "(⋀q. q |∈| dones |∪| ?nexts ⟹ q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M |∪| {|initial M|}))"
using "1.prems"(4,3) qtrans_prop
by auto

have i7: "{||} |∉| dones |∪| ?nexts"
using "1.prems" by fastforce

show ?thesis
proof (cases "?nexts' ≠ {||}")
case False
then have "?obs = ?ts'"
using res_cases by auto

have "⋀ q io . q |∈| ?dones' ⟹ q ≠ {||} ⟹ (∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = io) ⟷ (∃p'. pathlike ?obs q p' ∧ p_io p' = io)"
proof -
fix q io assume "q |∈| ?dones'" and "q ≠ {||}"
then show "(∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = io) ⟷ (∃p'. pathlike ?obs q p' ∧ p_io p' = io)"
proof (induction io arbitrary: q)
case Nil

have scheme: "⋀ q q' X . q' |∈| q ⟹ q |∈| fPow X ⟹ q' |∈| X"

obtain q' where "q' |∈| q"
using ‹q ≠ {||}› by fastforce
have "q' |∈| t_source |`| ftransitions M |∪| t_target |`| ftransitions M |∪| {|FSM.initial M|}"
using scheme[OF ‹q' |∈| q› i5[OF ‹q |∈| ?dones'›]] .
then have "q' ∈ states M"
using ftransitions_source[of q' M]
using ftransitions_target[of q' M]
by force
then have "∃ q' p . q' |∈| q ∧ path M q' p ∧ p_io p = []"
using ‹q' |∈| q› by auto
moreover have "(∃ p'. pathlike ?obs q p' ∧ p_io p' = [])"
by auto
ultimately show ?case
by simp
next
case (Cons ioT ioP)

have "(∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP) ⟹ (∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP)"
proof -
assume "∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP"
then obtain q' p where "q' |∈| q" and "path M q' p" and "p_io p = ioT # ioP"
by meson

then obtain tM pM where "p = tM # pM"
by auto
then have "tM ∈ transitions M" and "t_source tM |∈| q"
using ‹path M q' p› ‹q' |∈| q› by blast+
then obtain tP where "tP |∈| ts |∪| qtrans"
and   "t_source tP = q"
and   "t_input tP = t_input tM"
and   "t_output tP = t_output tM"
using Cons.prems i2 by blast

have "path M (t_target tM) pM" and "p_io pM = ioP"
using ‹path M q' p› ‹p_io p = ioT # ioP› unfolding ‹p = tM # pM› by auto
moreover have "t_target tM |∈| t_target tP"
using i1[OF ‹tP |∈| ts |∪| qtrans›]
using ‹p = tM # pM› ‹path M q' p› ‹q' |∈| q›
unfolding ‹t_input tP = t_input tM› ‹t_output tP = t_output tM› ‹t_source tP = q›
by fastforce
ultimately have "∃q' p. q' |∈| t_target tP ∧ path M q' p ∧ p_io p = ioP"
using ‹p_io pM = ioP› ‹path M (t_target tM) pM› by blast

have "t_target tP |∈| dones |∪| (t_target |`| ts |-| dones)"
using False ‹tP |∈| ts |∪| qtrans› by blast
moreover have "t_target tP ≠ {||}"
using i1[OF ‹tP |∈| ts |∪| qtrans›] by blast
ultimately obtain pP where "pathlike ?obs (t_target tP) pP" and "p_io pP = ioP"
using Cons.IH ‹∃q' p. q' |∈| t_target tP ∧ path M q' p ∧ p_io p = ioP› by blast
then have "pathlike ?obs q (tP#pP)"
using ‹t_source tP = q› ‹tP |∈| ts |∪| qtrans› ‹?obs = ?ts'› by auto
moreover have "p_io (tP#pP) = ioT # ioP"
using ‹t_input tP = t_input tM› ‹t_output tP = t_output tM› ‹p_io p = ioT # ioP› ‹p = tM # pM› ‹p_io pP = ioP› by simp
ultimately show ?thesis
by auto
qed

moreover have "(∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP) ⟹ (∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP)"
proof -
assume "∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP"
then obtain p' where "pathlike ?ts' q p'" and "p_io p' = ioT # ioP"
unfolding ‹?obs = ?ts'› by meson
then obtain tP pP where "p' = tP#pP"
by auto

then have "t_source tP = q" and "tP |∈| ?ts'"
using ‹pathlike ?ts' q p'› by auto

have "pathlike ?ts' (t_target tP) pP" and "p_io pP = ioP"
using ‹pathlike ?ts' q p'› ‹p_io p' = ioT # ioP› ‹p' = tP#pP› by auto
then have "∃p'. pathlike ?ts' (t_target tP) p' ∧ p_io p' = ioP"
by auto
moreover have "t_target tP |∈| dones |∪| (t_target |`| ts |-| dones)"
using False ‹tP |∈| ts |∪| qtrans› by blast
moreover have "t_target tP ≠ {||}"
using i1[OF ‹tP |∈| ts |∪| qtrans›] by blast

ultimately obtain q'' pM where "q'' |∈| t_target tP" and "path M q'' pM" and "p_io pM = ioP"
using Cons.IH unfolding ‹?obs = ?ts'› by blast

then obtain tM where "t_source tM |∈| q" and "tM ∈ transitions M" and "t_input tM = t_input tP" and "t_output tM = t_output tP" and "t_target tM = q''"
using i1[OF ‹tP |∈| ts |∪| qtrans›]
using ‹q'' |∈| t_target tP›
unfolding ‹t_source tP = q› by force

have "path M (t_source tM) (tM#pM)"
using ‹tM ∈ transitions M› ‹t_target tM = q''› ‹path M q'' pM› by auto
moreover have "p_io (tM#pM) = ioT # ioP"
using ‹p_io pM = ioP› ‹t_input tM = t_input tP› ‹t_output tM = t_output tP› ‹p_io p' = ioT # ioP› ‹p' = tP#pP› by auto
ultimately show ?thesis
using ‹t_source tM |∈| q› by meson
qed
ultimately show ?case
by meson
qed
qed

then show ?thesis
using ‹q |∈| dones› ‹{||} |∉| dones› by blast
next
case True

have "make_observable_transitions (ftransitions M) ?nexts' ?dones' ?ts' = make_observable_transitions (ftransitions M) ?nexts dones ts"
proof (cases "?nexts' = {||}")
case True
then have "?obs = ?ts'"
using qtrans_def by auto
moreover have "make_observable_transitions (ftransitions M) ?nexts' ?dones' ?ts' = ?ts'"
unfolding make_observable_transitions.simps[of "ftransitions M" ?nexts' ?dones' ?ts']
unfolding True Let_def by auto
ultimately show ?thesis
by blast
next
case False
then show ?thesis
unfolding make_observable_transitions.simps[of "ftransitions M" ?nexts dones ts] qtrans_def Let_def by auto
qed

then have IStep: "⋀ q io . q |∈| ?dones' ⟹
(∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = io) =
(∃p'. pathlike (make_observable_transitions (ftransitions M) ?nexts dones ts) q p' ∧ p_io p' = io)"
using "1.hyps"[OF qtrans_def _ _ _ _ i1 i2 i4 i5 i7] True
unfolding i3
by presburger

show ?thesis
unfolding ‹io = ioT # ioP›
proof
show "∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP ⟹ ∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP"
proof -
assume "∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP"
then obtain q' p where "q' |∈| q" and "path M q' p" and "p_io p = ioT # ioP"
by meson

then obtain tM pM where "p = tM # pM"
by auto
then have "tM ∈ transitions M" and "t_source tM |∈| q"
using ‹path M q' p› ‹q' |∈| q› by blast+

then obtain tP where "tP |∈| ts"
and   "t_source tP = q"
and   "t_input tP = t_input tM"
and   "t_output tP = t_output tM"
using "1.prems"(2,6) by blast

then have i9: "t_target tP |∈| dones |∪| ?nexts"
by simp

have "path M (t_target tM) pM" and "p_io pM = ioP"
using ‹path M q' p› ‹p_io p = ioT # ioP› unfolding ‹p = tM # pM› by auto
moreover have "t_target tM |∈| t_target tP"
using "1.prems"(1)[OF ‹tP |∈| ts›] ‹p = tM # pM› ‹path M q' p› ‹q' |∈| q›
unfolding ‹t_input tP = t_input tM› ‹t_output tP = t_output tM› ‹t_source tP = q›
by fastforce
ultimately have "∃q' p. q' |∈| t_target tP ∧ path M q' p ∧ p_io p = ioP"
using ‹p_io pM = ioP› ‹path M (t_target tM) pM› by blast

obtain pP where "pathlike ?obs (t_target tP) pP" and "p_io pP = ioP"
using ‹∃q' p. q' |∈| t_target tP ∧ path M q' p ∧ p_io p = ioP› unfolding IStep[OF i9]
using that by blast

then have "pathlike ?obs q (tP#pP)"
using ‹t_source tP = q› ‹tP |∈| ts› make_observable_transitions_mono by blast
moreover have "p_io (tP#pP) = ioT # ioP"
using ‹t_input tP = t_input tM› ‹t_output tP = t_output tM› ‹p_io p = ioT # ioP› ‹p = tM # pM› ‹p_io pP = ioP› by simp
ultimately show ?thesis
by auto
qed

show "∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP ⟹ ∃q' p. q' |∈| q ∧ path M q' p ∧ p_io p = ioT # ioP"
proof -
assume "∃p'. pathlike ?obs q p' ∧ p_io p' = ioT # ioP"
then obtain p' where "pathlike ?obs q p'" and "p_io p' = ioT # ioP"
by meson
then obtain tP pP where "p' = tP#pP"
by auto

have "⋀ t' . t' |∈| ftransitions M = (t' ∈ transitions M)"
using ftransitions_set
by metis

from ‹p' = tP#pP› have "t_source tP = q" and "tP |∈| ?obs"
using ‹pathlike ?obs q p'› by auto
then have "tP |∈| ts"
using "1.prems"(6) make_observable_transitions_t_source[of ts dones "ftransitions M"] "1.prems"(1,2)
unfolding ‹⋀ t' . t' |∈| ftransitions M = (t' ∈ transitions M)›
by blast
then have i9: "t_target tP |∈| dones |∪| ?nexts"
by simp

have "pathlike ?obs (t_target tP) pP" and "p_io pP = ioP"
using ‹pathlike ?obs q p'› ‹p_io p' = ioT # ioP› ‹p' = tP#pP› by auto
then have "∃p'. pathlike ?obs (t_target tP) p' ∧ p_io p' = ioP"
by auto
then obtain q'' pM where "q'' |∈| t_target tP" and "path M q'' pM" and "p_io pM = ioP"
using IStep[OF i9] by blast

obtain tM where "t_source tM |∈| q" and "tM ∈ transitions M" and "t_input tM = t_input tP" and "t_output tM = t_output tP" and "t_target tM = q''"
using "1.prems"(1)[OF ‹tP |∈| ts›] ‹q'' |∈| t_target tP›
unfolding ‹t_source tP = q›
by force

have "path M (t_source tM) (tM#pM)"
using ‹tM ∈ transitions M› ‹t_target tM = q''› ‹path M q'' pM› by auto
moreover have "p_io (tM#pM) = ioT # ioP"
using ‹p_io pM = ioP› ‹t_input tM = t_input tP› ‹t_output tM = t_output tP› ‹p_io p' = ioT # ioP› ‹p' = tP#pP› by auto
ultimately show ?thesis
using ‹t_source tM |∈| q› by meson
qed
qed
qed
qed
qed

fun observable_fset :: "('a,'b,'c) transition fset ⇒ bool" where
"observable_fset ts = (∀ t1 t2 . t1 |∈| ts ⟶ t2 |∈| ts ⟶
t_source t1 = t_source t2 ⟶ t_input t1 = t_input t2 ⟶ t_output t1 = t_output t2
⟶ t_target t1 = t_target t2)"

lemma make_observable_transitions_observable :
assumes "⋀ t . t |∈| ts ⟹ t_source t |∈| dones ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
and     "observable_fset ts"
shows "observable_fset (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts)"
using assms proof (induction base_trans "(fimage t_target ts) |-| dones" dones ts rule: make_observable_transitions.induct)
case (1 base_trans dones ts)

let ?nexts = "(fimage t_target ts) |-| dones"

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) ?nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| ?nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

let ?dones' = "dones |∪| ?nexts"
let ?ts'    = "ts |∪| qtrans"
let ?nexts' = "(fimage t_target qtrans) |-| ?dones'"

have "observable_fset qtrans"
using qtrans_prop
unfolding observable_fset.simps
by (metis (mono_tags, lifting) Collect_cong fset_inject)
moreover have "t_source |`| qtrans |∩| t_source |`| ts = {||}"
using "1.prems"(1) qtrans_prop by force
ultimately have "observable_fset ?ts'"
using "1.prems"(2) unfolding observable_fset.simps
by blast

have res_cases: "make_observable_transitions base_trans ?nexts dones ts = (if ?nexts' = {||}
then ?ts'
else make_observable_transitions base_trans ?nexts' ?dones' ?ts')"
unfolding make_observable_transitions.simps[of base_trans ?nexts dones ts] qtrans_def Let_def by simp

show ?case proof (cases "?nexts' = {||}")
case True
then have "make_observable_transitions base_trans ?nexts dones ts = ?ts'"
using res_cases by simp
then show ?thesis
using ‹observable_fset ?ts'› by simp
next
case False
then have *: "make_observable_transitions base_trans ?nexts dones ts = make_observable_transitions base_trans ?nexts' ?dones' ?ts'"
using res_cases by simp

have i1: "(⋀t. t |∈| ts |∪| qtrans ⟹
t_source t |∈| dones |∪| ?nexts ∧
t_target t ≠ {||} ∧
fset (t_target t) =
t_target `
{t' . t' |∈| base_trans ∧
t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t})"
using "1.prems"(1) qtrans_prop by blast

have i3: "t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) = t_target |`| qtrans |-| (dones |∪| (t_target |`| ts |-| dones))"
by auto

have i4: "t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) ≠ {||}"
using False by auto

show ?thesis
using "1.hyps"[OF qtrans_def _ _ i3 i4 i1 ‹observable_fset ?ts'›] unfolding * i3 by metis
qed
qed

lemma make_observable_transitions_transition_props :
assumes "⋀ t . t |∈| ts ⟹ t_source t |∈| dones ∧ t_target t |∈| dones |∪| ((fimage t_target ts) |-| dones) ∧ t_input t |∈| t_input |`| base_trans ∧ t_output t |∈| t_output |`| base_trans"
assumes "t |∈| make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts"
shows "t_source t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))"
and "t_target t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))"
and "t_input t |∈| t_input |`| base_trans"
and "t_output t |∈| t_output |`| base_trans"
proof -
have "t_source t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))
∧ t_target t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))
∧ t_input t |∈| t_input |`| base_trans
∧ t_output t |∈| t_output |`| base_trans"
using assms(1,2)
proof (induction base_trans "((fimage t_target ts) |-| dones)" dones ts rule: make_observable_transitions.induct)
case (1 base_trans dones ts)

let ?nexts = "((fimage t_target ts) |-| dones)"

define qtrans where qtrans_def: "qtrans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) base_trans;
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)) ?nexts)"

have qtrans_prop: "⋀ t . t |∈| qtrans ⟷ t_source t |∈| ?nexts ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' . t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using make_observable_transitions_qtrans_helper[OF qtrans_def]
by presburger

let ?dones' = "dones |∪| ?nexts"
let ?ts'    = "ts |∪| qtrans"
let ?nexts' = "(fimage t_target qtrans) |-| ?dones'"

have res_cases: "make_observable_transitions base_trans ?nexts dones ts = (if ?nexts' = {||}
then ?ts'
else make_observable_transitions base_trans ?nexts' ?dones' ?ts')"
unfolding make_observable_transitions.simps[of base_trans ?nexts dones ts] qtrans_def Let_def by simp

have qtrans_trans_prop: "(⋀t. t |∈| qtrans ⟹
t_source t |∈| dones |∪| (t_target |`| ts |-| dones) ∧
t_target t |∈| dones |∪| (t_target |`| ts |-| dones) |∪| (t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones))) ∧
t_input t |∈| t_input |`| base_trans ∧ t_output t |∈| t_output |`| base_trans)" (is "⋀ t . t |∈| qtrans ⟹ ?P t")
proof -
fix t assume "t |∈| qtrans"

then have "t_source t |∈| dones |∪| (t_target |`| ts |-| dones)"
using ‹t |∈| qtrans› unfolding qtrans_prop[of t] by blast
moreover have "t_target t |∈| dones |∪| (t_target |`| ts |-| dones) |∪| (t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)))"
using ‹t |∈| qtrans› "1.prems"(1) by blast
moreover have "t_input t |∈| t_input |`| base_trans ∧ t_output t |∈| t_output |`| base_trans"
proof -
obtain t' where "t' ∈ {t'. t' |∈| base_trans ∧ t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using ‹t |∈| qtrans› unfolding qtrans_prop[of t]
by (metis (mono_tags, lifting) Collect_empty_eq bot_fset.rep_eq empty_is_image fset_inject mem_Collect_eq)
then show ?thesis
by force
qed
ultimately show "?P t"
by blast
qed

show ?case proof (cases "?nexts' = {||}")
case True
then have "t |∈| ?ts'"
using "1.prems"(2) res_cases by force
then show ?thesis
using "1.prems"(1) qtrans_trans_prop
by (metis True fimage_funion funion_fminus_cancel funion_iff res_cases)
next
case False
then have *: "make_observable_transitions base_trans ?nexts dones ts = make_observable_transitions base_trans ?nexts' ?dones' ?ts'"
using res_cases by simp

have i1: "t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) = t_target |`| qtrans |-| (dones |∪| (t_target |`| ts |-| dones))"
by blast

have i2: "t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones)) ≠ {||}"
using False by blast

have i3: "(⋀t. t |∈| ts |∪| qtrans ⟹
t_source t |∈| dones |∪| (t_target |`| ts |-| dones) ∧
t_target t |∈| dones |∪| (t_target |`| ts |-| dones) |∪| (t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones))) ∧
t_input t |∈| t_input |`| base_trans ∧ t_output t |∈| t_output |`| base_trans)"
using "1.prems"(1) qtrans_trans_prop by blast

have i4: "t |∈| make_observable_transitions base_trans (t_target |`| (ts |∪| qtrans) |-| (dones |∪| (t_target |`| ts |-| dones))) (dones |∪| (t_target |`| ts |-| dones)) (ts |∪| qtrans)"
using "1.prems"(2) unfolding * i1 by assumption

show ?thesis
using "1.hyps"[OF qtrans_def _ _ i1 i2 i3 i4] unfolding  i1 unfolding *[symmetric]
using make_observable_transitions_mono[of ts base_trans ?nexts dones] by blast
qed
qed
then show "t_source t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))"
and "t_target t |∈| dones |∪| (t_target |`| (make_observable_transitions base_trans ((fimage t_target ts) |-| dones) dones ts))"
and "t_input t |∈| t_input |`| base_trans"
and "t_output t |∈| t_output |`| base_trans"
by blast+
qed

fun make_observable :: "('a :: linorder,'b :: linorder,'c :: linorder) fsm ⇒ ('a fset,'b,'c) fsm" where
"make_observable M = (let
initial_trans = (let qts = ffilter (λt . t_source t = initial M) (ftransitions M);
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . ({|initial M|},x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios);
nexts = fimage t_target initial_trans |-| {|{|initial M|}|};
ptransitions = make_observable_transitions (ftransitions M) nexts {|{|initial M|}|} initial_trans;
pstates = finsert {|initial M|} (t_target |`| ptransitions);
M' = create_unconnected_fsm_from_fsets {|initial M|} pstates (finputs M) (foutputs M)

lemma make_observable_language_observable :
shows "L (make_observable M) = L M"
and "observable (make_observable M)"
and "initial (make_observable M) = {|initial M|}"
and "inputs (make_observable M) = inputs M"
and "outputs (make_observable M) = outputs M"
proof -

define initial_trans where "initial_trans = (let qts = ffilter (λt . t_source t = initial M) (ftransitions M);
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . ({|initial M|},x,y, t_target |`| ((ffilter (λt . (t_input t, t_output t) = (x,y)) qts)))) ios)"
moreover define ptransitions where "ptransitions = make_observable_transitions (ftransitions M) (fimage t_target initial_trans |-| {|{|initial M|}|}) {|{|initial M|}|} initial_trans"
moreover define pstates where "pstates = finsert {|initial M|} (t_target |`| ptransitions)"
moreover define M' where "M' = create_unconnected_fsm_from_fsets {|initial M|} pstates (finputs M) (foutputs M)"

ultimately have "make_observable M = add_transitions M' (fset ptransitions)"
unfolding make_observable.simps Let_def by blast

have "{|initial M|} |∈| pstates"
unfolding pstates_def by blast
have "inputs M' = inputs M"
unfolding M'_def create_unconnected_fsm_from_fsets_simps(3)[OF ‹{|initial M|} |∈| pstates›, of "finputs M" "foutputs M"]
using fset_of_list.rep_eq inputs_as_list_set by fastforce
have "outputs M' = outputs M"
unfolding M'_def create_unconnected_fsm_from_fsets_simps(4)[OF ‹{|initial M|} |∈| pstates›, of "finputs M" "foutputs M"]
using fset_of_list.rep_eq outputs_as_list_set by fastforce
have "states M' = fset pstates" and "transitions M' = {}" and "initial M' = {|initial M|}"
unfolding M'_def create_unconnected_fsm_from_fsets_simps(1,2,5)[OF ‹{|initial M|} |∈| pstates›] by simp+

have initial_trans_prop: "⋀ t . t |∈| initial_trans ⟷ t_source t |∈| {|{|FSM.initial M|}|} ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' ∈ transitions M . t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
proof -
have *:"⋀ t' . t' |∈| ftransitions M = (t' ∈ transitions M)"
using ftransitions_set
by metis
have **: "initial_trans = ffUnion (fimage (λ q . (let qts = ffilter (λt . t_source t |∈| q) (ftransitions M);
ios = fimage (λ t . (t_input t, t_output t)) qts
in fimage (λ(x,y) . (q,x,y, t_target |`| (ffilter (λt . (t_input t, t_output t) = (x,y)) qts))) ios)) {|{|initial M|}|})"
unfolding initial_trans_def by auto
show "⋀ t . t |∈| initial_trans ⟷ t_source t |∈| {|{|FSM.initial M|}|} ∧ t_target t ≠ {||} ∧ fset (t_target t) = t_target ` {t' ∈ transitions M . t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
unfolding make_observable_transitions_qtrans_helper[OF **] *
by presburger
qed

have well_formed_transitions: "⋀ t . t ∈ (fset ptransitions) ⟹ t_source t ∈ states M' ∧ t_input t ∈ inputs M' ∧ t_output t ∈ outputs M' ∧ t_target t ∈ states M'"
(is "⋀ t .  t ∈ (fset ptransitions) ⟹ ?P1 t ∧ ?P2 t ∧ ?P3 t ∧ ?P4 t")
proof -
fix t assume "t ∈ (fset ptransitions)"

then have i2: "t |∈| make_observable_transitions (ftransitions M) (fimage t_target initial_trans |-| {|{|initial M|}|}) {|{|initial M|}|} initial_trans"
using ptransitions_def
by metis

have i1: "(⋀t. t |∈| initial_trans ⟹
t_source t |∈| {|{|FSM.initial M|}|} ∧
t_target t |∈| {|{|FSM.initial M|}|} |∪| (t_target |`| initial_trans |-| {|{|FSM.initial M|}|}) ∧
t_input t |∈| t_input |`| ftransitions M ∧ t_output t |∈| t_output |`| ftransitions M)" (is "⋀ t . t |∈| initial_trans ⟹ ?P t")
proof -
fix t assume *: "t |∈| initial_trans"
then have "t_source t |∈| {|{|FSM.initial M|}|}"
and  "t_target t ≠ {||}"
and  "fset (t_target t) = t_target ` {t' ∈ FSM.transitions M. t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}"
using initial_trans_prop by blast+

have "t_target t |∈| {|{|FSM.initial M|}|} |∪| (t_target |`| initial_trans |-| {|{|FSM.initial M|}|})"
using "*" by blast

moreover have "t_input t |∈| t_input |`| ftransitions M ∧ t_output t |∈| t_output |`| ftransitions M"
proof -
obtain t' where "t' ∈ transitions M" and "t_input t = t_input t'" and "t_output t = t_output t'"
using ‹t_target t ≠ {||}› ‹fset (t_target t) = t_target ` {t' ∈ FSM.transitions M. t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t}›
by (metis (mono_tags, lifting) bot_fset.rep_eq empty_Collect_eq fset_inject image_empty)

have "fset (ftransitions M) = transitions M"
then show ?thesis
unfolding ‹t_input t = t_input t'› ‹t_output t = t_output t'›
using ‹t' ∈ transitions M›
by auto
qed

ultimately show "?P t"
using ‹t_source t |∈| {|{|FSM.initial M|}|}› by blast
qed

have "?P1 t"
using make_observable_transitions_transition_props(1)[OF i1 i2] unfolding pstates_def ptransitions_def ‹states M' = fset pstates›
by (metis finsert_is_funion)
moreover have "?P2 t"
proof-
have "t_input t |∈| t_input |`| ftransitions M"
using make_observable_transitions_transition_props(3)[OF i1 i2] by blast
then have "t_input t ∈ t_input ` transitions M"
using ftransitions_set by (metis (mono_tags, lifting) fset.set_map)
then show ?thesis
using finputs_set fsm_transition_input ‹inputs M' = inputs M› by fastforce
qed
moreover have "?P3 t"
proof-
have "t_output t |∈| t_output |`| ftransitions M"
using make_observable_transitions_transition_props(4)[OF i1 i2] by blast
then have "t_output t ∈ t_output ` transitions M"
using ftransitions_set by (metis (mono_tags, lifting) fset.set_map)
then show ?thesis
using foutputs_set fsm_transition_output ‹outputs M' = outputs M› by fastforce
qed
moreover have "?P4 t"
using make_observable_transitions_transition_props(2)[OF i1 i2] unfolding pstates_def ptransitions_def ‹states M' = fset pstates›
by (metis finsert_is_funion)

ultimately show "?P1 t ∧ ?P2 t ∧ ?P3 t ∧ ?P4 t"
by blast
qed

have "initial (make_observable M) = {|initial M|}"
and "states (make_observable M) = fset pstates"
and "inputs (make_observable M) = inputs M"
and "outputs (make_observable M) = outputs M"
and "transitions (make_observable M) = fset ptransitions"
using add_transitions_simps[OF well_formed_transitions, of "fset ptransitions"]
unfolding ‹make_observable M = add_transitions M' (fset ptransitions)›[symmetric]
‹inputs M' = inputs M› ‹outputs M' = outputs M› ‹initial M' = {|initial M|}› ‹states M' = fset pstates› ‹transitions M' = {}›
by blast+

then show "initial (make_observable M) = {|initial M|}" and "inputs (make_observable M) = inputs M" and "outputs (make_observable M) = outputs M"
by presburger+

have i1: "(⋀t. t |∈| initial_trans ⟹
t_source t |∈| {|{|FSM.initial M|}|} ∧
t_target t ≠ {||} ∧
fset (t_target t) = t_target ` {t' ∈ FSM.transitions M. t_source t' |∈| t_source t ∧ t_input t' = t_input t ∧ t_output t' = t_output t})"
using initial_trans_prop by blast

have i2: "(⋀q t'.
q |∈| {|{|FSM.initial M|}|} ⟹
t' ∈ FSM.transitions M ⟹ t_source t' |∈| q ⟹ ∃t. t |∈| initial_trans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t')"
proof -
fix q t' assume "q |∈| {|{|FSM.initial M|}|}" and "t' ∈ FSM.transitions M" and "t_source t' |∈| q"
then have "q = {|FSM.initial M|}" and "t_source t' = initial M"
by auto

define tgt where "tgt =  t_target ` {t'' ∈ FSM.transitions M. t_source t'' |∈| {|FSM.initial M|} ∧ t_input t'' = t_input t' ∧ t_output t'' = t_output t'}"
have "t_target t' ∈ tgt"
unfolding tgt_def using ‹t' ∈ FSM.transitions M› ‹t_source t' = initial M› by auto
then have "tgt ≠ {}"
by auto

have "finite tgt"
using fsm_transitions_finite[of M] unfolding tgt_def by auto
then have "fset (Abs_fset tgt) = tgt"
then have "Abs_fset tgt ≠ {||}"
using ‹tgt ≠ {}› by auto

let ?t = "({|FSM.initial M|}, t_input t', t_output t', Abs_fset tgt)"
have "?t |∈| initial_trans"
unfolding initial_trans_prop fst_conv snd_conv ‹fset (Abs_fset tgt) = tgt›
unfolding ‹tgt =  t_target ` {t'' ∈ FSM.transitions M. t_source t'' |∈| {|FSM.initial M|} ∧ t_input t'' = t_input t' ∧ t_output t'' = t_output t'}›[symmetric]
using ‹Abs_fset tgt ≠ {||}›
by blast
then show "∃t. t |∈| initial_trans ∧ t_source t = q ∧ t_input t = t_input t' ∧ t_output t = t_output t'"
using ‹q = {|FSM.initial M|}› by auto
qed

have i3: "(⋀q. q |∈| t_target |`| initial_trans |-| {|{|FSM.initial M|}|} ⟹ q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M))"
proof -
fix q assume "q |∈| t_target |`| initial_trans |-| {|{|FSM.initial M|}|}"
then obtain t where "t |∈| initial_trans" and "t_target t = q"
by auto

have "fset q ⊆ t_target ` (transitions M)"
using ‹t |∈| initial_trans›
unfolding initial_trans_prop ‹t_target t = q›
by auto
then have "q |⊆| (t_target |`| ftransitions M)"
using ftransitions_set[of M]
then show "q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M)"
by auto
qed

have i4: "(⋀q. q |∈| {|{|FSM.initial M|}|} ⟹ q |∈| fPow (t_source |`| ftransitions M |∪| t_target |`| ftransitions M |∪| {|FSM.initial M|}))"
and i5: "{||} |∉| {|{|FSM.initial M|}|}"
and i6: "{|FSM.initial M|} |∈| {|{|FSM.initial M|}|}"
by blast+

show "L (make_observable M) = L M"
proof -
have *: "⋀ p . pathlike ptransitions {|initial M|} p = path (make_observable M) {|initial M|} p"
proof
have "⋀ q p . p ≠ [] ⟹ pathlike ptransitions q p ⟹ path (make_observable M) q p"
proof -
fix q p assume "p ≠ []" and "pathlike ptransitions q p"
then show "path (make_observable M) q p"
proof (induction p arbitrary: q)
case Nil
then show ?case by blast
next
case (Cons t p)
then have "t |∈| ptransitions" and "pathlike ptransitions (t_target t) p" and "t_source t = q"
by blast+

have "t ∈ transitions (make_observable M)"
using ‹t |∈| ptransitions› ‹transitions (make_observable M) = fset ptransitions›
by metis
moreover have "path (make_observable M) (t_target t) p"
using Cons.IH[OF _ ‹pathlike ptransitions (t_target t) p›] calculation by blast
ultimately show ?case
using ‹t_source t = q› by blast
qed
qed
then show "⋀ p . pathlike ptransitions {|initial M|} p ⟹ path (make_observable M) {|initial M|} p"
by (metis ‹FSM.initial (make_observable M) = {|FSM.initial M|}› fsm_initial path.nil)

show "⋀ q p . path (make_observable M) q p ⟹ pathlike ptransitions q p"
proof -
fix q p assume "path (make_observable M) q p"
then show "pathlike ptransitions q p"
proof (induction p arbitrary: q rule: list.induct)
case Nil
then show ?case by blast
next
case (Cons t p)
then have "t ∈ transitions (make_observable M)" and "path (make_observable M) (t_target t) p" and "t_source t = q"
by blast+

have "t |∈| ptransitions"
using ‹t ∈ transitions (make_observable M)› ‹transitions (make_observable M) = fset ptransitions›
by metis
then show ?case
using Cons.IH[OF ‹path (make_observable M) (t_target t) p›] ‹t_source t = q› by blast
qed
qed
qed

have "⋀ io . (∃q' p. q' |∈| {|FSM.initial M|} ∧ path M q' p ∧ p_io p = io) = (∃p'. pathlike ptransitions {|FSM.initial M|} p' ∧ p_io p' = io)"
using make_observable_transitions_path[OF i1 i2 i3 i4 i5 i6] unfolding ptransitions_def[symmetric] by blast
then have "⋀ io . (∃p. path M (FSM.initial M) p ∧ p_io p = io) = (∃p' . path (make_observable<```