Theory Input_Output_Language_Conformance

theory Input_Output_Language_Conformance
  imports "HOL-Library.Sublist"
begin


section ‹Preliminaries›

type_synonym ('a) alphabet = "'a set"
type_synonym ('x,'y) word = "('x × 'y) list"
type_synonym ('x,'y) language = "('x,'y) word set"
type_synonym ('y) output_relation = "('y set × 'y set) set"



fun is_language :: "'x alphabet  'y alphabet  ('x,'y) language  bool" where 
  "is_language X Y L = (
      ― ‹nonempty›
      (L  {}) 
      ( π  L .
        ― ‹over X and Y›
        ( xy  set π . fst xy  X  snd xy  Y) 
        ― ‹prefix closed›
        ( π' . prefix π' π  π'  L)))"

lemma language_contains_nil : 
  assumes "is_language X Y L"
shows "[]  L"
  using assms by auto

lemma language_intersection_is_language : 
  assumes "is_language X Y L1" 
  and     "is_language X Y L2"
shows "is_language X Y (L1  L2)" 
  using assms 
  using language_contains_nil[OF assms(1)] language_contains_nil[OF assms(2)]
  unfolding is_language.simps
  by (metis IntD1 IntD2 IntI disjoint_iff) 
  


fun language_for_state :: "('x,'y) language  ('x,'y) word  ('x,'y) language" where
  "language_for_state L π = {τ . π@τ  L}" 

notation language_for_state ("ℒ[_,_]")

lemma language_for_state_is_language :
  assumes "is_language X Y L"
  and     "π  L"
shows "is_language X Y ℒ[L,π]"
proof -
  have " τ . τ  ℒ[L,π]  ( xy  set τ . fst xy  X  snd xy  Y)  ( τ' . prefix τ' τ  τ'  ℒ[L,π])"
  proof -
    fix τ assume "τ  ℒ[L,π]"
    then have "π@τ  L" by auto
    then have " xy . xy  set (π@τ)  fst xy  X  snd xy  Y"
          and " π' . prefix π' (π@τ)  π'  L"
      using assms(1) by auto
    
    have " xy . xy  set τ  fst xy  X  snd xy  Y"
      using  xy . xy  set (π@τ)  fst xy  X  snd xy  Y by auto
    moreover have " τ' . prefix τ' τ  τ'  ℒ[L,π]"
      by (simp add: π'. prefix π' (π @ τ)  π'  L)
    ultimately show "( xy  set τ . fst xy  X  snd xy  Y)  ( τ' . prefix τ' τ  τ'  ℒ[L,π])"
      by simp
  qed
  moreover have "ℒ[L,π]  {}"
    using assms(2)
    by (metis (no_types, lifting) append.right_neutral empty_Collect_eq language_for_state.simps) 
  ultimately show ?thesis
    by simp
qed


lemma language_of_state_empty_iff : 
  assumes "is_language X Y L"
shows "(ℒ[L,π] = {})  (π  L)" 
  using assms unfolding is_language.simps language_for_state.simps
  by (metis Collect_empty_eq append.right_neutral prefixI)



fun are_equivalent_for_language :: "('x,'y) language  ('x,'y) word  ('x,'y) word  bool" where
  "are_equivalent_for_language L α β = (ℒ[L,α] = ℒ[L,β])"


abbreviation(input) "input_projection π  map fst π"
abbreviation(input) "output_projection π  map snd π"
notation input_projection ("[_]I")
notation output_projection ("[_]O")


fun is_executable :: "('x,'y) language  ('x,'y) word  'x list  bool" where
  "is_executable L π xs = ( τ  ℒ[L,π] . [τ]I = xs)"

fun executable_sequences :: "('x,'y) language  ('x,'y) word  'x list set" where 
  "executable_sequences L π = {xs . is_executable L π xs}"

fun executable_inputs :: "('x,'y) language  ('x,'y) word  'x set" where 
  "executable_inputs L π = {x . is_executable L π [x]}"

notation executable_inputs ("exec[_,_]")


lemma executable_sequences_alt_def : "executable_sequences L π = {xs .  ys . length ys = length xs  zip xs ys  ℒ[L,π]}"
proof -
  have *:" A xs . (τA. map fst τ = xs) = (ys. length ys = length xs  zip xs ys  A)"
    by (metis length_map map_fst_zip zip_map_fst_snd)

  show ?thesis
    unfolding executable_sequences.simps is_executable.simps
    unfolding * 
    by simp
qed

lemma executable_inputs_alt_def : "executable_inputs L π = {x .  y . [(x,y)]  ℒ[L,π]}" 
proof -
  have *:" A xs . (τA. map fst τ = xs) = (ys. length ys = length xs  zip xs ys  A)"
    by (metis length_map map_fst_zip zip_map_fst_snd)

  have **: " A x . (ys. length ys = length [x]  zip [x] ys  A) = ( y . [(x,y)]  A)"
    by (metis length_Suc_conv length_map zip_Cons_Cons zip_Nil) 

  show ?thesis
    unfolding executable_inputs.simps is_executable.simps 
    unfolding * 
    unfolding **
    by fastforce 
qed

lemma executable_inputs_in_alphabet : 
  assumes "is_language X Y L"
  and     "x  exec[L,π]"
shows "x  X" 
  using assms unfolding executable_inputs_alt_def by auto


fun output_sequences :: "('x,'y) language  ('x,'y) word  'x list  'y list set" where 
  "output_sequences L π xs = output_projection ` {τ  ℒ[L,π] . [τ]I = xs}"

lemma prefix_closure_no_member :
  assumes "is_language X Y L"
  and     "π  L"
shows "π@τ  L"
  by (meson assms(1) assms(2) is_language.elims(2) prefixI)


lemma output_sequences_empty_iff :
  assumes "is_language X Y L"
shows  "(output_sequences L π xs = {}) = ((π  L)  (¬ is_executable L π xs))"  
  unfolding output_sequences.simps is_executable.simps language_for_state.simps
  using Collect_empty_eq assms image_empty mem_Collect_eq prefix_closure_no_member by auto



fun outputs :: "('x,'y) language  ('x,'y) word  'x  'y set" where 
  "outputs L π x = {y . [(x,y)]  ℒ[L,π]}"

notation outputs ("out[_,_,_]")

lemma outputs_in_alphabet :
  assumes "is_language X Y L"
shows "out[L,π,x]  Y"
  using assms by auto

lemma outputs_executable : "(out[L,π,x] = {})  (x  exec[L,π])"
  by auto



fun is_completely_specified_for :: "'x set  ('x,'y) language  bool" where 
  "is_completely_specified_for X L = ( π  L .  x  X . out[L,π,x]  {})"


lemma prefix_executable : 
  assumes "is_language X Y L"
  and     "π  L"
  and     "i < length π"
shows "fst (π ! i)  exec[L,take i π]"
proof - 
  define π' where "π' = take i π"
  moreover define π'' where "π'' = drop (Suc i) π"
  moreover define xy where "xy = π ! i" 
  ultimately have "π = π'@[xy]@π''"
    by (simp add: Cons_nth_drop_Suc assms(3))
  then have "π'@[xy]  L"
    using assms(1,2) by auto
  then show ?thesis
    unfolding π'_def xy_def 
    unfolding executable_inputs_alt_def language_for_state.simps
    by (metis (mono_tags, lifting) CollectI eq_fst_iff) 
qed


section ‹Conformance Relations›

definition language_equivalence :: "('x,'y) language  ('x,'y) language  bool" where 
  "language_equivalence L1 L2 = (L1 = L2)"

definition language_inclusion :: "('x,'y) language  ('x,'y) language  bool" where 
  "language_inclusion L1 L2 = (L1  L2)"

abbreviation(input) "reduction L1 L2  language_inclusion L1 L2"

definition quasi_equivalence :: "('x,'y) language  ('x,'y) language  bool" where 
  "quasi_equivalence L1 L2 = ( π  L1  L2 .  x  exec[L2,π] . out[L1,π,x] = out[L2,π,x])"

definition quasi_reduction :: "('x,'y) language  ('x,'y) language  bool" where 
  "quasi_reduction L1 L2 = ( π  L1  L2 .  x  exec[L2,π] . (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x]))"

definition strong_reduction :: "('x,'y) language  ('x,'y) language  bool" where 
  "strong_reduction L1 L2 = (quasi_reduction L1 L2  ( π  L1  L2 .  x . out[L2,π,x] = {}  out[L1,π,x] = {}))"

definition semi_equivalence :: "('x,'y) language  ('x,'y) language  bool" where 
  "semi_equivalence L1 L2 = ( π  L1  L2 .  x  exec[L2,π] . 
      (out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]) 
      ( x' . out[L1,π,x']  out[L2,π,x']  {}))"

definition semi_reduction :: "('x,'y) language  ('x,'y) language  bool" where 
  "semi_reduction L1 L2 = ( π  L1  L2 .  x  exec[L2,π] . 
      (out[L1,π,x]  out[L2,π,x]) 
      ( x' . out[L1,π,x']  out[L2,π,x']  {}))"

definition strong_semi_equivalence :: "('x,'y) language  ('x,'y) language  bool" where 
  "strong_semi_equivalence L1 L2 = ( π  L1  L2 .  x . 
      (x  exec[L2,π]  ((out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x])  ( x' . out[L1,π,x']  out[L2,π,x']  {}))) 
      (x  exec[L2,π]  out[L1,π,x] = {}))"

definition strong_semi_reduction :: "('x,'y) language  ('x,'y) language  bool" where 
  "strong_semi_reduction L1 L2 = ( π  L1  L2 .  x . 
      (x  exec[L2,π]  (out[L1,π,x]  out[L2,π,x]  ( x' . out[L1,π,x']  out[L2,π,x']  {}))) 
      (x  exec[L2,π]  out[L1,π,x] = {}))"



section ‹Unifying Characterisations›


subsection ‹$\preceq$ Conformance›

fun type_1_conforms :: "('x,'y) language  'x alphabet  'y output_relation  ('x,'y) language  bool" where
  "type_1_conforms L1 X H L2 = ( π  L1  L2 .  x  X . (out[L1,π,x],out[L2,π,x])  H)"

notation type_1_conforms ("_ ≼[_,_] _")

fun equiv :: "'y alphabet  'y output_relation" where 
  "equiv Y = {(A,A) | A . A  Y}"

fun red :: "'y alphabet  'y output_relation" where 
  "red Y = {(A,B) | A B . A  B  B  Y}"

fun quasieq :: "'y alphabet  'y output_relation" where 
  "quasieq Y = {(A,A) | A . A  Y}  {(A,{}) | A . A  Y}"

fun quasired :: "'y alphabet  'y output_relation" where 
  "quasired Y = {(A,B) | A B . A  {}  A  B  B  Y}  {(C,{}) | C . C  Y}"

fun strongred :: "'y alphabet  'y output_relation" where 
  "strongred Y = {(A,B) | A B . A  {}  A  B  B  Y}  {({},{})}"



lemma red_type_1 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "reduction L1 L2  (L1 ≼[X,red Y] L2)"
unfolding language_inclusion_def proof 
  show "L1  L2  L1 ≼[X,red Y] L2"
    using outputs_in_alphabet[OF assms(2)]  
    unfolding type_1_conforms.simps red.simps
    by auto

  show "L1 ≼[X,red Y] L2  L1  L2" 
  proof 
    fix π assume "π  L1" and "L1 ≼[X,red Y] L2" 

    then show "π  L2" proof (induction π rule: rev_induct)
      case Nil
      then show ?case using assms(2) by auto
    next
      case (snoc xy π)
      then have "π  L1" and "π  L1  L2"
        using assms(1) by auto
      
      obtain x y where "xy = (x,y)"
        by fastforce 
      then have "y  out[L1,π,x]" 
        using snoc.prems(1)
        by simp 
      moreover have "x  X" and "y  Y"
        using snoc.prems(1) assms(1) unfolding xy = (x,y) by auto
      ultimately have "y  out[L2,π,x]"
        using snoc.prems(2) π  L1  L2
        unfolding type_1_conforms.simps
        by fastforce
      then show ?case
        using xy = (x, y) by auto 
    qed
  qed
qed


lemma equiv_by_reduction : "(L1 ≼[X,equiv Y] L2)  ((L1 ≼[X,red Y] L2)  (L2 ≼[X,red Y] L1))"
  by fastforce 

lemma equiv_type_1 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(L1 = L2)  (L1 ≼[X,equiv Y] L2)"
  unfolding equiv_by_reduction
  unfolding red_type_1[OF assms(1,2), symmetric] 
  unfolding red_type_1[OF assms(2,1), symmetric]
  unfolding language_inclusion_def
  by blast


lemma quasired_type_1 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "quasi_reduction L1 L2  (L1 ≼[X,quasired Y] L2)"
proof 
  have " π x . quasi_reduction L1 L2  π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  quasired Y"
  proof - 
    fix π x assume "quasi_reduction L1 L2" and "π  L1  L2" and "x  X"

    show "(out[L1,π,x], out[L2,π,x])  quasired Y" 
    proof (cases "x  exec[L2,π]")
      case False
      then show ?thesis
        by (metis (mono_tags, lifting) CollectI UnCI assms(1) outputs_executable outputs_in_alphabet quasired.elims)
    next
      case True
      then obtain y where "y  out[L2,π,x]" by auto
      then have "out[L1,π,x]  out[L2,π,x]" and "out[L1,π,x]  {}"
        using π  L1  L2 x  X quasi_reduction L1 L2
        unfolding quasi_reduction_def by force+
      moreover have "out[L2,π,x]  Y"
        by (meson assms(2) outputs_in_alphabet) 
      ultimately show ?thesis
        unfolding quasired.simps by blast 
    qed
  qed
  then show "quasi_reduction L1 L2  (L1 ≼[X,quasired Y] L2)"
    by auto


  have " π x . L1 ≼[X,quasired Y] L2  π  L1  L2  x  exec[L2,π]  out[L1,π,x]  out[L2,π,x]"
   and " π x . L1 ≼[X,quasired Y] L2  π  L1  L2  x  exec[L2,π]  out[L1,π,x]  {}"
  proof -
    fix π x assume "L1 ≼[X,quasired Y] L2" and "π  L1  L2" and "x  exec[L2,π]"
    then have "x  X"
      using executable_inputs_in_alphabet[OF assms(2)] by auto 

    have "out[L2,π,x]  {}"
      using x  exec[L2,π]
      by (meson outputs_executable) 
    moreover have "(out[L1,π,x],out[L2,π,x])  quasired Y"
      by (meson L1 ≼[X,quasired Y] L2 π  L1  L2 x  X type_1_conforms.elims(2))
    ultimately show "out[L1,π,x]  out[L2,π,x]"
                and "out[L1,π,x]  {}"
      unfolding quasired.simps 
      by blast+
  qed 
  then show "L1 ≼[X,quasired Y] L2  quasi_reduction L1 L2"
    by (meson quasi_reduction_def) 
qed


lemma quasieq_type_1 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "quasi_equivalence L1 L2  (L1 ≼[X,quasieq Y] L2)" 
proof     
  have " π x . quasi_equivalence L1 L2  π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  quasieq Y"
  proof - 
    fix π x assume "quasi_equivalence L1 L2" and "π  L1  L2" and "x  X"

    show "(out[L1,π,x], out[L2,π,x])  quasieq Y" 
    proof (cases "x  exec[L2,π]")
      case False
      then show ?thesis
        by (metis (mono_tags, lifting) CollectI UnCI assms(1) outputs_executable outputs_in_alphabet quasieq.simps)
    next
      case True
      then show ?thesis
        by (metis (mono_tags, lifting) CollectI UnCI π  L1  L2 quasi_equivalence L1 L2 assms(1) outputs_in_alphabet quasi_equivalence_def quasieq.simps) 
    qed
  qed    
  then show "quasi_equivalence L1 L2  (L1 ≼[X,quasieq Y] L2)"
    by auto


  have " π x . L1 ≼[X,quasieq Y] L2  π  L1  L2  x  exec[L2,π]  out[L1,π,x] = out[L2,π,x]"
  proof -
    fix π x assume "L1 ≼[X,quasieq Y] L2" and "π  L1  L2" and "x  exec[L2,π]"
    then have "x  X"
      using executable_inputs_in_alphabet[OF assms(2)] by auto 

    have "out[L2,π,x]  {}"
      using x  exec[L2,π]
      by (meson outputs_executable) 
    moreover have "(out[L1,π,x],out[L2,π,x])  quasieq Y"
      by (meson L1 ≼[X,quasieq Y] L2 π  L1  L2 x  X type_1_conforms.elims(2))
    ultimately show "out[L1,π,x] = out[L2,π,x]"
      unfolding quasieq.simps 
      by blast
  qed 
  then show "L1 ≼[X,quasieq Y] L2  quasi_equivalence L1 L2"
    by (meson quasi_equivalence_def) 
qed


lemma strongred_type_1 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "strong_reduction L1 L2  (L1 ≼[X,strongred Y] L2)"
proof 
  have " π x . strong_reduction L1 L2  π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  strongred Y"
  proof - 
    fix π x assume "strong_reduction L1 L2" and "π  L1  L2" and "x  X"

    have "out[L2,π,x]  Y"
      using outputs_in_alphabet[OF assms(2)] .

    show "(out[L1,π,x], out[L2,π,x])  strongred Y" 
    proof (cases "x  exec[L2,π]")
      case False
      then have "out[L2,π,x] = {}"
        using outputs_executable by force
      then have "out[L1,π,x] = {}"
        using strong_reduction L1 L2 π  L1  L2  
        unfolding strong_reduction_def by blast
      then show ?thesis
        using out[L2,π,x] = {} by auto 
    next
      case True
      then have "out[L1,π,x]  {}" 
        using strong_reduction L1 L2 π  L1  L2  
        unfolding strong_reduction_def
        by (meson quasi_reduction_def)
      moreover have "out[L1,π,x]  out[L2,π,x]"
        by (meson True π  L1  L2 strong_reduction L1 L2 quasi_reduction_def strong_reduction_def)
      ultimately show ?thesis 
        unfolding strongred.simps 
        using outputs_executable outputs_in_alphabet[OF assms(2)]
        by force
    qed
  qed
  then show "strong_reduction L1 L2  (L1 ≼[X,strongred Y] L2)"
    by auto


  have " π x . L1 ≼[X,strongred Y] L2  π  L1  L2  x  exec[L2,π]  out[L1,π,x]  {}"
   and " π x . L1 ≼[X,strongred Y] L2  π  L1  L2  x  exec[L2,π]  out[L1,π,x]  out[L2,π,x]"
  proof -
    fix π x y assume "L1 ≼[X,strongred Y] L2" and "π  L1  L2" and "x  exec[L2,π]"
    then have "x  X"
      using executable_inputs_in_alphabet[OF assms(2)] by auto 

    have "out[L2,π,x]  {}"
      using x  exec[L2,π]
      by (meson outputs_executable) 
    moreover have "(out[L1,π,x],out[L2,π,x])  strongred Y"
      by (meson L1 ≼[X,strongred Y] L2 π  L1  L2 x  X type_1_conforms.elims(2))
    ultimately show "out[L1,π,x]  {}" and "out[L1,π,x]  out[L2,π,x]"
      unfolding strongred.simps 
      by blast+
  qed 
  moreover have " π x . L1 ≼[X,strongred Y] L2  π  L1  L2  out[L2,π,x] = {}  out[L1,π,x] = {}"
  proof -
    fix π x assume "L1 ≼[X,strongred Y] L2" and "π  L1  L2" and "out[L2,π,x] = {}"

    show "out[L1,π,x] = {}" 
    proof (rule ccontr)
      assume "out[L1,π,x]  {}"
      then have "x  X"
        by (meson assms(1) executable_inputs_in_alphabet outputs_executable) 
      then have "out[L2,π,x]  {}"
        using L1 ≼[X,strongred Y] L2 π  L1  L2 out[L1,π,x]  {} by fastforce 
      then show False 
        using out[L2,π,x] = {} by simp
    qed
  qed
  ultimately show "L1 ≼[X,strongred Y] L2  strong_reduction L1 L2" 
    unfolding strong_reduction_def quasi_reduction_def by blast
qed


subsection ‹$\le$ Conformance›

fun type_2_conforms :: "('x,'y) language  'x alphabet  'y output_relation  ('x,'y) language  bool" where
  "type_2_conforms L1 X H L2 = (
      ( π  L1  L2 .  x  X . (out[L1,π,x],out[L2,π,x])  H) 
      ( π  L1  L2 . exec[L2,π]  {}  ( x . out[L1,π,x]  out[L2,π,x]  {})))"

notation type_2_conforms ("_ ≤[_,_] _")

fun semieq :: "'y alphabet  'y output_relation" where 
  "semieq Y = {(A,A) | A . A  Y}  {({},A) | A . A  Y}  {(A,{}) | A . A  Y}"

fun semired :: "'y alphabet  'y output_relation" where 
  "semired Y = {(A,B) | A B . A  B  B  Y}  {(C,{}) | C . C  Y}"

fun strongsemieq :: "'y alphabet  'y output_relation" where 
  "strongsemieq Y = {(A,A) | A . A  Y}  {({},A) | A . A  Y}"

fun strongsemired :: "'y alphabet  'y output_relation" where 
  "strongsemired Y = {(A,B) | A B . A  B  B  Y}"

lemma strongsemieq_alt_def : "strongsemieq Y = semieq Y  red Y" 
  by auto

lemma strongsemired_alt_def : "strongsemired Y = red Y" 
  by auto


lemma semired_type_2 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(semi_reduction L1 L2)  (L1 ≤[X, semired Y] L2)"
proof 
  show "semi_reduction L1 L2  L1 ≤[X, semired Y] L2"
  proof -
    assume "semi_reduction L1 L2"
    then have p1: " π x . π  L1  L2  x  exec[L2,π]  (out[L1,π,x]  out[L2,π,x])"
         and  p2: " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}"
      unfolding semi_reduction_def by blast+

    have " π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  semired Y"
      by (metis (mono_tags, lifting) CollectI UnCI assms(1) assms(2) outputs_executable outputs_in_alphabet p1 semired.simps)
    moreover have " π x . π  L1  L2  exec[L2,π]  {}  x. out[L1,π,x]  out[L2,π,x]  {}"
      using p2 by fastforce
    ultimately show "L1 ≤[X, semired Y] L2"
      by auto
  qed

  show "L1 ≤[X,semired Y] L2  semi_reduction L1 L2"
  proof -
    assume "L1 ≤[X,semired Y] L2" 
    then have p1 : " π x . π  L1  L2  x  X  (out[L1,π,x],out[L2,π,x])  semired Y"
         and  p2 : " π x . π  L1  L2  exec[L2,π]  {}   x . out[L1,π,x]  out[L2,π,x]  {}"
      by auto

    have " π x . π  L1  L2  x  exec[L2,π]  (out[L1,π,x]  out[L2,π,x])" 
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      then have "(out[L1,π,x],out[L2,π,x])  semired Y"
        using p1 executable_inputs_in_alphabet[OF assms(2)] by auto
      moreover have "out[L2,π,x]  {}"
        using x  exec[L2,π] by auto
      ultimately show "(out[L1,π,x]  out[L2,π,x])" 
        unfolding semired.simps by blast
    qed
    moreover have " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}" 
      using p2 by blast 
    ultimately show ?thesis
      unfolding semi_reduction_def by blast
  qed
qed


lemma semieq_type_2 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(semi_equivalence L1 L2)  (L1 ≤[X, semieq Y] L2)"
proof 
  show "semi_equivalence L1 L2  L1 ≤[X, semieq Y] L2"
  proof -
    assume "semi_equivalence L1 L2"
    then have p1: " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]"
         and  p2: " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}"
      unfolding semi_equivalence_def by blast+

    have " π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  semieq Y"
    proof -
      fix π x assume "π  L1  L2" and "x  X"
      show "(out[L1,π,x], out[L2,π,x])  semieq Y"
      proof (cases "x  exec[L2,π]")
        case True
        then have "out[L2,π,x]  {}" by auto
        then show ?thesis 
          using p1[OF π  L1  L2 True]  
          using outputs_in_alphabet[OF assms(2)]
          unfolding semieq.simps
          by fastforce 
      next
        case False
        then show ?thesis
          by (metis (mono_tags, lifting) CollectI UnI2 assms(1) outputs_executable outputs_in_alphabet semieq.elims)
      qed
    qed
    moreover have " π x . π  L1  L2  exec[L2,π]  {}  x. out[L1,π,x]  out[L2,π,x]  {}"
      using p2 by fastforce
    ultimately show "L1 ≤[X, semieq Y] L2"
      by auto
  qed

  show "L1 ≤[X,semieq Y] L2  semi_equivalence L1 L2"
  proof -
    assume "L1 ≤[X,semieq Y] L2" 
    then have p1 : " π x . π  L1  L2  x  X  (out[L1,π,x],out[L2,π,x])  semieq Y"
         and  p2 : " π x . π  L1  L2  exec[L2,π]  {}   x . out[L1,π,x]  out[L2,π,x]  {}"
      by auto

    have " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]" 
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      then have "(out[L1,π,x],out[L2,π,x])  semieq Y"
        using p1 executable_inputs_in_alphabet[OF assms(2)] by auto
      moreover have "out[L2,π,x]  {}"
        using x  exec[L2,π] by auto
      ultimately show "out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]" 
        unfolding semieq.simps
        by blast
    qed
    moreover have " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}" 
      using p2 by blast 
    ultimately show ?thesis
      unfolding semi_equivalence_def by blast
  qed
qed


lemma strongsemired_type_2 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(strong_semi_reduction L1 L2)  (L1 ≤[X, strongsemired Y] L2)"
proof 
  show "strong_semi_reduction L1 L2  L1 ≤[X, strongsemired Y] L2"
  proof -
    assume "strong_semi_reduction L1 L2"
    then have p1: " π x . π  L1  L2  x  exec[L2,π]  (out[L1,π,x]  out[L2,π,x])"
         and  p2: " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}"
         and  p3: " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}"
      unfolding strong_semi_reduction_def by blast+

    have " π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  strongsemired Y"
      unfolding strongsemired.simps
      by (metis (mono_tags, lifting) CollectI assms(2) outputs_executable outputs_in_alphabet p1 p3 set_eq_subset)
    moreover have " π x . π  L1  L2  exec[L2,π]  {}  x. out[L1,π,x]  out[L2,π,x]  {}"
      using p2 by fastforce
    ultimately show "L1 ≤[X, strongsemired Y] L2"
      by auto
  qed

  show "L1 ≤[X,strongsemired Y] L2  strong_semi_reduction L1 L2"
  proof -
    assume "L1 ≤[X,strongsemired Y] L2" 
    then have p1 : " π x . π  L1  L2  x  X  (out[L1,π,x],out[L2,π,x])  strongsemired Y"
         and  p2 : " π x . π  L1  L2  exec[L2,π]  {}   x . out[L1,π,x]  out[L2,π,x]  {}"
      by auto

    have " π x . π  L1  L2  x  exec[L2,π]  (out[L1,π,x]  out[L2,π,x])" 
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      then have "(out[L1,π,x],out[L2,π,x])  semired Y"
        using p1 executable_inputs_in_alphabet[OF assms(2)] by auto
      moreover have "out[L2,π,x]  {}"
        using x  exec[L2,π] by auto
      ultimately show "(out[L1,π,x]  out[L2,π,x])" 
        unfolding semired.simps by blast
    qed
    moreover have " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}" 
      using p2 by blast 
    moreover have " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}"
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      
      have "(out[L1,π,x],out[L2,π,x])  strongsemired Y"
      proof (cases "x  exec[L1,π]")
        case True
        then show ?thesis
          by (meson π  L1  L2 assms(1) executable_inputs_in_alphabet p1) 
      next
        case False 
        then show ?thesis
          using x  exec[L2,π] by fastforce 
      qed 
      moreover have "out[L2,π,x] = {}"
        using x  exec[L2,π] by auto
      ultimately show "out[L1,π,x] = {}" 
        unfolding strongsemired.simps
        by blast 
    qed
    ultimately show ?thesis
      unfolding strong_semi_reduction_def by blast
  qed
qed


lemma strongsemieq_type_2 : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(strong_semi_equivalence L1 L2)  (L1 ≤[X, strongsemieq Y] L2)"
proof 
  show "strong_semi_equivalence L1 L2  L1 ≤[X, strongsemieq Y] L2"
  proof -
    assume "strong_semi_equivalence L1 L2"
    then have p1: " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]"
         and  p2: " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}"
         and  p3: " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}"
      unfolding strong_semi_equivalence_def by blast+

    have " π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  strongsemieq Y"
    proof -
      fix π x assume "π  L1  L2" and "x  X"
      show "(out[L1,π,x], out[L2,π,x])  strongsemieq Y"
      proof (cases "x  exec[L2,π]")
        case True
        then have "out[L2,π,x]  {}" by auto
        then show ?thesis 
          using p1[OF π  L1  L2 True]  
          using outputs_in_alphabet[OF assms(2)]
          by fastforce 
      next
        case False
        then show ?thesis
          using π  L1  L2 p3 by fastforce
      qed
    qed
    moreover have " π x . π  L1  L2  exec[L2,π]  {}  x. out[L1,π,x]  out[L2,π,x]  {}"
      using p2 by fastforce
    ultimately show "L1 ≤[X, strongsemieq Y] L2"
      by auto
  qed

  show "L1 ≤[X,strongsemieq Y] L2  strong_semi_equivalence L1 L2"
  proof -
    assume "L1 ≤[X,strongsemieq Y] L2" 
    then have p1 : " π x . π  L1  L2  x  X  (out[L1,π,x],out[L2,π,x])  strongsemieq Y"
         and  p2 : " π x . π  L1  L2  exec[L2,π]  {}   x . out[L1,π,x]  out[L2,π,x]  {}"
      by auto

    have " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]" 
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      then have "(out[L1,π,x],out[L2,π,x])  semieq Y"
        using p1 executable_inputs_in_alphabet[OF assms(2)] by auto
      moreover have "out[L2,π,x]  {}"
        using x  exec[L2,π] by auto
      ultimately show "out[L1,π,x] = {}  out[L1,π,x] = out[L2,π,x]" 
        unfolding semieq.simps
        by blast
    qed
    moreover have " π x . π  L1  L2  x  exec[L2,π]   x' . out[L1,π,x']  out[L2,π,x']  {}" 
      using p2 by blast 
    moreover have " π x . π  L1  L2  x  exec[L2,π]  out[L1,π,x] = {}"
    proof -
      fix π x assume "π  L1  L2" and "x  exec[L2,π]"
      
      have "(out[L1,π,x],out[L2,π,x])  strongsemieq Y"
      proof (cases "x  exec[L1,π]")
        case True
        then show ?thesis
          by (meson π  L1  L2 assms(1) executable_inputs_in_alphabet p1) 
      next
        case False 
        then show ?thesis
          using x  exec[L2,π] by fastforce 
      qed 
      moreover have "out[L2,π,x] = {}"
        using x  exec[L2,π] by auto
      ultimately show "out[L1,π,x] = {}" 
        unfolding strongsemieq.simps
        by blast 
    qed
    ultimately show ?thesis
      unfolding strong_semi_equivalence_def by blast
  qed
qed


section ‹Comparing Conformance Relations›

lemma type_1_subset :
  assumes "L1 ≼[X,H1] L2"
  and     "H1  H2"
shows "L1 ≼[X,H2] L2"
  using assms by auto 

lemma type_1_subsets : 
shows "equiv Y  strongred Y"
  and "equiv Y  quasieq Y"
  and "strongred Y  red Y"
  and "strongred Y  quasired Y"
  and "quasieq Y  quasired Y" 
  by auto

lemma type_1_implications : 
shows "L1 ≼[X, equiv Y] L2  L1 ≼[X, strongred Y] L2" 
  and "L1 ≼[X, equiv Y] L2  L1 ≼[X, red Y] L2"
  and "L1 ≼[X, equiv Y] L2  L1 ≼[X, quasired Y] L2"
  and "L1 ≼[X, equiv Y] L2  L1 ≼[X, quasieq Y] L2"
  and "L1 ≼[X, strongred Y] L2  L1 ≼[X, red Y] L2" 
  and "L1 ≼[X, strongred Y] L2  L1 ≼[X, quasired Y] L2" 
  and "L1 ≼[X, quasieq Y] L2  L1 ≼[X, quasired Y] L2" 
  using type_1_subset[OF _ type_1_subsets(4), of L1 X Y L2] 
  using type_1_subset[OF _ type_1_subsets(5), of L1 X Y L2] 
  by auto


lemma type_2_subset :
  assumes "L1 ≤[X,H1] L2"
  and     "H1  H2"
shows "L1 ≤[X,H2] L2"
  using assms by auto 

lemma type_2_subsets : 
shows "strongsemieq Y  strongsemired Y"
  and "strongsemieq Y  semieq Y"
  and "semieq Y  semired Y"
  and "strongsemired Y  semired Y"
  and "strongsemired Y  red Y"
  by auto

lemma type_2_implications : 
shows "L1 ≤[X, strongsemieq Y] L2  L1 ≤[X, strongsemired Y] L2" 
  and "L1 ≤[X, strongsemieq Y] L2  L1 ≤[X, semieq Y] L2"
  and "L1 ≤[X, strongsemieq Y] L2  L1 ≤[X, semired Y] L2"
  and "L1 ≤[X, strongsemired Y] L2  L1 ≤[X, semired Y] L2"
  and "L1 ≤[X, semieq Y] L2  L1 ≤[X, semired Y] L2"
  by auto


lemma type_1_conformance_to_type_2 :
  assumes "is_language X Y L2"
  and     "L1 ≼[X,H1] L2"
  and     "H1  H2"
  and     " A B . (A,B)  H1  B  {}  A  B  {}"
shows "L1 ≤[X,H2] L2"
proof -
  have " π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  H2"
    using assms(2,3) by auto
  moreover have " π . π  L1  L2  exec[L2,π]  {}  x. out[L1,π,x]  out[L2,π,x]  {}"
  proof -
    fix π assume "π  L1  L2" and "exec[L2,π]  {}"
    then obtain x where "x  exec[L2,π]"
      by blast
    then have "x  X"
      by (meson assms(1) executable_inputs_in_alphabet)
    then have "(out[L1,π,x], out[L2,π,x])  H1"
      using π  L1  L2 assms(2) by auto
    moreover have "out[L2,π,x]  {}"
      by (meson x  exec[L2,π] outputs_executable) 
    ultimately have "out[L1,π,x]  out[L2,π,x]  {}" 
      using assms(4) by blast 
    then show "x. out[L1,π,x]  out[L2,π,x]  {}" 
      by blast
  qed
  ultimately show ?thesis 
    by auto
qed

lemma type_1_and_2_mixed_implications : 
  assumes "is_language X Y L2"
shows "L1 ≤[X, strongsemieq Y] L2  L1 ≼[X, red Y] L2"
  and "L1 ≤[X, strongsemired Y] L2  L1 ≼[X, red Y] L2"
  and "L1 ≼[X, quasieq Y] L2  L1 ≤[X, semieq Y] L2"
  and "L1 ≼[X, quasired Y] L2  L1 ≤[X, semired Y] L2"
  and "L1 ≼[X, equiv Y] L2  L1 ≤[X, strongsemieq Y] L2"
  and "L1 ≼[X, strongred Y] L2  L1 ≤[X, strongsemired Y] L2"
proof -

  show "L1 ≤[X, strongsemieq Y] L2  L1 ≼[X, red Y] L2"
   and "L1 ≤[X, strongsemired Y] L2  L1 ≼[X, red Y] L2"
    by auto

  have " A B . (A,B)  quasieq Y  B  {}  A  B  {}"
    by auto
  moreover have "quasieq Y  semieq Y"
    by auto    
  ultimately show "L1 ≼[X, quasieq Y] L2  L1 ≤[X, semieq Y] L2"
    using type_1_conformance_to_type_2[OF assms] by blast

  have " A B . (A,B)  quasired Y  B  {}  A  B  {}"
    by auto
  moreover have "quasired Y  semired Y"
    unfolding quasired.simps semired.simps by blast
  ultimately show "L1 ≼[X, quasired Y] L2  L1 ≤[X, semired Y] L2"
    using type_1_conformance_to_type_2[OF assms] by blast

  have " A B . (A,B)  equiv Y  B  {}  A  B  {}"
    by auto
  moreover have "equiv Y  strongsemieq Y"
    unfolding equiv.simps strongsemieq.simps by blast
  ultimately show "L1 ≼[X, equiv Y] L2  L1 ≤[X, strongsemieq Y] L2"
    using type_1_conformance_to_type_2[OF assms] by blast

  have " A B . (A,B)  strongred Y  B  {}  A  B  {}"
    by auto
  moreover have "strongred Y  strongsemired Y"
    unfolding strongred.simps strongsemired.simps by blast
  ultimately show "L1 ≼[X, strongred Y] L2  L1 ≤[X, strongsemired Y] L2"
    using type_1_conformance_to_type_2[OF assms] by blast
qed


subsection ‹Completely Specified Languages›

definition partiality_component :: "'y set  'y output_relation" where
  "partiality_component Y = {(A,{}) | A . A  Y}  {({},A) | A . A  Y}"

abbreviation(input) "Π Y  partiality_component Y"


lemma conformance_without_partiality : 
shows "strongsemieq Y - Π Y = semieq Y - Π Y"
  and "semieq Y - Π Y = equiv Y - Π Y"
  and "strongsemired Y - Π Y = semired Y - Π Y"
  and "semired Y - Π Y = red Y - Π Y"
  unfolding partiality_component_def  
  by fastforce+


section ‹Conformance Testing›

type_synonym ('x,'y) state_cover = "('x,'y) language"
type_synonym ('x,'y) transition_cover = "('x,'y) state_cover × 'x set"

fun is_state_cover :: "('x,'y) language  ('x,'y) language  ('x,'y) state_cover  bool" where
  "is_state_cover L1 L2 V = ( π  L1  L2 .  α  V . ℒ[L1,π] = ℒ[L1,α]  ℒ[L2,π] = ℒ[L2,α])"



lemma state_cover_subset : 
assumes "is_language X Y L1" 
    and "is_language X Y L2"
    and "is_state_cover L1 L2 V"
    and "π  L1  L2"
obtains α where "α  V"
            and "α  L1  L2"
            and "ℒ[L1,π] = ℒ[L1,α]"
            and "ℒ[L2,π] = ℒ[L2,α]"
proof -
  obtain α where "α  V"
             and "ℒ[L1,π] = ℒ[L1,α]"
             and "ℒ[L2,π] = ℒ[L2,α]"
    using assms
    by (meson is_state_cover.elims(2)) 
  moreover have "ℒ[L1,π]  {}" and "ℒ[L2,π]  {}"
    by (metis Collect_empty_eq_bot Int_iff append.right_neutral assms(4) empty_def language_for_state.elims)+
  ultimately have "α  L1  L2"
    using language_of_state_empty_iff[OF assms(1)] language_of_state_empty_iff[OF assms(2)]
    by blast
  then show ?thesis using that[OF α  V _ ℒ[L1,π] = ℒ[L1,α] ℒ[L2,π] = ℒ[L2,α]] 
    by blast
qed
  

theorem sufficient_condition_for_type_1_conformance : 
  assumes "is_language X Y L1" 
  and     "is_language X Y L2"
  and     "is_state_cover L1 L2 V"
shows "(L1 ≼[X,H] L2)  ( π  V .  x  X . π  L1  L2  (out[L1,π,x], out[L2,π,x])  H)"
proof 
  show "(L1 ≼[X,H] L2)  ( π  V .  x  X . π  L1  L2  (out[L1,π,x], out[L2,π,x])  H)"
    by auto

  have "( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H)  ( π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  H)"
  proof -
    fix π x assume "( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H)"
               and "π  L1  L2"
               and "x  X" 

    obtain α where "α  V" and "α  L1  L2" and "ℒ[L1,π] = ℒ[L1,α]" and "ℒ[L2,π] = ℒ[L2,α]" 
      using state_cover_subset[OF assms π  L1  L2] by auto
    then have "out[L1,π,x] = out[L1,α,x]" and "out[L2,π,x] = out[L2,α,x]" 
      by force+
    moreover have "(out[L1,α,x], out[L2,α,x])  H"
      using ( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H) α  V x  X α  L1  L2
      by blast
    ultimately show "(out[L1,π,x], out[L2,π,x])  H" 
      by simp
  qed
  then show "πV. xX. π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  L1 ≼[X,H] L2" 
    by auto
qed

theorem sufficient_condition_for_type_2_conformance : 
  assumes "is_language X Y L1" 
  and     "is_language X Y L2"
  and     "is_state_cover L1 L2 V"
shows "(L1 ≤[X,H] L2)  ( π  V .  x  X . π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))"
proof 

  have " π x . (L1 ≤[X,H] L2)  π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {}))"
  proof -
    fix π x assume "L1 ≤[X,H] L2" and "π  V" and "x  X" and "π  L1  L2"

    have "(out[L1,π,x], out[L2,π,x])  H"
      using L1 ≤[X,H] L2 π  L1  L2 x  X by force 
    moreover have "out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})"
      by (metis (no_types, lifting) L1 ≤[X,H] L2 π  L1  L2 assms(2) empty_iff executable_inputs_in_alphabet inf_bot_right outputs_executable type_2_conforms.elims(2))  
    ultimately show "(out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {}))"
      by blast
  qed
  then show "(L1 ≤[X,H] L2)  ( π  V .  x  X . π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))"
    by auto

  have "( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))  ( π x . π  L1  L2  x  X  (out[L1,π,x], out[L2,π,x])  H)"
    by (meson assms(1) assms(2) assms(3) sufficient_condition_for_type_1_conformance type_1_conforms.elims(2)) 
  moreover have "( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))  ( π . π  L1  L2  exec[L2,π]  {}  ( x . out[L1,π,x]  out[L2,π,x]  {}))"
  proof -
    fix π assume "π  L1  L2" 
             and "exec[L2,π]  {}" 
             and *: "( π x . π  V  x  X  π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))"
 
    then obtain x where "x  X" and "out[L2,π,x]  {}"
      by (metis all_not_in_conv assms(2) executable_inputs_in_alphabet outputs_executable)

    moreover obtain α where "α  V"
                        and "α  L1  L2"
                        and "ℒ[L1,π] = ℒ[L1,α]"
                        and "ℒ[L2,π] = ℒ[L2,α]"
      using state_cover_subset[OF assms π  L1  L2] by blast
    ultimately show "( x . out[L1,π,x]  out[L2,π,x]  {})"
      using *
      by (metis outputs.elims) 
  qed
  ultimately show "( π  V .  x  X . π  L1  L2  (out[L1,π,x], out[L2,π,x])  H  (out[L2,π,x]  {}  ( x'  X . out[L1,π,x']  out[L2,π,x']  {})))  (L1 ≤[X,H] L2)"
    by auto
qed


lemma intersections_card_helper : 
  assumes "finite X" 
  and     "finite Y" 
shows "card {A  B | A B . A  X  B  Y}  card X * card Y"
proof -
  have "{A  B | A B . A  X  B  Y} = (λ (A,B) . A  B) ` (X × Y)"
    by auto
  then have "card {A  B | A B . A  X  B  Y}  card (X × Y)"
    by (metis (no_types, lifting) assms(1) assms(2) card_image_le finite_SigmaI) 
  then show ?thesis
    by (simp add: card_cartesian_product) 
qed


lemma prefix_length_take : 
  "(prefix xs ys  length xs  k)  (prefix xs (take k ys))"
proof 
  show "prefix xs ys  length xs  k  prefix xs (take k ys)"
    using prefix_length_prefix take_is_prefix by fastforce 
  show "prefix xs (take k ys)  prefix xs ys  length xs  k"
    by (metis le_trans length_take min.cobounded2 prefix_length_le prefix_order.order_trans take_is_prefix) 
qed


lemma brute_force_state_cover : 
  assumes "is_language X Y L1"
      and "is_language X Y L2"
      and "finite {ℒ[L1,π] | π . π  L1}"
      and "finite {ℒ[L2,π] | π . π  L2}"
      and "card {ℒ[L1,π] | π . π  L1}  n"
      and "card {ℒ[L2,π] | π . π  L2}  m" 
    shows "is_state_cover L1 L2 {α . length α  m * n - 1  ( xy  set α . fst xy  X  snd xy  Y)}" 
proof (rule ccontr)
  let ?V = "{α. length α  m * n - 1  (xyset α. fst xy  X  snd xy  Y)}"
  assume "¬ is_state_cover L1 L2 ?V"


  (* there is a trace of minimal length that is not covered *)

  define is_covered where "is_covered = (λ π .  α  ?V . ℒ[L1,π] = ℒ[L1,α]  ℒ[L2,π] = ℒ[L2,α])"
  define missing_traces where "missing_traces = {τ . τ  L1  L2  ¬is_covered τ}"  
  define τ where "τ = arg_min length (λ π . π  missing_traces)"

  have "missing_traces  {}" 
    using ¬ is_state_cover L1 L2 ?V
    using is_covered_def missing_traces_def by fastforce 
  then have "τ  missing_traces" 
            " τ' . τ'  missing_traces  length τ  length τ'" 
    using arg_min_nat_lemma[where P="(λ π . π  missing_traces)" and m=length] 
    unfolding τ_def[symmetric] by blast+
  then have τ_props: "τ  L1  L2"
                     " α . α  ?V  ¬(ℒ[L1,τ] = ℒ[L1,α]  ℒ[L2,τ] = ℒ[L2,α])"
    unfolding missing_traces_def is_covered_def by blast+
        
  
  (* this length is at least m*n, as otherwise it would be contained in the brute force state cover *)

  have " xy . xy  set τ  fst xy  X  snd xy  Y"
    using τ  L1  L2 assms(1) by auto
  moreover have "τ  ?V"
    using τ_props(2) by blast 
  ultimately have "length τ > m*n-1"
    by simp 


  (* at the same time, L1 × L2 has at most m*n 'states' *)

  let ?L12 = "{ℒ[L1,π] | π . π  L1} × {ℒ[L2,π] | π . π  L2}"

  have "finite ?L12"
    using assms(3,4)
    by blast 

  have "card ?L12  m*n"
    using assms(3,4,5,6)
    by (metis (no_types, lifting) Sigma_cong card_cartesian_product mult.commute mult_le_mono) 


  (* thus, the m*n many prefixes of τ of length up to m*n-1 may visit at most m*n states *)

  let ?visited_states = "{(ℒ[L1,τ'],ℒ[L2,τ']) | τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}"

  have " τ' . τ'  set (prefixes τ)  τ'  L1  L2"
    by (meson τ_props(1) assms(1) assms(2) in_set_prefixes is_language.elims(2) language_intersection_is_language)
  then have "?visited_states  ?L12"
    by blast
  then have "card ?visited_states  m * n"
    using finite ?L12 card ?L12  m * n
    by (meson card_mono dual_order.trans) 


  (* due to the minimality of τ, all prefixes of it must reach distinct states *)

  have no_index_loop : " i j . i < j  j  length τ  ℒ[L1, take i τ]  ℒ[L1, take j τ]  ℒ[L2, take i τ]  ℒ[L2, take j τ]"
  proof (rule ccontr)
    fix i j 
    assume "i < j" and "j  length τ" and "¬ (ℒ[L1,take i τ]  ℒ[L1,take j τ]  ℒ[L2,take i τ]  ℒ[L2,take j τ])"
    then have "ℒ[L1,take i τ] = ℒ[L1,take j τ]" and "ℒ[L2,take i τ] = ℒ[L2,take j τ]"
      by auto

    have "{τ'. τ @ τ'  L1} = {τ'. take j τ @ drop j τ @ τ'  L1}"
      by (metis append.assoc append_take_drop_id)
    have "{τ'. τ @ τ'  L2} = {τ'. take j τ @ drop j τ @ τ'  L2}"
      by (metis append.assoc append_take_drop_id)

    have "ℒ[L1,take i τ @ drop j τ] = ℒ[L1,τ]"
      using ℒ[L1,take i τ] = ℒ[L1,take j τ] 
      unfolding language_for_state.simps 
      unfolding {τ'. τ @ τ'  L1} = {τ'. take j τ @ drop j τ @ τ'  L1} append.assoc by blast
    moreover have "ℒ[L2,take i τ @ drop j τ] = ℒ[L2,τ]"
      using ℒ[L2,take i τ] = ℒ[L2,take j τ] 
      unfolding language_for_state.simps 
      unfolding {τ'. τ @ τ'  L2} = {τ'. take j τ @ drop j τ @ τ'  L2} append.assoc by blast
    
    have "(take i τ @ drop j τ)  missing_traces"
    proof (rule ccontr)
      assume "take i τ @ drop j τ  missing_traces" 
      moreover have "take i τ @ drop j τ  L1  L2"
        by (metis IntD1 IntD2 IntI ℒ[L1,take i τ] = ℒ[L1,take j τ] ℒ[L2,take i τ] = ℒ[L2,take j τ] τ_props(1) append_take_drop_id language_for_state.elims mem_Collect_eq)
      ultimately obtain α where "length α  m * n - 1" 
                                "(xyset α. fst xy  X  snd xy  Y)"
                                "ℒ[L1,take i τ @ drop j τ] = ℒ[L1,α]" 
                                "ℒ[L2,take i τ @ drop j τ] = ℒ[L2,α]"
        unfolding missing_traces_def is_covered_def 
        by blast
      then have "τ  missing_traces" 
        unfolding missing_traces_def is_covered_def 
        using τ  L1  L2
        unfolding ℒ[L1,take i τ @ drop j τ] = ℒ[L1,τ]
        unfolding ℒ[L2,take i τ @ drop j τ] = ℒ[L2,τ] 
        by blast
      then show False
        using τ  missing_traces by simp
    qed
    moreover have "length (take i τ @ drop j τ) < length τ" 
      using i < j j  length τ 
      by (induction τ arbitrary: i j; auto)
    ultimately show False
      using  τ' . τ'  missing_traces  length τ  length τ'
      using leD by blast 
  qed
  
  have no_prefix_loop : " τ' τ'' . τ'  set (prefixes τ)  τ''  set (prefixes τ)  τ'  τ''  (ℒ[L1,τ'],ℒ[L2,τ'])  (ℒ[L1,τ''],ℒ[L2,τ''])"
  proof -
    fix τ' τ'' assume "τ'  set (prefixes τ)" and "τ''  set (prefixes τ)" and "τ'  τ''" 

    obtain i where "τ' = take i τ" and "i  length τ"
      using τ'  set (prefixes τ)
      by (metis append_eq_conv_conj in_set_prefixes linorder_linear prefix_def take_all_iff) 

    obtain j where "τ'' = take j τ" and "j  length τ"
      using τ''  set (prefixes τ)
      by (metis append_eq_conv_conj in_set_prefixes linorder_linear prefix_def take_all_iff)

    have "i  j"
      using τ' = take i τ τ'  τ'' τ'' = take j τ by blast
    then consider (a) "i < j" | (b) "j < i"
      using nat_neq_iff by blast 
    then show "(ℒ[L1,τ'],ℒ[L2,τ'])  (ℒ[L1,τ''],ℒ[L2,τ''])" 
      using no_index_loop
      using j  length τ i  length τ
      unfolding τ' = take i τ τ'' = take j τ 
      by (cases; blast)
  qed
  then have "inj_on (λ τ' . (ℒ[L1,τ'], ℒ[L2,τ'])) {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}" 
    using inj_onI
    by (metis (mono_tags, lifting) mem_Collect_eq) 
  then have "card ((λ τ' . (ℒ[L1,τ'], ℒ[L2,τ'])) ` {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}) = card {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}"
    using card_image by blast
  moreover have "?visited_states = (λ τ' . (ℒ[L1,τ'], ℒ[L2,τ'])) ` {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}"
    by auto
  ultimately have "card ?visited_states = card {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1}" 
    by simp  
  moreover have "card {τ' . τ'  set (prefixes τ)  length τ'  m * n - 1} = m*n" 
  proof -
    have "{τ' . τ'  set (prefixes τ)  length τ'  m * n - 1} = set (prefixes (take (m*n-1) τ))"
      unfolding in_set_prefixes prefix_length_take 
      by auto
    moreover have "length (take (m*n-1) τ) = m*n-1"
      using length τ > m*n-1 by auto
    ultimately show ?thesis 
      using length_prefixes distinct_prefixes
      by (metis card {(ℒ[L1,τ'], ℒ[L2,τ']) |τ'. τ'  set (prefixes τ)  length τ'  m * n - 1} = card {τ'  set (prefixes τ). length τ'  m * n - 1} card {(ℒ[L1,τ'], ℒ[L2,τ']) |τ'. τ'  set (prefixes τ)  length τ'  m * n - 1}  m * n distinct_card less_diff_conv not_less_iff_gr_or_eq order_le_less) 
  qed
  
  
  (* that is, the m*n many prefixes of τ of length up to m*n-1 must visit all m*n states *)

  ultimately have "card ?visited_states = m*n"
    by simp
  then have "?visited_states = ?L12"
    by (metis (no_types, lifting) card ({ℒ[L1,π] |π. π  L1} × {ℒ[L2,π] |π. π  L2})  m * n finite ({ℒ[L1,π] |π. π  L1} × {ℒ[L2,π] |π. π  L2}) {(ℒ[L1,τ'], ℒ[L2,τ']) |τ'. τ'  set (prefixes τ)  length τ'  m * n - 1}  {ℒ[L1,π] |π. π  L1} × {ℒ[L2,π] |π. π  L2} card_seteq) 
    

  (* so that the state reached by τ itself must at the same time be visited and not visited *)

  have "(ℒ[L1,τ], ℒ[L2,τ])  ?L12"
    using τ  L1  L2
    by blast 
  moreover have "(ℒ[L1,τ], ℒ[L2,τ])  ?visited_states" 
  proof 
    assume "(ℒ[L1,τ], ℒ[L2,τ])  ?visited_states"
    then obtain τ' where "(ℒ[L1,τ], ℒ[L2,τ]) = (ℒ[L1,τ'],ℒ[L2,τ'])" 
                     and "τ'  set (prefixes τ)" 
                     and "length τ'  m * n - 1"
      by blast

    then have "τ  τ'"
      using length τ > m*n-1 by auto
    
    show False
      using (ℒ[L1,τ], ℒ[L2,τ]) = (ℒ[L1,τ'],ℒ[L2,τ']) 
      using no_prefix_loop[OF _ τ'  set (prefixes τ) τ  τ'] 
      by simp
  qed
  ultimately show False
    unfolding ?visited_states = ?L12 
    by blast
qed


section ‹Reductions Between Relations›

subsection ‹Quasi-Equivalence via Quasi-Reduction and Absences›

fun absence_completion :: "'x alphabet  'y alphabet  ('x,'y) language  ('x, 'y × bool) language" where 
  "absence_completion X Y L = 
    ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)
     {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L  out[L,π,x]  {}  y  Y  y  out[L,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"

lemma absence_completion_is_language : 
  assumes "is_language X Y L"
shows "is_language X (Y × UNIV) (absence_completion X Y L)" 
proof -       
  let ?L = "(absence_completion X Y L)" 
  have "[]  ?L" 
    using language_contains_nil[OF assms] by auto

  have "?L  {}"
    using language_contains_nil[OF assms] by auto
  moreover have " γ xy . γ  ?L  xy  set γ  fst xy  X  snd xy  (Y × UNIV)"
            and " γ γ' . γ  ?L  prefix γ' γ  γ'  ?L"
  proof -
    fix γ xy γ' assume "γ  ?L" 
    then consider (a) "γ  ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)" | 
                  (b) "γ  {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L  out[L,π,x]  {}  y  Y  y  out[L,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
      unfolding absence_completion.simps by blast
    then have "(xy  set γ  fst xy  X  snd xy  (Y × UNIV))  (prefix γ' γ  γ'  ?L)"
    proof cases
      case a
      then obtain π where *:"γ = map (λ(x,y) . (x,(y,True))) π" and "π  L"
        by auto
      then have p1: " xy . xy  set π  fst xy  X  snd xy  Y"
            and p2: " π' . prefix π' π  π'  L"
        using assms by auto

      have "xy  set γ  fst xy  X  snd xy  (Y × UNIV)"
      proof -
        assume "xy  set γ"
        then have "(fst xy, fst (snd xy))  set π" and "snd (snd xy) = True"
          unfolding * by auto
        then show "fst xy  X  snd xy  (Y × UNIV)"
          by (metis p1 SigmaI UNIV_I fst_conv prod.collapse snd_conv)  
      qed
      moreover have "prefix γ' γ  γ'  ?L" 
      proof -
        assume "prefix γ' γ" 
        then obtain i where "γ' = take i γ"
          by (metis append_eq_conv_conj prefix_def) 
        then have "γ' = map (λ(x,y) . (x,(y,True))) (take i π)"
          unfolding * using take_map by blast 
        moreover have "take i π  L"
          using p2 π  L take_is_prefix by blast 
        ultimately have "γ'  ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)"
          by simp
        then show "γ'  ?L" 
          by auto
      qed
      ultimately show ?thesis by blast
    next
      case b
      then obtain π x y τ where *: "γ = (map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ"
                            and "π  L" 
                            and "out[L,π,x]  {}" 
                            and "y  Y" 
                            and "y  out[L,π,x]" 
                            and "( (x,(y,a))  set τ . x  X  y  Y)"
        by blast
      then have p1: " xy . xy  set π  fst xy  X  snd xy  Y"
            and p2: " π' . prefix π' π  π'  L"
        using assms by auto

      have "x  X"
        using out[L,π,x]  {} assms
        by (meson executable_inputs_in_alphabet outputs_executable) 

      have "xy  set γ  fst xy  X  snd xy  (Y × UNIV)"
      proof -
        assume "xy  set γ" 
        then consider (b1) "xy  set (map (λ(x,y) . (x,(y,True))) π)" | 
                      (b2) "xy = (x,(y,False))" | 
                      (b3) "xy  set τ" 
          unfolding * by force
        then show ?thesis proof cases
          case b1
          then have "(fst xy, fst (snd xy))  set π" and "snd (snd xy) = True"
            unfolding * by auto
          then show "fst xy  X  snd xy  (Y × UNIV)"
            by (metis p1 SigmaI UNIV_I fst_conv prod.collapse snd_conv)
        next
          case b2
          then show ?thesis 
            using x  X y  Y by simp
        next
          case b3
          then show ?thesis 
            using ( (x,(y,a))  set τ . x  X  y  Y) by force
        qed
      qed
      moreover have "prefix γ' γ  γ'  ?L" 
      proof -
        assume "prefix γ' γ" 
        then obtain i where "γ' = take i γ"
          by (metis append_eq_conv_conj prefix_def)
        then consider (b1) "i  length π" | 
                      (b2) "i > length π"
          by linarith
        then show "γ'  ?L" proof cases
          case b1 
          then have "i  length (map (λ(x, y). (x, y, True)) π)" 
            by auto
          then have "γ' = map (λ(x,y) . (x,(y,True))) (take i π)"
            unfolding * γ' = take i γ
            by (simp add: take_map) 
          moreover have "take i π  L"
            using p2 π  L take_is_prefix by blast 
          ultimately have "γ'  ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)"
            by simp
          then show "γ'  ?L" 
            by auto
        next
          case b2 
          then have "i > length (map (λ(x, y). (x, y, True)) π)" 
            by auto

          have " k xs ys . k > length xs  take k (xs@ys) = xs@(take (k - length xs) ys)"
            by simp 
          have take_helper: " k xs y zs . k > length xs  take k (xs@[y]@zs) = xs@[y]@(take (k - length xs - 1) zs)"
            by (metis One_nat_def Suc_pred ys xs k. length xs < k  take k (xs @ ys) = xs @ take (k - length xs) ys append_Cons append_Nil take_Suc_Cons zero_less_diff) 
          
          have **: "γ' = (map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@(take (i - length π - 1) τ)"
            unfolding * γ' = take i γ
            using take_helper[OF i > length (map (λ(x, y). (x, y, True)) π)] by simp
          
          have "( (x,(y,a))  set (take (i - length π - 1) τ) . x  X  y  Y)"
            using ( (x,(y,a))  set τ . x  X  y  Y)
            by (meson in_set_takeD) 
          then show ?thesis 
            unfolding ** absence_completion.simps
            using π  L out[L,π,x]  {} y  Y y  out[L,π,x]
            by blast
        qed
      qed
      ultimately show ?thesis by simp
    qed
    then show "xy  set γ  fst xy  X  snd xy  (Y × UNIV)"
          and "prefix γ' γ  γ'  ?L"
      by blast+
  qed
  ultimately show ?thesis
    unfolding is_language.simps by blast
qed

lemma absence_completion_inclusion_R : 
  assumes "is_language X Y L"
  and     "π  absence_completion X Y L"
shows "(map (λ(x,y,a) . (x,y)) π  L)  ( (x,y,a)  set π . a = True)" 
proof -
  define L'a where "L'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)"
  define L'b where "L'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L  out[L,π,x]  {}  y  Y  y  out[L,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
  

  have " π xya . π  L'a  xya  set π  snd (snd xya) = True" 
    unfolding L'a_def by auto
  moreover have " π . π  L'b   xya  set π . snd (snd xya) = False" 
    unfolding L'b_def by auto
  moreover have "π  L'a  L'b"
    using assms(2) unfolding absence_completion.simps L'a_def L'b_def .
  ultimately have "( (x,y,a)  set π . a = True) = (π  L'a)" 
    by fastforce

  show ?thesis proof (cases "( (x,y,a)  set π . a = True)")
    case True 
    then obtain τ where "π = map (λ(x, y). (x, y, True)) τ"
                    and "τ  L"
      unfolding ( (x,y,a)  set π . a = True) = (π  L'a) L'a_def
      by blast
    
    have "map (λ(x, y, a). (x, y)) π = τ"
      unfolding π = map (λ(x, y). (x, y, True)) τ
      by (induction τ; auto)
    show ?thesis 
      using True τ  L
      unfolding ( (x,y,a)  set π . a = True) = (π  L'a) L'a_def
      unfolding map (λ(x, y, a). (x, y)) π = τ
      unfolding π = map (λ(x, y). (x, y, True)) τ 
      by blast
  next
    case False 
    then have "π  L'b" 
      using ( (x,y,a)  set π . a = True) = (π  L'a) π  L'a  L'b by blast
    then obtain τ x y τ' where "π = (map (λ(x,y) . (x,(y,True))) τ)@[(x,(y,False))]@τ'"
                           and "τ  L" 
                           and "out[L,τ,x]  {}"
                           and "y  Y" 
                           and "y  out[L,τ,x]" 
                           and "( (x,(y,a))  set τ' . x  X  y  Y)"
      unfolding L'b_def by blast
    then have "τ@[(x,y)]  L"
      by fastforce 
    then have "τ@[(x,y)]@(map (λ(x, y, a). (x, y)) τ')  L"
      using assms(1)
      by (metis append.assoc prefix_closure_no_member) 
    moreover have "map (λ(x, y, a). (x, y)) π = τ@[(x,y)]@(map (λ(x, y, a). (x, y)) τ')" 
      unfolding π = (map (λ(x,y) . (x,(y,True))) τ)@[(x,(y,False))]@τ' 
      by (induction τ; auto)
    ultimately have "map (λ(x, y, a). (x, y)) π  L"
      by simp
    then show ?thesis
      using False by blast
  qed
qed

lemma absence_completion_inclusion_L : 
  "(π  L)  (map (λ(x,y) . (x,y,True)) π  absence_completion X Y L)" 
proof -

  let ?L = "absence_completion X Y L"
  define L'a where "L'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)"
  define L'b where "L'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L  out[L,π,x]  {}  y  Y  y  out[L,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
  have "?L = L'a  L'b" 
    unfolding L'a_def L'b_def absence_completion.simps by blast

  have " π . π  L'b   xya  set π . snd (snd xya) = False" 
    unfolding L'b_def by auto
  then have "(map (λ(x,y) . (x,y,True)) π  ?L) = (map (λ(x,y) . (x,y,True)) π  L'a)"
    unfolding ?L = L'a  L'b
    by fastforce 

  have "inj (λ π . map (λ(x,y) . (x,(y,True))) π)"
    by (simp add: inj_def) 
  then show ?thesis 
    unfolding (map (λ(x,y) . (x,y,True)) π  ?L) = (map (λ(x,y) . (x,y,True)) π  L'a)
    unfolding L'a_def
    by (simp add: image_iff inj_def) 
qed


fun is_present :: "('x,'y × bool) word  ('x,'y) language  bool" where
  "is_present π L = (π  map (λ(x, y). (x, y, True)) ` L)"

lemma is_present_rev :
  assumes "is_present π L" 
shows "map (λ(x, y, a). (x, y)) π  L"
proof -
  obtain π' where "π = map (λ(x, y). (x, y, True)) π'" and "π'  L"
    using assms by auto
  moreover have "map (λ(x, y, a). (x, y)) (map (λ(x, y). (x, y, True)) π') = π'"
    by (induction π'; auto)
  ultimately show ?thesis
    by force
qed



lemma absence_completion_out : 
  assumes "is_language X Y L"
  and     "x  X"
  and     "π  absence_completion X Y L"
shows "is_present π L  out[L,map (λ(x, y, a). (x, y)) π,x]  {}  out[absence_completion X Y L, π, x] = {(y,True) | y . y  out[L,map (λ(x, y, a). (x, y)) π,x]}  {(y,False) | y . y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
and   "is_present π L  out[L,map (λ(x, y, a). (x, y)) π,x] = {}  out[absence_completion X Y L, π, x] = {}"
and   "¬ is_present π L  out[absence_completion X Y L, π, x] = Y × UNIV"
proof -

  let ?L = "absence_completion X Y L"
  define L'a where "L'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L)"
  define L'b where "L'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L  out[L,π,x]  {}  y  Y  y  out[L,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
  have "?L = L'a  L'b" 
    unfolding L'a_def L'b_def absence_completion.simps by blast
  then have "out[?L, π, x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b}"
    unfolding outputs.simps language_for_state.simps by blast

  show "is_present π L  out[L,map (λ(x, y, a). (x, y)) π,x]  {}  out[?L, π, x] = {(y,True) | y . y  out[L,map (λ(x, y, a). (x, y)) π,x]}  {(y,False) | y . y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
  proof - 
    assume "is_present π L" and "out[L,map (λ(x, y, a). (x, y)) π,x]  {}"
    then have "map (λ(x, y, a). (x, y)) π  L" 
      using assms(1) by auto 

    have "{y. π @ [(x, y)]  L'a} = {(y,True) | y . y  out[L,map (λ(x, y, a). (x, y)) π,x]}" 
    proof 
      show "{y. π @ [(x, y)]  L'a}  {(y, True) |y. y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
      proof 
        fix ya assume "ya  {y. π @ [(x, y)]  L'a}"
        then have "π @ [(x, ya)]  map (λ(x, y). (x, y, True)) ` L"
          unfolding L'a_def by blast
        then obtain γ where "γ  L" and "π @ [(x, ya)] = map (λ(x, y). (x, y, True)) γ"
          by blast
        then have "length (π @ [(x, ya)]) = length γ"
          by auto
        then obtain γ' xy where "γ = γ'@[xy]"
          by (metis add.right_neutral dual_order.strict_iff_not length_append_singleton less_add_Suc2 rev_exhaust take0 take_all_iff)
        then have "(x,ya) = (λ(x, y). (x, y, True)) xy"
          using π @ [(x, ya)] = map (λ(x, y). (x, y, True)) γ unfolding γ = γ'@[xy] by auto
        then have "ya = (snd xy, True)" and "xy = (x,snd xy)"
          by (simp add: split_beta)+
        moreover define y where "y = snd xy"
        ultimately have "ya = (y, True)" and "xy = (x,y)"
          by auto

        have "π = map (λ(x, y). (x, y, True)) γ'"
          using π @ [(x, ya)] = map (λ(x, y). (x, y, True)) γ unfolding γ = γ'@[xy] by auto
        then have "map (λ(x, y, a). (x, y)) π = γ'" 
          by (induction π arbitrary: γ'; auto)
          
        have "[(x, y)]  {τ. map (λ(x, y, a). (x, y)) π @ τ  L}"
          using γ  L 
          unfolding γ = γ'@[xy] ya = (y, True) xy = (x,y) 
          unfolding map (λ(x, y, a). (x, y)) π = γ' 
          by auto
        then show "ya  {(y, True) |y. y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
          unfolding ya = (snd xy, True) outputs.simps language_for_state.simps 
          unfolding ya = (y, True) xy = (x,y) map (λ(x, y, a). (x, y)) π = γ' 
          by auto
      qed
      show "{(y, True) |y. y  out[L,map (λ(x, y, a). (x, y)) π,x]}  {y. π @ [(x, y)]  L'a}" 
      proof 
        fix ya assume "ya  {(y, True) |y. y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
        then obtain y where "ya = (y,True)" and "y  out[L,map (λ(x, y, a). (x, y)) π,x]"
          by blast
        then have "[(x, y)]  {τ. map (λ(x, y, a). (x, y)) π @ τ  L}"
          unfolding outputs.simps language_for_state.simps by auto
        then have "(map (λ(x, y, a). (x, y)) π) @ [(x,y)]  L"
          by auto
        moreover have "map (λ(x, y). (x, y, True)) ((map (λ(x, y, a). (x, y)) π) @ [(x,y)]) = π @ [(x, (y, True))]"
          using is_present π L unfolding is_present.simps 
          by (induction π arbitrary: x y; auto)
        ultimately have "π @ [(x, (y, True))]  L'a" 
          unfolding L'a_def
          by force 
        then show "ya  {y. π @ [(x, y)]  L'a}"
          unfolding ya = (y,True)
          by blast
      qed
    qed
    moreover have "{y. π @ [(x, y)]  L'b} = {(y,False) | y . y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
    proof 
      show "{y. π @ [(x, y)]  L'b}  {(y, False) |y. y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
      proof 
        fix ya assume "ya  {y. π @ [(x, y)]  L'b}"
        then have "π @ [(x,ya)]  L'b"
          by auto
        then obtain π' x' y' τ where "π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ"
                                 and "π'  L" 
                                 and "out[L,π',x']  {}" 
                                 and "y'  Y" 
                                 and "y'  out[L,π',x']" 
                                 and "((x, y, a)set τ. x  X  y  Y)"
          unfolding L'b_def by blast

        obtain π'' where "π = map (λ(x, y). (x, y, True)) π''" and "π''  L"
          using is_present π L by auto
        then have " xya . xya  set π  snd (snd xya) = True"
          by (induction π; auto) 

        have "τ = []"
        proof (rule ccontr)
          assume "τ  []"
          then obtain τ' xyz where "τ = τ'@[xyz]"
            by (metis append_butlast_last_id)
          then have "π = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ'"
            using π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ
            by auto
          then have "(x', y', False)  set π"
            by auto
          then show False 
            using  xya . xya  set π  snd (snd xya) = True by force
        qed
        then have "x' = x" and "ya = (y', False)" and "π = map (λ(x, y). (x, y, True)) π'"
          using π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ
          by auto

        have *: "map (λ(x, y, a). (x, y)) (map (λ(x, y). (x, y, True)) π') = π'"
          by (induction π'; auto)

        have "y'  out[L,map (λ(x, y, a). (x, y)) π,x]"
          using y'  out[L,π',x'] 
          unfolding outputs.simps language_for_state.simps 
          unfolding π = map (λ(x, y). (x, y, True)) π' x' = x
          unfolding * .
        then show "ya  {(y, False) |y. y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}" 
          using y'  Y
          unfolding ya = (y', False) by auto
      qed

      show "{(y, False) |y. y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}  {y. π @ [(x, y)]  L'b}"
      proof 
        fix ya assume "ya  {(y, False) |y. y  Y  y  out[L,map (λ(x, y, a). (x, y)) π,x]}"
        then obtain y where "ya = (y,False)"
                        and "y  Y"
                        and "y  out[L,map (λ(x, y, a). (x, y)) π,x]"
          by blast

        obtain π' where "π = map (λ(x, y). (x, y, True)) π'" and "π'  L"
          using is_present π L by auto
        have *: "map (λ(x, y, a). (x, y)) (map (λ(x, y). (x, y, True)) π') = π'"
          by (induction π'; auto)
        have "out[L,π',x]  {}" 
          using out[L,map (λ(x, y, a). (x, y)) π,x]  {} 
          unfolding π = map (λ(x, y). (x, y, True)) π' * .
        have "y  out[L,π',x]"
          using y  out[L,map (λ(x, y, a). (x, y)) π,x]
          unfolding π = map (λ(x, y). (x, y, True)) π' * .

        have "π@[(x,ya)] = map (λ(x, y). (x, y, True)) π' @ [(x, y, False)]"
          unfolding ya = (y,False) π = map (λ(x, y). (x, y, True)) π'
          by auto
        then show "ya  {y. π @ [(x, y)]  L'b}"
          unfolding L'b_def 
          using π'  L out[L,π',x]  {} y  Y y  out[L,π',x] 
          by force
      qed
    qed
    ultimately show ?thesis
      unfolding out[?L, π, x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} 
      by blast
  qed


  show "is_present π L  out[L,map (λ(x, y, a). (x, y)) π,x] = {}  out[absence_completion X Y L, π, x] = {}"
  proof - 
    assume "is_present π L" and "out[L,map (λ(x, y, a). (x, y)) π,x] = {}"

    obtain π' where "π = map (λ(x, y). (x, y, True)) π'" and "π'  L"
      using is_present π L by auto
    have *: "map (λ(x, y, a). (x, y)) (map (λ(x, y). (x, y, True)) π') = π'"
      by (induction π'; auto)
    then have "map (λ(x, y, a). (x, y)) π = π'"
      using π = map (λ(x, y). (x, y, True)) π' by blast 

    have "{y. π @ [(x, y)]  L'a} = {}" 
    proof -
      have " y . π @ [(x, y)]  L'a"
      proof 
        assume "y. π @ [(x, y)]  L'a"
        then obtain ya where "π @ [(x, ya)]  L'a" 
          by blast        
        then obtain π'' where "π''  L" and "map (λ(x, y). (x, y, True)) π'' = π @ [(x, ya)]"
          unfolding L'a_def by force
        then have "(x,ya) = (λ(x, y). (x, y, True)) (last π'')"
          by (metis (mono_tags, lifting) append_is_Nil_conv last_map last_snoc list.map_disc_iff not_Cons_self2) 
        then obtain y where "ya = (y,True)"
          by (simp add: split_beta)
  
        have "map (λ(x, y). (x, y, True)) π'' = map (λ(x, y). (x, y, True)) (π' @ [(x, y)])"
          using map (λ(x, y). (x, y, True)) π'' = π @ [(x, ya)]
          unfolding π = map (λ(x, y). (x, y, True)) π' ya = (y,True) by auto
        moreover have "inj (λ(x, y). (x, y, True))"
          by (simp add: inj_def) 
        ultimately have "π'' = π' @ [(x,y)]"
          using inj_map_eq_map by blast
  
        show False
          using π''  L out[L,map (λ(x, y, a). (x, y)) π,x] = {}
          unfolding map (λ(x, y, a). (x, y)) π = π' π'' = π' @ [(x,y)]
          by simp 
      qed
      then show ?thesis 
        by blast
    qed 
    moreover have "{y. π @ [(x, y)]  L'b} = {}"  
    proof -
      have " y . π @ [(x, y)]  L'b"
      proof 
        assume "y. π @ [(x, y)]  L'b"
        then obtain ya where "π @ [(x, ya)]  L'b" 
          by blast    
        then obtain π'' x' y' τ where "π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π'' @ [(x', y', False)] @ τ"
                                  and "π''  L" 
                                  and "out[L,π'',x']  {}" 
                                  and "y'  Y" 
                                  and "y'  out[L,π'',x']" 
                                  and "((x, y, a)set τ. x  X  y  Y)"
          unfolding L'b_def by blast

        have " xya . xya  set π  snd (snd xya) = True"
          using π = map (λ(x, y). (x, y, True)) π'
          by (induction π; auto) 

        have "τ = []"
        proof (rule ccontr)
          assume "τ  []"
          then obtain τ' xyz where "τ = τ'@[xyz]"
            by (metis append_butlast_last_id)
          then have "π = map (λ(x, y). (x, y, True)) π'' @ [(x', y', False)] @ τ'"
            using π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π'' @ [(x', y', False)] @ τ
            by auto
          then have "(x', y', False)  set π"
            by auto
          then show False 
            using  xya . xya  set π  snd (snd xya) = True by force
        qed
        then have "x' = x" and "ya = (y', False)" and "π = map (λ(x, y). (x, y, True)) π''"
          using π @ [(x,ya)]  = map (λ(x, y). (x, y, True)) π'' @ [(x', y', False)] @ τ
          by auto
        moreover have "inj (λ(x, y). (x, y, True))"
          by (simp add: inj_def) 
        ultimately have "π'' = π'"
          unfolding π = map (λ(x, y). (x, y, True)) π'
          using map_injective by blast
        then show False 
          using out[L,π'',x']  {} out[L,map (λ(x, y, a). (x, y)) π,x] = {}
          unfolding map (λ(x, y, a). (x, y)) π = π' x' = x
          by blast
      qed
      then show ?thesis 
        by blast
    qed
    ultimately show ?thesis
      unfolding out[?L, π, x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} 
      by blast
  qed

  show "¬ is_present π L  out[absence_completion X Y L,π,x] = Y × UNIV" 
  proof 

    show "out[absence_completion X Y L,π,x]  Y × UNIV"
      using absence_completion_is_language[OF assms(1)]
      by (meson outputs_in_alphabet)



    assume "¬ is_present π L" 
    then have "π  L'a"
      unfolding L'a_def by auto
    then have "π  L'b" 
      using π  ?L ?L = L'a  L'b by blast
    then obtain π' x' y' τ where "π = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ"
                             and "π'  L" 
                             and "out[L,π',x']  {}" 
                             and "y'  Y" 
                             and "y'  out[L,π',x']" 
                             and "((x, y, a)set τ. x  X  y  Y)"
      unfolding L'b_def by blast

    show "Y × UNIV  out[absence_completion X Y L,π,x]"
    proof 
      fix ya assume "ya  Y × (UNIV :: bool set)"

      have "π@[(x,ya)] = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ (τ @ [(x,ya)])"
        using π = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ
        by auto
      moreover have ((x, y, a)set (τ @ [(x,ya)]) . x  X  y  Y)
        using ((x, y, a)set τ. x  X  y  Y) x  X ya  Y × (UNIV :: bool set)
        by auto
      ultimately have "π@[(x,ya)]  L'b"
        unfolding L'b_def
        using π'  L out[L,π',x']  {} y'  Y y'  out[L,π',x']
        by blast
      then show "ya  out[?L,π,x]"
        unfolding out[?L, π, x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} 
        by blast
    qed
  qed
qed

    

theorem quasieq_via_quasired : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(L1 ≼[X,quasieq Y] L2)  ((absence_completion X Y L1) ≼[X, quasired (Y × UNIV)] (absence_completion X Y L2))"
proof 

  define L1' where "L1' = absence_completion X Y L1"
  define L2' where "L2' = absence_completion X Y L2"

  define L1'a where "L1'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L1)"
  define L1'b where "L1'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L1  out[L1,π,x]  {}  y  Y  y  out[L1,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
  define L2'a where "L2'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` L2)"
  define L2'b where "L2'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L2  out[L2,π,x]  {}  y  Y  y  out[L2,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"

  

  have " π xya . π  L1'a  xya  set π  snd (snd xya) = True" 
    unfolding L1'a_def by auto
  moreover have " π xya . π  L2'a  xya  set π  snd (snd xya) = True" 
    unfolding L2'a_def by auto
  moreover have " π . π  L1'b   xya  set π . snd (snd xya) = False" 
    unfolding L1'b_def by auto
  moreover have " π . π  L2'b   xya  set π . snd (snd xya) = False" 
    unfolding L2'b_def by auto
  ultimately have "L1'a  L2'b = {}" and "L1'b  L2'a = {}"
    by blast+
  moreover have "L1' = L1'a  L1'b" 
    unfolding L1'_def L1'a_def L1'b_def by auto
  moreover have "L2' = L2'a  L2'b" 
    unfolding L2'_def L2'a_def L2'b_def by auto
  ultimately have "L1'  L2' = (L1'a  L2'a)  (L1'b  L2'b)"
    by blast

  have "inj (λ π . map (λ(x,y) . (x,(y,True))) π)"
    by (simp add: inj_def) 
  then have "L1'a  L2'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` (L1  L2))" 
    unfolding L1'a_def L2'a_def
    using image_Int by blast 

  have intersection_b: "L1'b  L2'b = {(map (λ(x,y) . (x,(y,True))) π)@[(x,(y,False))]@τ | π x y τ . π  L1  L2  out[L1,π,x]  {}  out[L2,π,x]  {}  y  Y  y  out[L1,π,x]  y  out[L2,π,x]  ( (x,(y,a))  set τ . x  X  y  Y)}"
    (is "L1'b  L2'b = ?L12'b")
  proof 
    show "?L12'b  L1'b  L2'b" 
      unfolding L1'b_def L2'b_def by blast
    show "L1'b  L2'b  ?L12'b" 
    proof 
      fix γ assume "γ  L1'b  L2'b"
      
      obtain π1 x1 y1 τ1 where "γ = (map (λ(x,y) . (x,(y,True))) π1)@[(x1,(y1,False))]@τ1" 
                           and "π1  L1" 
                           and "out[L1,π1,x1]  {}" 
                           and "y1  Y" 
                           and "y1  out[L1,π1,x1]" 
                           and "( (x,(y,a))  set τ1 . x  X  y  Y)"
        using γ  L1'b  L2'b unfolding L1'b_def by blast

      obtain π2 x2 y2 τ2 where "γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2" 
                           and "π2  L2" 
                           and "out[L2,π2,x2]  {}" 
                           and "y2  Y" 
                           and "y2  out[L2,π2,x2]" 
                           and "( (x,(y,a))  set τ2 . x  X  y  Y)"
        using γ  L1'b  L2'b unfolding L2'b_def by blast

      have " i . i < length π1  snd (snd (γ ! i)) = True" 
      proof - 
        fix i assume "i < length π1" 
        then have "i < length (map (λ(x,y) . (x,(y,True))) π1)" by auto 
        then have "γ ! i = (map (λ(x,y) . (x,(y,True))) π1) ! i" 
          unfolding γ = (map (λ(x,y) . (x,(y,True))) π1)@[(x1,(y1,False))]@τ1
          by (simp add: nth_append) 
        also have " = (λ(x,y) . (x,(y,True))) (π1 ! i)"
          using i < length π1 nth_map by blast 
        finally show "snd (snd (γ ! i)) = True"
          by (metis (no_types, lifting) case_prod_conv old.prod.exhaust snd_conv) 
      qed
      have "γ ! length π1 = (x1,(y1,False))"
        unfolding γ = (map (λ(x,y) . (x,(y,True))) π1)@[(x1,(y1,False))]@τ1
        by (metis append_Cons length_map nth_append_length)
      have " i . i < length π2  snd (snd (γ ! i)) = True" 
      proof - 
        fix i assume "i < length π2" 
        then have "i < length (map (λ(x,y) . (x,(y,True))) π2)" by auto 
        then have "γ ! i = (map (λ(x,y) . (x,(y,True))) π2) ! i" 
          unfolding γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2
          by (simp add: nth_append) 
        also have " = (λ(x,y) . (x,(y,True))) (π2 ! i)"
          using i < length π2 nth_map by blast 
        finally show "snd (snd (γ ! i)) = True"
          by (metis (no_types, lifting) case_prod_conv old.prod.exhaust snd_conv) 
      qed
      have "γ ! length π2 = (x2,(y2,False))"
        unfolding γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2
        by (metis append_Cons length_map nth_append_length)

      have "length π1 = length π2"
        by (metis i. i < length π1  snd (snd (γ ! i)) = True i. i < length π2  snd (snd (γ ! i)) = True γ ! length π1 = (x1, y1, False) γ ! length π2 = (x2, y2, False) not_less_iff_gr_or_eq snd_conv) 
      then have "π1 = π2"  
        using γ = (map (λ(x,y) . (x,(y,True))) π1)@[(x1,(y1,False))]@τ1 inj (λ π . map (λ(x,y) . (x,(y,True))) π)
        unfolding γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2
        using map_injective by fastforce 
      then have "[(x1,(y1,False))]@τ1 = [(x2,(y2,False))]@τ2"
        using γ = (map (λ(x,y) . (x,(y,True))) π1)@[(x1,(y1,False))]@τ1
        unfolding γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2
        by force
      then have "x1 = x2" and "y1 = y2" and "τ1 = τ2"
        by auto

      show "γ  ?L12'b"
        using π1  L1 out[L1,π1,x1]  {} y1  Y y1  out[L1,π1,x1] ( (x,(y,a))  set τ1 . x  X  y  Y)
        using π2  L2 out[L2,π2,x2]  {} y2  Y y2  out[L2,π2,x2] ( (x,(y,a))  set τ2 . x  X  y  Y) 
        unfolding π1 = π2 x1 = x2 y1 = y2 τ1 = τ2 γ = (map (λ(x,y) . (x,(y,True))) π2)@[(x2,(y2,False))]@τ2
        by blast
    qed
  qed
  

  have "is_language X (Y × UNIV) L1'"
    using absence_completion_is_language[OF assms(1)] unfolding L1'_def .
  have "is_language X (Y × UNIV) L2'"
    using absence_completion_is_language[OF assms(2)] unfolding L2'_def .

  have "(L1 ≼[X,quasieq Y] L2) = quasi_equivalence L1 L2"
    using quasieq_type_1[OF assms] by blast

  have "(L1' ≼[X,quasired (Y × UNIV)] L2') = quasi_reduction L1' L2'"
    using quasired_type_1[OF is_language X (Y × UNIV) L1' is_language X (Y × UNIV) L2'] by blast

  have " π x . quasi_equivalence L1 L2  π  L1'  L2'  x  exec[L2',π]  (out[L1',π,x]  {}  out[L1',π,x]  out[L2',π,x])"
  proof -
    fix π x assume "quasi_equivalence L1 L2" and "π  L1'  L2'" and "x  exec[L2',π]"

    have "x  X"
      using x  exec[L2',π] absence_completion_is_language[OF assms(2)]
      by (metis L2'_def executable_inputs_in_alphabet)
    have "π  absence_completion X Y L1" and "π  absence_completion X Y L2"
      using π  L1'  L2' unfolding L1'_def L2'_def by blast+

    consider (a) "π  L1'a  L2'a" | (b) "π  (L1'b  L2'b) - (L1'a  L2'a)"
      using π  L1'  L2' L1'  L2' = (L1'a  L2'a)  (L1'b  L2'b) by blast
    then show "(out[L1',π,x]  {}  out[L1',π,x]  out[L2',π,x])"
    proof cases
      case a
      then obtain τ where "τ  L1  L2"
                      and "π = map (λ(x,y) . (x,(y,True))) τ"
        using L1'a  L2'a = ((λ π . map (λ(x,y) . (x,(y,True))) π) ` (L1  L2)) by blast
      
      have "map (λ(x, y, a). (x, y)) π = τ"
        unfolding π = map (λ(x,y) . (x,(y,True))) τ by (induction τ; auto)

      have "is_present π L1" and "is_present π L2"
        using τ  L1  L2 unfolding π = map (λ(x,y) . (x,(y,True))) τ by auto

      have "out[L2,map (λ(x, y, a). (x, y)) π,x]  {}"
        using x  exec[L2',π]
        using absence_completion_out(2)[OF assms(2) x  X π  absence_completion X Y L2 is_present π L2]
        unfolding L2'_def[symmetric]
        by (meson outputs_executable)  
      then have "x  exec[L2,map (λ(x, y, a). (x, y)) π]"
        by auto
      then have "out[L1,map (λ(x, y, a). (x, y)) π,x]  {}" and "out[L1,map (λ(x, y, a). (x, y)) π,x] = out[L2,map (λ(x, y, a). (x, y)) π,x]"
        using quasi_equivalence L1 L2 τ  L1  L2
        unfolding quasi_equivalence_def map (λ(x, y, a). (x, y)) π = τ by force+
      
      have "out[L1',π,x] = out[L2',π,x]"
        unfolding L1'_def L2'_def 
        unfolding absence_completion_out(1)[OF assms(2) x  X π  absence_completion X Y L2 is_present π L2 out[L2,map (λ(x, y, a). (x, y)) π,x]  {}]
        unfolding absence_completion_out(1)[OF assms(1) x  X π  absence_completion X Y L1 is_present π L1 out[L1,map (λ(x, y, a). (x, y)) π,x]  {}]
        using quasi_equivalence L1 L2 τ  L1  L2 x  exec[L2,map (λ(x, y, a). (x, y)) π]
        unfolding quasi_equivalence_def 
        unfolding map (λ(x, y, a). (x, y)) π = τ
        by blast
      then show ?thesis
        by (metis x  exec[L2',π] dual_order.refl outputs_executable) 
    next
      case b

      then obtain π' x' y' τ' where "π = map (λ(x, y). (x, y, True)) π' @ [(x', y', False)] @ τ'"
                                and "π'  L1  L2"
                                and "out[L1,π',x']  {}"
                                and "out[L2,π',x']  {}" 
                                and "y'  Y" 
                                and "y'  out[L1,π',x']" 
                                and "y'  out[L2,π',x']" 
                                and "((x, y, a)set τ'. x  X  y  Y)"
        unfolding intersection_b 
        by blast

      have "¬ is_present π L1"
        using L1'a  map (λ(x, y). (x, y, True)) ` L1 L1'a  L2'b = {} b by auto

      have "¬ is_present π L2"
        using L2'a  map (λ(x, y). (x, y, True)) ` L2 L1'b  L2'a = {} b by auto

      show ?thesis 
        unfolding L1'_def L2'_def
        unfolding absence_completion_out(3)[OF assms(1) x  X π  absence_completion X Y L1 ¬ is_present π L1]
        unfolding absence_completion_out(3)[OF assms(2) x  X π  absence_completion X Y L2 ¬ is_present π L2]
        using y'  Y
        by blast
    qed
  qed
  then show "L1 ≼[X,quasieq Y] L2  (absence_completion X Y L1) ≼[X,quasired (Y × UNIV)] (absence_completion X Y L2)"
    unfolding L1'_def[symmetric] L2'_def[symmetric] 
    unfolding (L1' ≼[X,quasired (Y × UNIV)] L2') = quasi_reduction L1' L2'
    unfolding (L1 ≼[X,quasieq Y] L2) = quasi_equivalence L1 L2
    unfolding quasi_reduction_def 
    by blast


  have " π x . quasi_reduction L1' L2'  π  L1  L2  x  exec[L2,π]  out[L1,π,x] = out[L2,π,x]"
  proof -
    fix π x assume "quasi_reduction L1' L2'" and "π  L1  L2" and "x  exec[L2,π]"
    then have "x  X"
      by (meson assms(2) executable_inputs_in_alphabet)

    let  = "map (λ(x, y). (x, y, True)) π"
    have "map (λ(x, y, a). (x, y))  = π"
      by (induction π; auto)
    then have "out[L2,map (λ(x, y, a). (x, y)) ,x]  {}"
      using x  exec[L2,π] by auto

    have "is_present  L1" and "is_present  L2" 
      using π  L1  L2 by auto

    have "  L1'a  L2'a"
      using L1'a_def L2'a  map (λ(x, y). (x, y, True)) ` L2 is_present (map (λ(x, y). (x, y, True)) π) L1 is_present (map (λ(x, y). (x, y, True)) π) L2 by auto 
    then have "  absence_completion X Y L1" and "  absence_completion X Y L2" and "  L1'  L2'"
      unfolding L1'_def[symmetric] L2'_def[symmetric]
      unfolding L1' = L1'a  L1'b L2' = L2'a  L2'b
      by blast+
    
    have "out[L2',,x] = {(y, True) |y. y  out[L2,π,x]}  {(y, False) |y. y  Y  y  out[L2,π,x]}"    
      using absence_completion_out(1)[OF assms(2) x  X   absence_completion X Y L2 is_present  L2 out[L2,map (λ(x, y, a). (x, y)) ,x]  {}]
      unfolding L2'_def[symmetric] map (λ(x, y, a). (x, y))  = π .
    then have "x  exec[L2',]"
      using x  exec[L2,π] by fastforce  
    then have "out[L1',,x]  {}" and "out[L1',,x]  out[L2',,x]"
      using quasi_reduction L1' L2'   L1'  L2'
      unfolding quasi_reduction_def 
      by blast+

    have "out[L1,π,x]  {}"
      by (metis L1'_def is_present (map (λ(x, y). (x, y, True)) π) L1 map (λ(x, y). (x, y, True)) π  absence_completion X Y L1 map (λ(x, y, a). (x, y)) (map (λ(x, y). (x, y, True)) π) = π out[L1',map (λ(x, y). (x, y, True)) π,x]  {} x  X absence_completion_out(2) assms(1)) 
    then have "out[L1',,x] = {(y, True) |y. y  out[L1,π,x]}  {(y, False) |y. y  Y  y  out[L1,π,x]}"    
      using absence_completion_out(1)[OF assms(1) x  X   absence_completion X Y L1 is_present  L1]
      unfolding L1'_def[symmetric] map (λ(x, y, a). (x, y))  = π
      by blast

    have "out[L1,π,x]  Y" and "out[L2,π,x]  Y"
      by (meson assms(1,2) outputs_in_alphabet)+ 

    have " y . y  out[L1,π,x]  y  out[L2,π,x]"
    proof -
      fix y assume "y  out[L1,π,x]"
      then have "(y, True)  out[L1',,x]" 
        unfolding out[L1',,x] = {(y, True) |y. y  out[L1,π,x]}  {(y, False) |y. y  Y  y  out[L1,π,x]} by blast
      then have "(y, True)  out[L2',,x]"
        using out[L1',,x]  out[L2',,x] by blast
      then show "y  out[L2,π,x]" 
        unfolding out[L2',,x] = {(y, True) |y. y  out[L2,π,x]}  {(y, False) |y. y  Y  y  out[L2,π,x]}
        by fastforce 
    qed
    moreover have " y . y  out[L2,π,x]  y  out[L1,π,x]"
    proof -
      fix y assume "y  out[L2,π,x]"
      then have "(y, True)  out[L2',,x]" and "(y, False)  out[L2',,x]"
        unfolding out[L2',,x] = {(y, True) |y. y  out[L2,π,x]}  {(y, False) |y. y  Y  y  out[L2,π,x]} by blast+
      moreover have "(y, True)  out[L1',,x]  (y, False)  out[L1',,x]"  
        unfolding out[L1',,x] = {(y, True) |y. y  out[L1,π,x]}  {(y, False) |y. y  Y  y  out[L1,π,x]}
        using out[L2,π,x]  Y y  out[L2,π,x] by auto 
      ultimately have "(y, True)  out[L1',,x]"
        using out[L1',,x]  out[L2',,x] by blast
      then show "y  out[L1,π,x]" 
        unfolding out[L1',,x] = {(y, True) |y. y  out[L1,π,x]}  {(y, False) |y. y  Y  y  out[L1,π,x]}
        by fastforce 
    qed
    ultimately show "out[L1,π,x] = out[L2,π,x]"
      by blast
  qed
  then show "(absence_completion X Y L1) ≼[X,quasired (Y × UNIV)] (absence_completion X Y L2)  L1 ≼[X,quasieq Y] L2"
    unfolding L1'_def[symmetric] L2'_def[symmetric]  
    unfolding (L1' ≼[X,quasired (Y × UNIV)] L2') = quasi_reduction L1' L2'
    unfolding (L1 ≼[X,quasieq Y] L2) = quasi_equivalence L1 L2
    unfolding quasi_reduction_def quasi_equivalence_def 
    by blast
qed




subsection ‹Quasi-Reduction via Reduction and explicit Undefined Behaviour›

fun bottom_completion :: "'x alphabet  'y alphabet  ('x,'y) language  ('x, 'y option) language" where 
  "bottom_completion X Y L = 
    ((λ π . map (λ(x,y) . (x,Some y)) π) ` L)
     {(map (λ(x,y) . (x,Some y)) π)@[(x,y)]@τ | π x y τ . π  L  out[L,π,x] = {}  x  X  (y = None  y  Some ` Y)  ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))}"

lemma bottom_completion_is_language :
  assumes "is_language X Y L"
shows "is_language X ({None}  Some ` Y) (bottom_completion X Y L)"
proof -
  let ?L = "bottom_completion X Y L"

  have "?L  {}"
    using language_contains_nil[OF assms] by auto
  moreover have " π . π  ?L  ( xy  set π . fst xy  X  snd xy  ({None}  Some ` Y))  ( π' . prefix π' π  π'  ?L)"
  proof -
    fix π assume "π  ?L"
    then consider (a) "π  ((λ π . map (λ(x,y) . (x,Some y)) π) ` L)" | 
                  (b) "π  {(map (λ(x,y) . (x,Some y)) π)@[(x,y)]@τ | π x y τ . π  L  out[L,π,x] = {}  x  X  (y = None  y  Some ` Y)  ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))}"
      unfolding bottom_completion.simps by blast
    then show "( xy  set π . fst xy  X  snd xy  ({None}  Some ` Y))  ( π' . prefix π' π  π'  ?L)" 
    proof cases
      case a
      then obtain π' where "π = map (λ(x, y). (x, Some y)) π'" and "π'  L"
        by auto
      then have "( xy  set π' . fst xy  X  snd xy  Y)"
            and "( π'' . prefix π'' π'  π''  L)"
        using assms by auto

      
      have "( π' . prefix π' π  π'  ((λ π . map (λ(x,y) . (x,Some y)) π) ` L))" 
        using ( π'' . prefix π'' π'  π''  L) unfolding π = map (λ(x, y). (x, Some y)) π'
        using prefix_map_rightE by force
      then have "( π' . prefix π' π  π'  ?L)" 
        by auto
      moreover have "( xy  set π . fst xy  X  snd xy  ({None}  Some ` Y))"
        using ( xy  set π' . fst xy  X  snd xy  Y) unfolding π = map (λ(x, y). (x, Some y)) π' 
        by (induction π'; auto)
      ultimately show ?thesis
        by blast
    next
      case b
      then obtain π' x y τ where "π = (map (λ(x,y) . (x,Some y)) π')@[(x,y)]@τ"    
                             and "π'  L" 
                             and "out[L,π',x] = {}" 
                             and "x  X" 
                             and "(y = None  y  Some ` Y)" 
                             and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
        by blast
      then have "( xy  set π' . fst xy  X  snd xy  Y)"
            and "( π'' . prefix π'' π'  π''  L)"
        using assms by auto

      have "( xy  set (map (λ(x,y) . (x,Some y)) π') . fst xy  X  snd xy  ({None}  Some ` Y))"
        using ( xy  set π' . fst xy  X  snd xy  Y) 
        by (induction π'; auto)
      moreover have "set π = set (map (λ(x,y) . (x,Some y)) π')  {(x,y)}  set τ"
        unfolding π = (map (λ(x,y) . (x,Some y)) π')@[(x,y)]@τ 
        by simp
      ultimately have "( xy  set π . fst xy  X  snd xy  ({None}  Some ` Y))"
        using x  X (y = None  y  Some ` Y) ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))
        by auto
      moreover have " π'' . prefix π'' π  π''  ?L"
      proof -
        fix π'' assume "prefix π'' π" 
        then obtain i where "π'' = take i π"
          by (metis append_eq_conv_conj prefix_def)
        then consider (b1) "i  length π'" | 
                      (b2) "i > length π'"
          by linarith
        then show "π''  ?L" proof cases
          case b1 
          then have "i  length (map (λ(x,y) . (x,Some y)) π')" 
            by auto
          then have "π'' = map (λ(x,y) . (x,Some y)) (take i π')"
            unfolding π'' = take i π
            using π = map (λ(x, y). (x, Some y)) π' @ [(x, y)] @ τ take_map by fastforce 
          moreover have "take i π'  L"
            using π'  L take_is_prefix
            using π''. prefix π'' π'  π''  L by blast 
          ultimately have "π''  ((λ π . map (λ(x,y) . (x,Some y)) π) ` L)"
            by simp
          then show "π''  ?L" 
            by auto
        next
          case b2 
          then have "i > length (map (λ(x,y) . (x,Some y)) π')" 
            by auto

          have " k xs ys . k > length xs  take k (xs@ys) = xs@(take (k - length xs) ys)"
            by simp 
          have take_helper: " k xs y zs . k > length xs  take k (xs@[y]@zs) = xs@[y]@(take (k - length xs - 1) zs)"
            by (metis One_nat_def Suc_pred ys xs k. length xs < k  take k (xs @ ys) = xs @ take (k - length xs) ys append_Cons append_Nil take_Suc_Cons zero_less_diff) 
          
          have **: "π'' = (map (λ(x,y) . (x,Some y)) π')@[(x,y)]@(take (i - length π' - 1) τ)"
            unfolding π = map (λ(x, y). (x, Some y)) π' @ [(x, y)] @ τ  π'' = take i π
            using take_helper[OF i > length (map (λ(x,y) . (x,Some y)) π')] by simp
          
          have "( (x,y)  set (take (i - length π' - 1) τ) . x  X  (y = None  y  Some ` Y))"
            using ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))
            by (meson in_set_takeD) 
          then show ?thesis
            unfolding ** bottom_completion.simps
            using π'  L out[L,π',x] = {} x  X (y = None  y  Some ` Y)
            by blast
        qed
      qed
      ultimately show ?thesis by auto
    qed 
  qed
  ultimately show ?thesis 
    unfolding is_language.simps by blast
qed




fun is_not_undefined :: "('x,'y option) word  ('x,'y) language  bool" where
  "is_not_undefined π L = (π  map (λ(x, y). (x, Some y)) ` L)"

lemma bottom_id : "map (λ(x,y) . (x, the y)) (map (λ(x, y). (x, Some y)) π) = π"
  by (induction π; auto) 



fun maximum_prefix_with_property :: "('a list  bool)  'a list  'a list" where 
  "maximum_prefix_with_property P xs = (last (filter P (prefixes xs)))"

lemma maximum_prefix_with_property_props : 
  assumes " ys  set (prefixes xs) . P ys"
shows "P (maximum_prefix_with_property P xs)"
  and "(maximum_prefix_with_property P xs)  set (prefixes xs)"
  and " ys . prefix ys xs  P ys  length ys  length (maximum_prefix_with_property P xs)"
proof -

  have "P (maximum_prefix_with_property P xs) 
        (maximum_prefix_with_property P xs)  set (prefixes xs) 
        ( ys . prefix ys xs  P ys  length ys  length (maximum_prefix_with_property P xs))"
    using assms 
  proof (induction xs rule: rev_induct)
    case Nil
    then show ?case by auto
  next
    case (snoc x xs)
    have "prefixes (xs @ [x]) = (prefixes xs)@[xs @ [x]]"
      by simp

    show ?case proof (cases "P (xs@[x])")
      case True      
      then have "maximum_prefix_with_property P (xs @ [x]) = (xs @ [x])"
        unfolding maximum_prefix_with_property.simps prefixes (xs @ [x]) = (prefixes xs)@[xs @ [x]] 
        by auto        
      show ?thesis 
        using True
        unfolding maximum_prefix_with_property P (xs @ [x]) = (xs@[x]) 
        using in_set_prefixes prefix_length_le by blast
    next
      case False 
      then have "maximum_prefix_with_property P (xs@[x]) = maximum_prefix_with_property P xs" 
        unfolding maximum_prefix_with_property.simps prefixes (xs @ [x]) = (prefixes xs)@[xs @ [x]]
        by auto

      have "aset (prefixes xs). P a" 
        using snoc.prems False unfolding prefixes (xs @ [x]) = (prefixes xs)@[xs @ [x]] by auto

      show ?thesis
        using snoc.IH[OF aset (prefixes xs). P a] False
        unfolding maximum_prefix_with_property P (xs@[x]) = maximum_prefix_with_property P xs
        unfolding prefixes (xs @ [x]) = (prefixes xs)@[xs @ [x]] by auto
    qed
  qed
  then show "P (maximum_prefix_with_property P xs)"
        and "(maximum_prefix_with_property P xs)  set (prefixes xs)"
        and " ys . prefix ys xs  P ys  length ys  length (maximum_prefix_with_property P xs)" 
    by blast+
qed


lemma bottom_completion_out : 
  assumes "is_language X Y L"
  and     "x  X"
  and     "π  bottom_completion X Y L"
shows "is_not_undefined π L  out[L,map (λ(x,y) . (x, the y)) π,x]  {}  out[bottom_completion X Y L, π, x] = Some ` out[L, map (λ(x,y) . (x, the y)) π, x]"
and   "is_not_undefined π L  out[L,map (λ(x,y) . (x, the y)) π,x] = {}  out[bottom_completion X Y L, π, x] = {None}  Some ` Y"
and   "¬ is_not_undefined π L  out[bottom_completion X Y L, π, x] = {None}  Some ` Y"
proof -

  let ?L = "bottom_completion X Y L"
  define L'a where "L'a = ((λ π . map (λ(x,y) . (x,Some y)) π) ` L)"
  define L'b where "L'b = {(map (λ(x,y) . (x,Some y)) π)@[(x,y)]@τ | π x y τ . π  L  out[L,π,x] = {}  x  X  (y = None  y  Some ` Y)  ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))}"
  have "?L = L'a  L'b" 
    unfolding L'a_def L'b_def bottom_completion.simps by blast
  then have "out[?L, π, x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b}"
    unfolding outputs.simps language_for_state.simps by blast

  have "is_language X ({None}  Some ` Y) ?L"
    using bottom_completion_is_language[OF assms(1)] .
  
  show "is_not_undefined π L  out[L,map (λ(x,y) . (x, the y)) π,x]  {}  out[bottom_completion X Y L, π, x] = Some ` out[L, map (λ(x,y) . (x, the y)) π, x]"
   and "is_not_undefined π L  out[L,map (λ(x,y) . (x, the y)) π,x] = {}  out[bottom_completion X Y L, π, x] = {None}  Some ` Y"
  proof -
    assume "is_not_undefined π L"
    then obtain π' where "π = map (λ(x, y). (x, Some y)) π'" and "π'  L"
      by auto
    then have "map (λ(x, y). (x, the y)) π = π'"
      using bottom_id by auto


    have "{y. π @ [(x, y)]  L'a} = Some ` out[L,map (λ(x,y) . (x, the y)) π,x]"
    proof 
      show "{y. π @ [(x, y)]  L'a}  Some ` out[L,map (λ(x, y). (x, the y)) π,x]"
      proof 
        fix y assume "y  {y. π @ [(x, y)]  L'a}"
        then have "π @ [(x, y)]  L'a" by auto
        then obtain π' where "π @ [(x, y)] = map (λ(x,y) . (x,Some y)) π'" and "π'  L" 
          unfolding L'a_def by blast
        then have "length (π @ [(x, y)]) = length π'"
          by auto
        then obtain γ' xy where "π' = γ'@[xy]"
          by (metis add.right_neutral dual_order.strict_iff_not length_append_singleton less_add_Suc2 rev_exhaust take0 take_all_iff)
        then have "(x,y) = (λ(x, y). (x, Some y)) xy"
          using π @ [(x, y)] = map (λ(x, y). (x, Some y)) π' unfolding π' = γ'@[xy] by auto
        then have "y = Some (snd xy)" and "xy = (x,snd xy)"
          by (simp add: split_beta)+
        moreover define y' where "y' = snd xy"
        ultimately have "y = Some y'" and "xy = (x,y')"
          by auto

        have "map (λ(x, y). (x, the y)) π = γ'"
          using π @ [(x, y)] = map (λ(x,y) . (x,Some y)) π' unfolding π' = γ'@[xy]
          using bottom_id by auto 

        have "y'  out[L,map (λ(x, y). (x, the y)) π,x]"
          using π'  L
          unfolding map (λ(x, y). (x, the y)) π = γ' π' = γ'@[xy] xy = (x,y') by auto
        then show "y  Some ` out[L,map (λ(x, y). (x, the y)) π,x]"
          unfolding y = Some y' by blast
      qed
      show "Some ` out[L,map (λ(x, y). (x, the y)) π,x]  {y. π @ [(x, y)]  L'a}"
      proof 
        fix y assume "y  Some ` out[L,map (λ(x, y). (x, the y)) π,x]"
        then obtain y' where "y = Some y'" and "y'  out[L,map (λ(x, y). (x, the y)) π,x]"
          by blast
        then have "π'@[(x,y')]  L"
          unfolding map (λ(x, y). (x, the y)) π = π' by auto
        then show "y  {y. π @ [(x, y)]  L'a}"
          unfolding L'a_def π = map (λ(x, y). (x, Some y)) π'
          using y = Some y' image_iff by fastforce
      qed
    qed



    show "out[L,map (λ(x,y) . (x, the y)) π,x]  {}  out[bottom_completion X Y L, π, x] = Some ` out[L, map (λ(x,y) . (x, the y)) π, x]"
    proof -
      assume "out[L,map (λ(x,y) . (x, the y)) π,x]  {}"
      then obtain ya where "π'@[(x,ya)]  L"
        using π'  L unfolding map (λ(x,y) . (x, the y)) π = π' by auto

      
      have "{y. π @ [(x, y)]  L'b} = {}"
      proof (rule ccontr)
        assume "{y. π @ [(x, y)]  L'b}  {}"
        then obtain y where "π @ [(x, y)]  L'b" by blast
        then obtain π'' x' y' τ where "π @ [(x, y)] = (map (λ(x,y) . (x,Some y)) π'')@[(x',y')]@τ"    
                                  and "π''  L" 
                                  and "out[L,π'',x'] = {}" 
                                  and "x'  X" 
                                  and "(y' = None  y'  Some ` Y)" 
                                  and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
          unfolding L'b_def 
          by blast

        have " y'' . π''@[(x',y'')]  L"
          using π''  L out[L,π'',x'] = {} 
          unfolding outputs.simps language_for_state.simps by force        

        have "length π' = length π''"
        proof -
          have "length π' = length π"
            using map (λ(x, y). (x, the y)) π = π' length_map by blast 

          have "¬ length π' < length π''"
          proof 
            assume "length π' < length π''"
            then have "length π'' = Suc (length π')"
              by (metis (no_types, lifting) One_nat_def π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ length π' = length π add_diff_cancel_left' length_append length_append_singleton length_map list.size(3) not_less_eq plus_1_eq_Suc zero_less_Suc zero_less_diff)
            then have "length π'' > length π"
              by (simp add: π = map (λ(x, y). (x, Some y)) π') 
            then show False
              by (metis (no_types, lifting) One_nat_def π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ length_Cons length_append length_append_singleton length_map less_add_same_cancel1 list.size(3) not_less_eq plus_1_eq_Suc zero_less_Suc) 
          qed
          moreover have "¬ length π'' < length π'"
          proof 
            assume "length π'' < length π'"
            then have "prefix ((map (λ(x,y) . (x,Some y)) π'')@[(x',y')]) (map (λ(x,y) . (x,Some y)) π')"
              by (metis (no_types, lifting) π = map (λ(x, y). (x, Some y)) π' π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ append.assoc length_append_singleton length_map linorder_not_le not_less_eq prefixI prefix_length_prefix) 
            then have "prefix π'' π'"
              by (metis append_prefixD bottom_id map_mono_prefix) 
            then have "take (length π'') π' = π''"
              by (metis append_eq_conv_conj prefix_def) 

            have "(x',y') = (((map (λ(x,y) . (x,Some y)) π'')@[(x',y')])) ! (length π'')"
              by (induction π'' arbitrary: x' y'; auto)
            then have "(x',y') = (map (λ(x,y) . (x,Some y)) π') ! (length π'')"
              by (metis (no_types, lifting) π = map (λ(x, y). (x, Some y)) π' π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ length π'' < length π' append_Cons length_map nth_append nth_append_length) 
            then have "fst (π' ! (length π'')) = x'"
              by (simp add: length π'' < length π' split_beta) 

            have "out[L, take (length π'') π', fst (π' ! (length π''))] = {}"
              unfolding take (length π'') π' = π'' fst (π' ! (length π'')) = x' 
              using out[L,π'',x'] = {} .
            moreover have " i . i < length π'  out[L, take i π', fst (π' ! i)]  {}"
              using prefix_executable[OF assms(1) π'  L]
              by (meson outputs_executable) 
            ultimately show False 
              using length π'' < length π' by blast
          qed
          ultimately show ?thesis
            by simp
        qed
        then have "π'' = π'"
          by (metis π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ map (λ(x, y). (x, the y)) π = π' append_eq_append_conv bottom_id length_map) 

        show False
          using π @ [(x, y)] = map (λ(x, y). (x, Some y)) π'' @ [(x', y')] @ τ π'' = π' map (λ(x, y). (x, the y)) π = π' out[L,π'',x'] = {} out[L,map (λ(x, y). (x, the y)) π,x]  {} 
          by force 
      qed
      then show ?thesis
        using out[bottom_completion X Y L,π,x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} 
        using {y. π @ [(x, y)]  L'a} = Some ` out[L,map (λ(x,y) . (x, the y)) π,x]
        by force 
    qed

    
    show "out[L,map (λ(x,y) . (x, the y)) π,x] = {}  out[bottom_completion X Y L, π, x] = {None}  Some ` Y"
    proof -
      assume "out[L,map (λ(x,y) . (x, the y)) π,x] = {}" 
      then have "{y. π @ [(x, y)]  L'a} = {}"
        unfolding {y. π @ [(x, y)]  L'a} = Some ` out[L,map (λ(x,y) . (x, the y)) π,x] by blast
      moreover have "{y. π @ [(x, y)]  L'b} = {None}  Some ` Y"
      proof 
        show "{y. π @ [(x, y)]  L'b}  {None}  Some ` Y"
        proof 
          fix y assume "y  {y. π @ [(x, y)]  L'b}"
          then have "π @ [(x, y)]  L'b" by blast
          then obtain π'' x' y' τ where "π @ [(x, y)] = (map (λ(x,y) . (x,Some y)) π'')@[(x',y')]@τ"    
                                    and "π''  L" 
                                    and "out[L,π'',x'] = {}" 
                                    and "x'  X" 
                                    and "(y' = None  y'  Some ` Y)" 
                                    and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
            unfolding L'b_def 
            by blast
  
          show "y  {None}  Some ` Y"
            by (metis (no_types, lifting) Un_insert_right out[bottom_completion X Y L,π,x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} y  {y. π @ [(x, y)]  L'b} assms(1) bottom_completion_is_language insert_subset mk_disjoint_insert outputs_in_alphabet) 
        qed
        show "{None}  Some ` Y  {y. π @ [(x, y)]  L'b}"
        proof 
          fix y assume "y  {None}  Some ` Y" 

          have "π @ [(x, y)] = map (λ(x, y). (x, Some y)) π' @ [(x, y)] @ []"
            by (simp add: π = map (λ(x, y). (x, Some y)) π')
          moreover note π'  L
          moreover have "out[L,π',x] = {}" 
            using out[L,map (λ(x,y) . (x, the y)) π,x] = {} unfolding map (λ(x,y) . (x, the y)) π = π' .
          moreover note x  X
          moreover have "(y = None  y  Some ` Y)"
            using y  {None}  Some ` Y by blast
          moreover have "((x, y)set []. x  X  (y = None  y  Some ` Y))"
            by simp
          ultimately show "y  {y. π @ [(x, y)]  L'b}"
            unfolding L'b_def by blast
        qed
      qed
      ultimately show ?thesis
        using out[bottom_completion X Y L,π,x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} 
        using {y. π @ [(x, y)]  L'a} = Some ` out[L,map (λ(x,y) . (x, the y)) π,x]
        by force 
    qed
  qed

  show "¬ is_not_undefined π L  out[bottom_completion X Y L,π,x] = {None}  Some ` Y" 
  proof -
    assume "¬ is_not_undefined π L" 
    then have "π  L'a" 
      unfolding L'a_def by auto 

    have "{y. π @ [(x, y)]  L'a} = {}"
    proof (rule ccontr)
      assume "{y. π @ [(x, y)]  L'a}  {}"
      then obtain y where "π @ [(x, y)]  L'a" by blast
      then obtain γ where "π @ [(x, y)] = map (λ(x, y). (x, Some y)) γ" and "γ  L" 
        unfolding L'a_def by blast
      then have "π = map (λ(x, y). (x, Some y)) (butlast γ)"
        by (metis (mono_tags, lifting) butlast_snoc map_butlast) 
      moreover have "butlast γ  L"
        using γ  L assms(1)
        by (simp add: prefixeq_butlast) 
      ultimately show False
        using π  L'a
        using L'a_def by blast
    qed 
    then have "out[?L, π, x] = {y. π @ [(x, y)]  L'b}"
      using out[bottom_completion X Y L,π,x] = {y. π @ [(x, y)]  L'a}  {y. π @ [(x, y)]  L'b} by blast 
    also have " = {None}  Some ` Y" 
    proof 
      show "{y. π @ [(x, y)]  L'b}  {None}  Some ` Y"
      proof 
        fix y assume "y  {y. π @ [(x, y)]  L'b}"
        then obtain π' x' y' τ where "π @ [(x, y)] = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@τ"    
                                 and "π'  L" 
                                 and "out[L,π',x'] = {}" 
                                 and "x'  X" 
                                 and "(y' = None  y'  Some ` Y)" 
                                 and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
          unfolding L'b_def 
          by blast

        have "(x,y)  set ([(x',y')]@τ)"
          by (metis π @ [(x, y)] = map (λ(x, y). (x, Some y)) π' @ [(x', y')] @ τ append_is_Nil_conv last_appendR last_in_set last_snoc length_Cons list.size(3) nat.simps(3))
        then show "y  {None}  Some ` Y"
          using (y' = None  y'  Some ` Y) ( (x,y)  set τ . x  X  (y = None  y  Some ` Y)) by auto
      qed
      show "{None}  Some ` Y  {y. π @ [(x, y)]  L'b}" 
      proof 
        fix y assume "y  {None}  Some ` Y" 

        have "π  L'b"
          using π  L'a ?L = L'a  L'b assms(3) by fastforce
        then obtain π' x' y' τ where "π = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@τ"    
                                 and "π'  L" 
                                 and "out[L,π',x'] = {}" 
                                 and "x'  X" 
                                 and "(y' = None  y'  Some ` Y)" 
                                 and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
          unfolding L'b_def 
          by blast

        have "π @ [(x,y)] = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@(τ@[(x,y)])"
          unfolding π = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@τ by auto
        moreover note π'  L and out[L,π',x'] = {} and x'  X and (y' = None  y'  Some ` Y)
        moreover have "( (x,y)  set (τ@[(x,y)]) . x  X  (y = None  y  Some ` Y))"
          using  (x,y)  set τ . x  X  (y = None  y  Some ` Y) y  {None}  Some ` Y x  X
          by auto
        ultimately show "y  {y. π @ [(x, y)]  L'b}" 
          unfolding L'b_def by blast
      qed
    qed
    finally show "out[?L,π,x] = {None}  Some ` Y" .
  qed
qed



theorem quasired_via_red : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(L1 ≼[X,quasired Y] L2)  ((bottom_completion X Y L1) ≼[X, red ({None}  Some ` Y)] (bottom_completion X Y L2))" 
proof -

  define L1' where "L1' = bottom_completion X Y L1"
  define L2' where "L2' = bottom_completion X Y L2"

  define L1'a where "L1'a = ((λ π . map (λ(x,y) . (x,Some y)) π) ` L1)"
  define L1'b where "L1'b = {(map (λ(x,y) . (x,Some y)) π)@[(x,y)]@τ | π x y τ . π  L1  out[L1,π,x] = {}  x  X  (y = None  y  Some ` Y)  ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))}"
  define L2'a where "L2'a = ((λ π . map (λ(x,y) . (x,Some y)) π) ` L2)"
  define L2'b where "L2'b = {(map (λ(x,y) . (x,Some y)) π)@[(x,y)]@τ | π x y τ . π  L2  out[L2,π,x] = {}  x  X  (y = None  y  Some ` Y)  ( (x,y)  set τ . x  X  (y = None  y  Some ` Y))}"

  let ?L1 = "bottom_completion X Y L1"
  
  have "?L1 = L1'a  L1'b" 
    unfolding L1'a_def L1'b_def bottom_completion.simps by blast
  then have " π x . out[?L1, π, x] = {y. π @ [(x, y)]  L1'a}  {y. π @ [(x, y)]  L1'b}"
    unfolding outputs.simps language_for_state.simps by blast

  let ?L2 = "bottom_completion X Y L2"
  
  have "?L2 = L2'a  L2'b" 
    unfolding L2'a_def L2'b_def bottom_completion.simps by blast
  then have " π x . out[?L2, π, x] = {y. π @ [(x, y)]  L2'a}  {y. π @ [(x, y)]  L2'b}"
    unfolding outputs.simps language_for_state.simps by blast

  have "is_language X ({None}  Some ` Y) ?L1"
    using bottom_completion_is_language[OF assms(1)] .
  have "is_language X ({None}  Some ` Y) ?L2"
    using bottom_completion_is_language[OF assms(2)] .
  then have " π x . out[bottom_completion X Y L2,π,x]  {None}  Some ` Y"
    by (meson outputs_in_alphabet) 

  have "(?L1 ≼[X, red ({None}  Some ` Y)] ?L2) = ( π  ?L1  ?L2 .  x  X . out[?L1,π,x]  out[?L2,π,x])"
    unfolding type_1_conforms.simps red.simps 
    using  π x . out[bottom_completion X Y L2,π,x]  {None}  Some ` Y by force
  also have " = ( π  ?L1  ?L2 .  x  X . (out[?L2,π,x] = {None}  Some ` Y  (out[?L1,π,x]  {}  out[?L1,π,x]  out[?L2,π,x])))"
    by (metis (no_types, lifting) IntD1 is_language X ({None}  Some ` Y) (bottom_completion X Y L1) is_language X ({None}  Some ` Y) (bottom_completion X Y L2) assms(1) bottom_completion_out(1) bottom_completion_out(2) bottom_completion_out(3) image_is_empty outputs_in_alphabet subset_antisym)
  also have " = ( π  ?L1  ?L2 .  x  X . (out[?L2,π,x] = {None}  Some ` Y  (is_not_undefined π L1  is_not_undefined π L2  out[L1,map (λ(x,y) . (x, the y)) π,x]  {}  out[L1,map (λ(x,y) . (x, the y)) π,x]  out[L2,map (λ(x,y) . (x, the y)) π,x])))"
  proof -
    have " π x . π  ?L1  ?L2  x  X  out[?L2,π,x]  {None}  Some ` Y  
              (out[?L1,π,x]  {}  out[?L1,π,x]  out[?L2,π,x]) = (is_not_undefined π L1  is_not_undefined π L2  out[L1,map (λ(x,y) . (x, the y)) π,x]  {}  out[L1,map (λ(x,y) . (x, the y)) π,x]  out[L2,map (λ(x,y) . (x, the y)) π,x])"
    proof -
      fix π x assume "π  ?L1  ?L2" and "x  X" and "out[?L2,π,x]  {None}  Some ` Y"
      then have "π  ?L1" and "π  ?L2" by blast+

      have "is_not_undefined π L2"
        using bottom_completion_out[OF assms(2) x  X π  ?L2]
        using out[bottom_completion X Y L2,π,x]  {None}  Some ` Y by fastforce 
      have "out[L2,map (λ(x, y). (x, the y)) π,x]  {}"
        using bottom_completion_out(1,2)[OF assms(2) x  X π  ?L2]
        using is_not_undefined π L2 out[bottom_completion X Y L2,π,x]  {None}  Some ` Y by blast 

      show "(out[?L1,π,x]  {}  out[?L1,π,x]  out[?L2,π,x]) = (is_not_undefined π L1  is_not_undefined π L2  out[L1,map (λ(x,y) . (x, the y)) π,x]  {}  out[L1,map (λ(x,y) . (x, the y)) π,x]  out[L2,map (λ(x,y) . (x, the y)) π,x])"
      proof (cases "is_not_undefined π L1")
        case False 
        then have "out[?L1,π,x] = {None}  Some ` Y"
          by (meson IntD1 π  bottom_completion X Y L1  bottom_completion X Y L2 x  X assms(1) bottom_completion_out(3)) 
        then have "¬ (out[?L1,π,x]  out[?L2,π,x])"
          by (metis is_language X ({None}  Some ` Y) (bottom_completion X Y L2) out[bottom_completion X Y L2,π,x]  {None}  Some ` Y outputs_in_alphabet subset_antisym) 
        then show ?thesis
          using False by presburger 
      next
        case True

        have "(out[?L1,π,x]  {}  out[?L1,π,x]  out[?L2,π,x]) = (out[L1,map (λ(x,y) . (x, the y)) π,x]  {}  out[L1,map (λ(x,y) . (x, the y)) π,x]  out[L2,map (λ(x,y) . (x, the y)) π,x])" 
        proof (cases "out[L1,map (λ(x, y). (x, the y)) π,x] = {}")
          case True

          have "¬ (out[?L1,π,x]  {}  out[?L1,π,x]  out[?L2,π,x])"
            unfolding bottom_completion_out(2)[OF assms(1) x  X π  ?L1 is_not_undefined π L1 True]
            by (meson x π. out[bottom_completion X Y L2,π,x]  {None}  Some ` Y out[bottom_completion X Y L2,π,x]  {None}  Some ` Y subset_antisym) 
          moreover have "¬ (out[L1,map (λ(x,y) . (x, the y)) π,x]  {}  out[L1,map (λ(x,y) . (x, the y)) π,x]  out[L2,map (λ(x,y) . (x, the y)) π,x])"
            using True by simp
          ultimately show ?thesis by blast
        next
          case False
          show ?thesis 
            unfolding bottom_completion_out(1)[OF assms(1) x  X π  ?L1 is_not_undefined π L1 False]
            unfolding bottom_completion_out(1)[OF assms(2) x  X π  ?L2 is_not_undefined π L2 out[L2,map (λ(x, y). (x, the y)) π,x]  {}]
            by blast
        qed
        then show ?thesis
          using is_not_undefined π L1 is_not_undefined π L2 
          by blast
      qed
    qed
    then show ?thesis
      by meson
  qed
  also have " = ( ( π  ?L1  ?L2 .  x  X . ¬ is_not_undefined π L1  is_not_undefined π L2  out[?L2,π,x] = {None}  Some ` Y) 
                   ( π  L1  L2 .  x  X . out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])))" 
    (is "?A = ?B")
  proof 
    show "?A  ?B"  
    proof  -
      assume ?A

      have " π x . π  ?L1  ?L2  x  X  ¬ is_not_undefined π L1  is_not_undefined π L2  out[?L2,π,x] = {None}  Some ` Y"
        using ?A by blast
      moreover have " π x . π  L1  L2  x  X  out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])"
      proof -
        fix π x assume "π  L1  L2" and "x  X" 

        let  = "map (λ(x, y). (x, Some y)) π" 

        have "is_not_undefined  L1" and "is_not_undefined  L2" 
          using π  L1  L2 by auto
        then have "  ?L1" and "  ?L2"
          by auto 

        show "out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])"
        proof (cases "out[L2,π,x] = {}")
          case True
          then show ?thesis by auto
        next
          case False
          then have "out[bottom_completion X Y L2,,x]  {None}  Some ` Y" 
            using bottom_completion_out(1)[OF assms(2) x  X   ?L2 is_not_undefined  L2]
            unfolding bottom_id
            by force
          then have "out[L1,map (λ(x, y). (x, the y)) ,x]  {}  out[L1,map (λ(x, y). (x, the y)) ,x]  out[L2,map (λ(x, y). (x, the y)) ,x]"
            using ?A 
            using   ?L1   ?L2 x  X 
            by blast
          then show "out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])"
            unfolding bottom_id by blast
        qed
      qed
      ultimately show ?B
        by meson
    qed
    show "?B  ?A" 
    proof -
      assume "?B" 

      have " π x . π  ?L1  ?L2  x  X  out[?L2,π,x] = {None}  Some ` Y  is_not_undefined π L1  is_not_undefined π L2  out[L1,map (λ(x, y). (x, the y)) π,x]  {}  out[L1,map (λ(x, y). (x, the y)) π,x]  out[L2,map (λ(x, y). (x, the y)) π,x]" 
      proof -
        fix π x assume "π  ?L1  ?L2" and "x  X"
        then have "π  ?L1" and "π  ?L2" by auto

        show "out[?L2,π,x] = {None}  Some ` Y  is_not_undefined π L1  is_not_undefined π L2  out[L1,map (λ(x, y). (x, the y)) π,x]  {}  out[L1,map (λ(x, y). (x, the y)) π,x]  out[L2,map (λ(x, y). (x, the y)) π,x]"
        proof (cases "out[?L2,π,x] = {None}  Some ` Y")
          case True
          then show ?thesis by blast
        next
          case False
          
          let  = "map (λ(x, y). (x, the y)) π"
          
          have "is_not_undefined π L2"
            using False (π bottom_completion X Y L1  bottom_completion X Y L2. xX. ¬ is_not_undefined π L1  is_not_undefined π L2  out[bottom_completion X Y L2,π,x] = {None}  Some ` Y)  (πL1  L2. xX. out[L2,π,x] = {}  out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x]) π  bottom_completion X Y L1  bottom_completion X Y L2 x  X
            by (meson π  bottom_completion X Y L2 assms(2) bottom_completion_out(3))
          then have "  L2" 
            using bottom_id
            by (metis (mono_tags, lifting) imageE is_not_undefined.elims(2)) 

          have "is_not_undefined π L1"
            using False (π bottom_completion X Y L1  bottom_completion X Y L2. xX. ¬ is_not_undefined π L1  is_not_undefined π L2  out[bottom_completion X Y L2,π,x] = {None}  Some ` Y)  (πL1  L2. xX. out[L2,π,x] = {}  out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x]) π  bottom_completion X Y L1  bottom_completion X Y L2 x  X
            using is_not_undefined π L2 by blast
          then have "  L1" 
            using bottom_id
            by (metis (mono_tags, lifting) imageE is_not_undefined.elims(2))

          have "out[L2,,x]  {}"
            using False bottom_completion_out(2)[OF assms(2) x  X π  ?L2 is_not_undefined π L2] 
            by blast
          then have "out[L1,,x]  {}" and "out[L1,,x]  out[L2,,x]"
            using ?B   L1   L2 x  X
            by (meson IntI)+ 
          then show ?thesis 
            using is_not_undefined π L1 is_not_undefined π L2
            by blast
        qed 
      qed
      then show ?A
        by blast
    qed
  qed
  also have " = ( ( π  ?L1  ?L2 .  x  X . ¬ is_not_undefined π L1  is_not_undefined π L2  out[L2,map (λ(x, y). (x, the y)) π,x] = {}) 
                   ( π  L1  L2 .  x  X . out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])))" 
    (is "(?A  ?B) = (?C  ?B)")
  proof -
    have "?A = ?C"
      by (metis IntD2 None_notin_image_Some UnCI assms(2) bottom_completion_out(1) bottom_completion_out(2) insertCI) 
    then show ?thesis by meson
  qed
  also have " = ( π  L1  L2 .  x  X . out[L2,π,x] = {}  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x]))" 
    (is "(?A  ?B) = ?B")
  proof -
    have "?B  ?A" 
    proof -
      assume "?B"


      have " π x . π  ?L1  ?L2  x  X  ¬ is_not_undefined π L1  is_not_undefined π L2  out[L2,map (λ(x, y). (x, the y)) π,x] = {}" 
      proof (rule ccontr)
        fix π x assume "π  ?L1  ?L2" and "x  X" and "¬ is_not_undefined π L1"  and "is_not_undefined π L2" 
                   and "out[L2,map (λ(x, y). (x, the y)) π,x]  {}"

        let  = "map (λ(x, y). (x, the y)) π"
        have "  L2"
          by (metis (mono_tags, lifting) is_not_undefined π L2 bottom_id image_iff is_not_undefined.elims(2)) 

        have "π  ?L1"
          using π  ?L1  ?L2 by auto
        moreover have "π  L1'a"
          unfolding L1'a_def using ¬ is_not_undefined π L1 by auto 
        ultimately have "π  L1'b"
          unfolding ?L1 = L1'a  L1'b by blast
        then obtain π' x' y' τ where "π = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@τ"    
                                 and "π'  L1" 
                                 and "out[L1,π',x'] = {}" 
                                 and "x'  X" 
                                 and "(y' = None  y'  Some ` Y)" 
                                 and "( (x,y)  set τ . x  X  (y = None  y  Some ` Y))"
          unfolding L1'b_def 
          by blast

        have " = (π'@[(x', the y')]) @ (map (λ(x, y). (x, the y)) τ)"
          unfolding π = (map (λ(x,y) . (x,Some y)) π')@[(x',y')]@τ 
          using bottom_id by (induction π' arbitrary: x' y' τ; auto)
        then have "π'@[(x', the y')]  L2" and "π'  L2"
          using   L2
          by (metis assms(2) prefix_closure_no_member)+
        then have "out[L2,π',x']  {}"
          by fastforce

        show False
          using ?B π'  L1 π'  L2 x'  X out[L2,π',x']  {} out[L1,π',x'] = {}
          by blast
      qed
      then show ?A
        by blast
    qed
    then show ?thesis by meson
  qed
  also have " = (L1 ≼[X,quasired Y] L2)"     
    unfolding quasired_type_1[OF assms, symmetric] quasi_reduction_def
    by (meson assms(2) executable_inputs_in_alphabet outputs_executable) 
  finally show ?thesis 
    by meson
qed


subsection ‹Strong Reduction via Reduction and Undefinedness Outputs›

fun non_bottom_shortening :: "('x,'y option) word  ('x,'y option) word" where
  "non_bottom_shortening π = filter (λ (x,y) . y  None) π"

fun non_bottom_projection :: "('x,'y option) word  ('x,'y) word" where
  "non_bottom_projection π = map (λ(x,y) . (x,the y)) (non_bottom_shortening π)" 

lemma non_bottom_projection_split: "non_bottom_projection (π'@π'') = (non_bottom_projection π')@(non_bottom_projection π'')"
  by (induction π' arbitrary: π''; auto)

lemma non_bottom_projection_id : "non_bottom_projection (map (λ(x,y) . (x,Some y)) π) = π"
  by (induction π; auto)


fun undefinedness_completion :: "'x alphabet  ('x,'y) language  ('x, 'y option) language" where 
  "undefinedness_completion X L = 
    {π . non_bottom_projection π  L  ( π' x π'' . π = π' @ [(x,None)] @ π''  x  X  out[L, non_bottom_projection π', x] = {})}"

lemma undefinedness_completion_is_language :
  assumes "is_language X Y L"
shows "is_language X ({None}  Some ` Y) (undefinedness_completion X L)"
proof -
  let ?L = "undefinedness_completion X L"

  have "[]  L"
    using language_contains_nil[OF assms] .
  moreover have "non_bottom_projection [] = []" 
    by auto
  ultimately have "[]  ?L"
    by simp 
  then have "?L  {}"
    by blast
  moreover have " π . π  ?L  ( xy . xy  set π  fst xy  X  snd xy  ({None}  Some ` Y))"
            and " π . π  ?L  ( π' . prefix π' π  π'  ?L)"
  proof -
    fix π assume "π  ?L"
    then have p1: "non_bottom_projection π  L" 
          and p2: " π' x π'' . π = π' @ [(x,None)] @ π''  x  X  out[L, non_bottom_projection π', x] = {}"
      by auto

    show " xy . xy  set π  fst xy  X  snd xy  ({None}  Some ` Y)"
    proof -
      fix xy assume "xy  set π"
      then obtain π' x y π'' where "xy = (x,y)" and "π = π' @ [(x,y)] @ π''"
        by (metis append_Cons append_Nil old.prod.exhaust split_list)  
      

      show "fst xy  X  snd xy  ({None}  Some ` Y)" 
      proof (cases "snd xy")
        case None
        then show ?thesis 
          unfolding xy = (x,y) snd_conv
          using p2 π = π' @ [(x,y)] @ π''
          by simp 
      next
        case (Some y')
        then have "y = Some y'"
          unfolding xy = (x,y) by auto
        have "(x,y')  set (non_bottom_projection π)"
          unfolding π = π' @ [(x,y)] @ π'' y = Some y'
          by auto
        then show ?thesis 
          unfolding xy = (x,y) snd_conv y = Some y' fst_conv
          using p1 assms
          unfolding is_language.simps by fastforce
      qed 
    qed

    show " π' . prefix π' π  π'  ?L" 
    proof -
      fix π' assume "prefix π' π"
      then obtain π'' where "π = π'@π''"
        using prefixE by blast

      have "non_bottom_projection π = (non_bottom_projection π')@(non_bottom_projection π'')"
        unfolding π = π'@π''
        using non_bottom_projection_split .
      then have "non_bottom_projection π'  L"
        by (metis assms p1 prefix_closure_no_member) 
      moreover have " π''' x π'''' . π' = π''' @ [(x,None)] @ π''''  x  X  out[L, non_bottom_projection π''', x] = {}"
        using p2 unfolding π = π'@π''
        by (metis append.assoc) 
      ultimately show "π'  ?L"
        by fastforce 
    qed
  qed 
  ultimately show ?thesis
    by (meson is_language.elims(3)) 
qed


lemma undefinedness_completion_inclusion :
  assumes "π  L" 
shows "map (λ(x,y) . (x,Some y)) π  undefinedness_completion X L"
proof -
  let  = "map (λ(x,y) . (x,Some y)) π"

  have " a . (a,None)  set "
    by (induction π; auto)
  then have " π' x π'' .  = π' @ [(x,None)] @ π''  x  X  out[L, non_bottom_projection π', x] = {}"
    by (metis Cons_eq_appendI in_set_conv_decomp) 
  moreover have "non_bottom_projection   L"
    using π  L unfolding non_bottom_projection_id .
  ultimately show ?thesis 
    by auto 
qed


lemma undefinedness_completion_out_shortening :
  assumes "is_language X Y L"
  and     "π  undefinedness_completion X L" 
  and     "x  X" 
shows "out[undefinedness_completion X L, π, x] = out[undefinedness_completion X L, non_bottom_shortening π, x]" 
using assms(2,3) proof (induction "length π" arbitrary: π x rule: less_induct)
  case less

  let ?L = "undefinedness_completion X L"

  show ?case proof (cases π rule: rev_cases)
    case Nil
    then show ?thesis by auto
  next
    case (snoc π' xy) 

    then obtain x' y' where "xy = (x',y')" by fastforce
  
    have "x'  X"
      using snoc less.prems(1) unfolding xy = (x',y')
      using undefinedness_completion_is_language[OF assms(1)]
      by (metis fst_conv is_language.elims(2) last_in_set snoc_eq_iff_butlast) 
  
    have "π'  ?L"
      using snoc less.prems(1) 
      using undefinedness_completion_is_language[OF assms(1)]
      using prefix_closure_no_member by blast 

    show ?thesis proof (cases y')
      case None

      then have "non_bottom_shortening π = non_bottom_shortening π'" 
        unfolding xy = (x',y') snoc by auto
      then have "out[?L, non_bottom_shortening π, x] = out[?L, non_bottom_shortening π', x]"
        by simp
      also have " = out[?L, π', x]"
        using less.hyps[OF _ π'  ?L x  X] unfolding snoc
        by (metis Suc_lessD length_append_singleton not_less_eq)
      also have " = out[?L, π, x]" 
      proof 

        show "out[?L, π', x]  out[?L, π, x]"
        proof 
          fix y assume "y  out[?L, π', x]"
          then have "π'@[(x,y)]  ?L"
            by auto
          then have p1: "non_bottom_projection (π'@[(x,y)])  L" 
                and p2: " γ' a γ'' . π'@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}" 
            by auto

          have "non_bottom_projection (π@[(x,y)]) = non_bottom_projection (π'@[(x,y)])"
            unfolding snoc xy = (x',y') None by auto
          then have "non_bottom_projection (π@[(x,y)])  L"
            using p1 by simp
          moreover have " γ' a γ'' . π@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}"
          proof -
            fix γ' a γ'' assume "π@[(x,y)] = γ' @ [(a,None)] @ γ''"
            then have "π'@[(x',None)]@[(x,y)] = γ' @ [(a,None)] @ γ''"
              unfolding snoc xy = (x',y') None by auto

            show "a  X  out[L, non_bottom_projection γ', a] = {}" 
            proof (cases γ'' rule: rev_cases)
              case Nil
              then show ?thesis
                using π @ [(x, y)] = γ' @ [(a, None)] @ γ'' non_bottom_shortening π = non_bottom_shortening π' p2 by auto 
            next
              case (snoc γ''' xy')
              then show ?thesis
                using π @ [(x, y)] = γ' @ [(a, None)] @ γ'' less.prems(1) by force 
            qed 
          qed
          ultimately show "y  out[?L, π, x]"
            by auto
        qed

        show "out[?L, π, x]  out[?L, π', x]" 
        proof 
          fix y assume "y  out[?L, π, x]"
          then have "π'@[(x',None)]@[(x,y)]  ?L"
            unfolding snoc xy = (x',y') None
            by auto
          then have p1: "non_bottom_projection (π'@[(x',None)]@[(x,y)])  L" 
                and p2: " γ' a γ'' . π'@[(x',None)]@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}" 
            by auto

          have "non_bottom_projection (π'@[(x',None)]@[(x,y)]) = non_bottom_projection (π'@[(x,y)])"
            by auto
          then have "non_bottom_projection (π'@[(x,y)])  L"
            using p1 by auto
          moreover have " γ' a γ'' . π'@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}"
          proof -
            fix γ' a γ'' assume "π'@[(x,y)] = γ' @ [(a,None)] @ γ''"

            show "a  X  out[L, non_bottom_projection γ', a] = {}"
            proof (cases γ'' rule: rev_cases)
              case Nil 
              then show ?thesis
                by (metis None π' @ [(x, y)] = γ' @ [(a, None)] @ γ'' non_bottom_shortening π = non_bottom_shortening π' xy = (x', y') append.assoc append.right_neutral append1_eq_conv non_bottom_projection.simps p2 snoc) 
            next
              case (snoc γ''' xy')
              then show ?thesis
                using π' @ [(x, y)] = γ' @ [(a, None)] @ γ'' π'  undefinedness_completion X L by force                 
            qed
          qed
          ultimately show "y  out[?L, π', x]" 
            by auto 
        qed
      qed
      finally show ?thesis 
        by blast
    next
      case (Some y'')
      
      have "non_bottom_shortening π = (non_bottom_shortening π')@[(x',Some y'')]"
        unfolding snoc xy = (x',y') Some by auto
      then have "non_bottom_projection π = (non_bottom_projection π')@[(x',y'')]"
        by auto

      have "π' @ [(x', Some y'')]  ?L" 
        using less.prems(1) unfolding snoc xy = (x',y') Some .
      then have "Some y''  out[?L,π',x']"
        by auto
      moreover have "out[?L,π',x'] = out[?L,non_bottom_shortening π',x']"
        using less.hyps[OF _ π'  ?L x'  X]
        unfolding snoc xy = (x',y') Some
        by (metis length_append_singleton lessI)
      ultimately have "Some y''  out[?L,non_bottom_shortening π',x']"
        by blast


      show ?thesis 
      proof 
        show "out[?L,π,x]  out[?L,non_bottom_shortening π,x]"
        proof 
          fix y assume "y  out[?L,π,x]" 
          then have "π'@[(x',Some y'')]@[(x,y)]  ?L"
            unfolding snoc xy = (x',y') Some by auto
          then have p1: "non_bottom_projection (π'@[(x',Some y'')]@[(x,y)])  L" 
                and p2: " γ' a γ'' . π'@[(x',Some y'')]@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}" 
            by auto

          have "non_bottom_projection ((non_bottom_shortening π)@[(x,y)]) = non_bottom_projection (π'@[(x',Some y'')]@[(x,y)])"
            unfolding non_bottom_shortening π = (non_bottom_shortening π')@[(x',Some y'')]
            by auto
          then have "non_bottom_projection ((non_bottom_shortening π)@[(x,y)])  L"
            using p1 by simp
          moreover have " γ' a γ'' . (non_bottom_shortening π)@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}"
          proof -
            fix γ' a γ'' assume "(non_bottom_shortening π)@[(x,y)] = γ' @ [(a,None)] @ γ''" 
            moreover have "(a, None)  set (non_bottom_shortening π)"
              by (induction π; auto)
            moreover have " xs a ys b zs . xs@[a] = ys@[b]@zs  b  set xs  zs = []" 
              by (metis append_Cons append_Nil butlast.simps(2) butlast_snoc in_set_butlast_appendI list.distinct(1) list.sel(1) list.set_sel(1))
            ultimately have "γ'' = []"
              by fastforce 
            then have "γ' = non_bottom_shortening π"
                  and "x = a"
                  and "y = None"
              using (non_bottom_shortening π)@[(x,y)] = γ' @ [(a,None)] @ γ''
              by auto


            show "a  X  out[L, non_bottom_projection γ', a] = {}"  
              using x  X unfolding x = a 
              unfolding γ' = non_bottom_shortening π
              by (metis (no_types, lifting) non_bottom_projection (non_bottom_shortening π @ [(x, y)]) = non_bottom_projection (π' @ [(x', Some y'')] @ [(x, y)]) x = a y = None append.assoc append.right_neutral append_same_eq non_bottom_projection_split p2)  
          qed
          ultimately show "y  out[?L,non_bottom_shortening π,x]"
            by auto
        qed

        show "out[?L,non_bottom_shortening π,x]  out[?L,π,x]" 
        proof 
          fix y assume "y  out[?L,non_bottom_shortening π,x]" 
          then have "(non_bottom_shortening π')@[(x',Some y'')]@[(x,y)]  ?L"
            unfolding snoc xy = (x',y') Some by auto
          then have p1: "non_bottom_projection ((non_bottom_shortening π')@[(x',Some y'')]@[(x,y)])  L" 
                and p2: " γ' a γ'' . (non_bottom_shortening π')@[(x',Some y'')]@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}" 
            by auto

          have "non_bottom_projection ((non_bottom_shortening π')@[(x',Some y'')]@[(x,y)]) = non_bottom_projection (π@[(x,y)])"
            unfolding snoc xy = (x',y') Some by auto
          then have "non_bottom_projection (π@[(x,y)])  L"
            using p1 by presburger 
          moreover have " γ' a γ'' . π@[(x,y)] = γ' @ [(a,None)] @ γ''  a  X  out[L, non_bottom_projection γ', a] = {}"
          proof 
            fix γ' a γ'' assume "π@[(x,y)] = γ' @ [(a,None)] @ γ''" 
            then have "(a,None)  set (π@[(x,y)])"
              by auto
            then consider "(a,None)  set π" | "(a,None) = (x,y)"
              by auto              
            then show "a  X"
              by (metis assms(1) fst_conv is_language.elims(2) less.prems(1) less.prems(2) undefinedness_completion_is_language) 

            show "out[L,non_bottom_projection γ',a] = {}" 
            proof (cases γ'' rule: rev_cases)
              case Nil
              then have "π = γ'" and "x = a" and "y = None"
                using π@[(x,y)] = γ' @ [(a,None)] @ γ'' by auto 
              then show ?thesis
                by (metis (no_types, opaque_lifting) non_bottom_projection (non_bottom_shortening π' @ [(x', Some y'')] @ [(x, y)]) = non_bottom_projection (π @ [(x, y)]) non_bottom_shortening π = non_bottom_shortening π' @ [(x', Some y'')] append.assoc append_Cons append_Nil append_same_eq non_bottom_projection_split p2)   
            next
              case (snoc γ''' xy')
              then have "π = γ' @ [(a, None)] @ γ'''"
                using π@[(x,y)] = γ' @ [(a,None)] @ γ'' by auto

              have "γ' @ [(a, None)]  ?L"
                using less.prems(1) unfolding π = γ' @ [(a, None)] @ γ''' 
                using undefinedness_completion_is_language[OF assms(1)]
                by (metis append_assoc prefix_closure_no_member)
              then show "out[L, non_bottom_projection γ', a] = {}"
                by auto
            qed
          qed
          ultimately show "y  out[?L,π,x]"
            by auto
        qed
      qed
    qed
  qed
qed



lemma undefinedness_completion_out_projection_not_empty :
  assumes "is_language X Y L"
  and     "π  undefinedness_completion X L" 
  and     "x  X" 
  and     "out[L, non_bottom_projection π, x]  {}"
shows "out[undefinedness_completion X L, non_bottom_shortening π, x] = Some ` out[L, non_bottom_projection π, x]" 
proof 
  
  let ?L = "undefinedness_completion X L"

  have "π@[(x,None)]  ?L" 
    using assms(4) by auto
  then have "None  out[?L,π,x]"
    by auto
  then have "None  out[?L,non_bottom_shortening π,x]"
    using undefinedness_completion_out_shortening[OF assms(1,2,3)] by blast
  then have "(non_bottom_shortening π)@[(x,None)]  ?L"
    by auto

  show "out[?L, non_bottom_shortening π, x]  Some ` out[L, non_bottom_projection π, x]" 
  proof 
    fix y assume "y  out[?L, non_bottom_shortening π, x]"
    then have "(non_bottom_shortening π) @ [(x,y)]  ?L" by auto
    then have "y  None" 
      using (non_bottom_shortening π)@[(x,None)]  ?L
      by meson 
    then obtain y' where "y = Some y'"
      by auto 

    have "non_bottom_projection ((non_bottom_shortening π) @ [(x,y)]) = (non_bottom_projection π) @ [(x,y')]"
      unfolding y = Some y' 
      by (induction π; auto)
    then have "(non_bottom_projection π) @ [(x,y')]  L"
      using (non_bottom_shortening π) @ [(x,y)]  ?L unfolding y = Some y' 
      by auto
    then show "y  Some ` out[L, non_bottom_projection π, x]" 
      unfolding y = Some y' by auto
  qed

  show "Some ` out[L,non_bottom_projection π,x]  out[?L,non_bottom_shortening π,x]" 
  proof 
    fix y assume "y  Some ` out[L,non_bottom_projection π,x]"
    then obtain y' where "y = Some y'" and "y'  out[L,non_bottom_projection π,x]"
      by auto
    then have "(non_bottom_projection π) @ [(x,y')]  L"
      by auto
    moreover have "non_bottom_projection ((non_bottom_shortening π) @ [(x,y)]) = (non_bottom_projection π) @ [(x,y')]"
      unfolding y = Some y' 
      by (induction π; auto)
    ultimately have "non_bottom_projection ((non_bottom_shortening π) @ [(x,y)])  L"
      unfolding y = Some y'  
      by auto
    moreover have " π' x' π'' . ((non_bottom_shortening π) @ [(x,y)]) = π' @ [(x',None)] @ π''  x'  X  out[L, non_bottom_projection π', x'] = {}"
    proof -
      fix π' x' π'' assume "((non_bottom_shortening π) @ [(x,y)]) = π' @ [(x',None)] @ π''"
      then have "(x',None)  set (non_bottom_shortening π)"
        by (metis y = Some y' append_Cons in_set_conv_decomp old.prod.inject option.distinct(1) rotate1.simps(2) set_ConsD set_rotate1) 
      then have False 
        by (induction π; auto)
      then show "x'  X  out[L, non_bottom_projection π', x'] = {}"
        by blast
    qed
    ultimately show "y  out[?L,non_bottom_shortening π,x]"
      by auto
  qed
qed


lemma undefinedness_completion_out_projection_empty :
  assumes "is_language X Y L"
  and     "π  undefinedness_completion X L" 
  and     "x  X" 
  and     "out[L, non_bottom_projection π, x] = {}"
shows "out[undefinedness_completion X L, non_bottom_shortening π, x] = {None}" 
proof 
  
  let ?L = "undefinedness_completion X L"

  have p1: "non_bottom_projection π  L" 
   and p2: " π' x π'' . π = π' @ [(x,None)] @ π''  x  X  out[L, non_bottom_projection π', x] = {}"
    using assms(2) by auto

  have "non_bottom_projection (π@[(x,None)])  L"
    using p1 by auto
  moreover have " π' x' π'' . π@[(x,None)] = π' @ [(x',None)] @ π''  x'  X  out[L, non_bottom_projection π', x'] = {}"
  proof -
    fix π' x' π'' assume "π@[(x,None)] = π' @ [(x',None)] @ π''"
    show "x'  X  out[L, non_bottom_projection π', x'] = {}"
    proof (cases π'' rule: rev_cases)
      case Nil
      then show ?thesis
        using π @ [(x, None)] = π' @ [(x', None)] @ π'' assms(3) assms(4) by auto
    next
      case (snoc ys y)
      then show ?thesis
        using π @ [(x, None)] = π' @ [(x', None)] @ π'' p2 by auto
    qed 
  qed
  ultimately have "π@[(x,None)]  ?L"
    by auto
  then show "{None}  out[?L,non_bottom_shortening π,x]"
    unfolding undefinedness_completion_out_shortening[OF assms(1,2,3), symmetric] 
    by auto

  show "out[?L,non_bottom_shortening π,x]  {None}" 
  proof (rule ccontr) 
    assume "¬ out[?L,non_bottom_shortening π,x]  {None}"    
    then obtain y where "y  out[?L,non_bottom_shortening π,x]"  and "y  None"
      by blast 
    then obtain y' where "y = Some y'"
      by auto

    have "π@[(x,Some y')]  ?L" 
      using y  out[?L,non_bottom_shortening π,x]
      unfolding y = Some y'
      unfolding undefinedness_completion_out_shortening[OF assms(1,2,3), symmetric] 
      by auto
    then have "(non_bottom_projection π)@[(x,y')]  L"
      by auto
    then show False
      using assms(4) by auto
  qed
qed


theorem strongred_via_red : 
  assumes "is_language X Y L1"
  and     "is_language X Y L2"
shows "(L1 ≼[X,strongred Y] L2)  ((undefinedness_completion X L1) ≼[X, red ({None}  Some ` Y)] (undefinedness_completion X L2))" 
proof -

  let ?L1 = "undefinedness_completion X L1"
  let ?L2 = "undefinedness_completion X L2"

  have "(L1 ≼[X,strongred Y] L2) = ( π  L1  L2 .  x  X . (out[L1,π,x] = {}  out[L2,π,x] = {})  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x]))"
    (is "?A = ?B")
  proof 
    show "?A  ?B" 
      unfolding strongred_type_1[OF assms, symmetric] strong_reduction_def quasi_reduction_def
      by (metis outputs_executable)
    show "?B  ?A" 
      unfolding strongred_type_1[OF assms, symmetric] strong_reduction_def quasi_reduction_def
      by (metis assms(1) assms(2) executable_inputs_in_alphabet outputs_executable subset_empty)
  qed
  also have " = ( π  ?L1  ?L2 .  x  X . (out[L1,non_bottom_projection π,x] = {}  out[L2,non_bottom_projection π,x] = {})  (out[L1,non_bottom_projection π,x]  {}  out[L1,non_bottom_projection π,x]  out[L2,non_bottom_projection π,x]))"
    (is "?A = ?B")
  proof 
    have " π x . ?A  π  ?L1  ?L2  x  X  (out[L1,non_bottom_projection π,x] = {}  out[L2,non_bottom_projection π,x] = {})  (out[L1,non_bottom_projection π,x]  {}  out[L1,non_bottom_projection π,x]  out[L2,non_bottom_projection π,x])"
    proof -
      fix π x assume "?A" and "π  ?L1  ?L2" and "x  X"

      let  = "non_bottom_projection π"

      have "  L1"
       and "  L2"
        using π  ?L1  ?L2 by auto
      then show "(out[L1,,x] = {}  out[L2,,x] = {})  (out[L1,,x]  {}  out[L1,,x]  out[L2,,x])"
        using ?A x  X by blast
    qed
    then show "?A  ?B" 
      by blast

    have " π x . ?B  π  L1  L2  x  X  (out[L1,π,x] = {}  out[L2,π,x] = {})  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])"
    proof -
      fix π x assume "?B" and "π  L1  L2" and "x  X"

      let  = "map (λ(x,y) . (x,Some y)) π"

      have "  ?L1" and "  ?L2" 
        using π  L1  L2 undefinedness_completion_inclusion by blast+
      then have "(out[L1,non_bottom_projection ,x] = {}  out[L2,non_bottom_projection ,x] = {})  (out[L1,non_bottom_projection ,x]  {}  out[L1,non_bottom_projection ,x]  out[L2,non_bottom_projection ,x])"
        using ?B x  X by blast 
      then show "(out[L1,π,x] = {}  out[L2,π,x] = {})  (out[L1,π,x]  {}  out[L1,π,x]  out[L2,π,x])"
        unfolding non_bottom_projection_id .
    qed
    then show "?B  ?A"
      by blast
  qed
  also have " = ( π  ?L1  ?L2 .  x  X . (out[?L1,π,x] = {None}  out[?L2,π,x] = {None})  (out[?L1,π,x]  {None}  out[?L1,π,x]  out[?L2,π,x]))"
  proof -
    have " π x . π  ?L1  ?L2  x  X  (out[L1,non_bottom_projection π,x] = {}  out[L2,non_bottom_projection π,x] = {}) = (out[?L1,π,x] = {None}  out[?L2,π,x] = {None})"
      by (metis IntD1 IntD2 None_notin_image_Some assms(1) assms(2) insertCI undefinedness_completion_out_projection_empty undefinedness_completion_out_projection_not_empty undefinedness_completion_out_shortening)
    moreover have " π x . π  ?L1  ?L2  x  X  (out[L1,non_bottom_projection π,x]  {}  out[L1,non_bottom_projection π,x]  out[L2,non_bottom_projection π,x]) = (out[?L1,π,x]  {None}  out[?L1,π,x]  out[?L2,π,x])"
    proof - 
      fix π x assume "π  ?L1  ?L2" and "x  X" 
      then have "π  ?L1" and "π  ?L2" by auto

      have "(out[L1,non_bottom_projection π,x]  {}) = (out[?L1,π,x]  {None})"
        by (metis None_notin_image_Some π  undefinedness_completion X L1 x  X assms(1) singletonI undefinedness_completion_out_projection_empty undefinedness_completion_out_projection_not_empty undefinedness_completion_out_shortening)


      show "(out[L1,non_bottom_projection π,x]  {}  out[L1,non_bottom_projection π,x]  out[L2,non_bottom_projection π,x]) = (out[?L1,π,x]  {None}  out[?L1,π,x]  out[?L2,π,x])"
      proof (cases "out[L1,non_bottom_projection π,x]  {}")
        case False
        then show ?thesis using (out[L1,non_bottom_projection π,x]  {}) = (out[?L1,π,x]  {None}) by blast
      next
        case True
        have "out[undefinedness_completion X L1,π,x] = Some ` out[L1,non_bottom_projection π,x]"
          using undefinedness_completion_out_projection_not_empty[OF assms(1) π  ?L1 x  X True]
          unfolding undefinedness_completion_out_shortening[OF assms(1) π  ?L1 x  X,symmetric] .

        
        show ?thesis proof (cases "out[L2,non_bottom_projection π,x] = {}")
          case True
          then show ?thesis
            by (metis (out[L1,non_bottom_projection π,x]  {}) = (out[undefinedness_completion X L1,π,x]  {None}) π  undefinedness_completion X L2 out[undefinedness_completion X L1,π,x] = Some ` out[L1,non_bottom_projection π,x] x  X assms(2) image_is_empty subset_empty subset_singletonD undefinedness_completion_out_projection_empty undefinedness_completion_out_shortening)   
        next
          case False

          have "out[undefinedness_completion X L2,π,x] = Some ` out[L2,non_bottom_projection π,x]"
            using undefinedness_completion_out_projection_not_empty[OF assms(2) π  ?L2 x  X False]
            unfolding undefinedness_completion_out_shortening[OF assms(2) π  ?L2 x  X,symmetric] .

          show ?thesis 
            unfolding out[undefinedness_completion X L1,π,x] = Some ` out[L1,non_bottom_projection π,x] 
            unfolding out[undefinedness_completion X L2,π,x] = Some ` out[L2,non_bottom_projection π,x]
            by (metis (out[L1,non_bottom_projection π,x]  {}) = (out[undefinedness_completion X L1,π,x]  {None}) out[undefinedness_completion X L1,π,x] = Some ` out[L1,non_bottom_projection π,x] subset_image_iff these_image_Some_eq)            
        qed 
      qed 
    qed
    ultimately show ?thesis
      by meson
  qed
  also have " = ( π  ?L1  ?L2 .  x  X . out[?L1,π,x]  out[?L2,π,x])" 
    (is "?A = ?B")
  proof
    show "?A  ?B"
      by blast 
    show "?B  ?A"
      by (metis IntD2 None_notin_image_Some assms(2) insert_subset undefinedness_completion_out_projection_empty undefinedness_completion_out_projection_not_empty undefinedness_completion_out_shortening) 
  qed
  also have " = (?L1 ≼[X, red ({None}  Some ` Y)] ?L2)"
    unfolding type_1_conforms.simps red.simps
    using outputs_in_alphabet[OF undefinedness_completion_is_language[OF assms(2)]]
    by force
  finally show ?thesis .
qed


end