Theory Process_Normalization

(***********************************************************************************
 * Copyright (c) 2025 Université Paris-Saclay
 *
 * Author: Benoît Ballenghien, Université Paris-Saclay,
 *         CNRS, ENS Paris-Saclay, LMF
 * Author: Burkhart Wolff, Université Paris-Saclay,
 *         CNRS, ENS Paris-Saclay, LMF
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 ***********************************************************************************)


chapter ‹ProcOmata: Functional Automata embedded into CSP Processes›

(*<*)
theory Process_Normalization
  imports "HOL-CSP_PTick"
begin
  (*>*)


text ‹
We will often have to perform induction on both the list of automata
and the list of states, provided that they have the same length.›

lemma induct_2_lists012 [consumes 1, case_names Nil single Cons] : 
  length xs = length ys; P [] []; x1 y1. P [x1] [y1];
    x1 x2 xs y1 y2 ys. length xs = length ys  P xs ys 
                         P (x2 # xs) (y2 # ys)  P (x1 # x2 # xs) (y1 # y2 # ys)
    P xs ys
  by (induct xs arbitrary: ys rule: induct_list012)
    (auto simp add: Suc_length_conv length_Suc_conv)

lemma nat_induct_012 [case_names 0 1 2 Suc]:
  P 0; P (Suc 0); P (Suc (Suc 0)); k. Suc (Suc 0)  k  P k  P (Suc k)  P n
  by (metis One_nat_def Suc_1 less_2_cases_iff linorder_not_le nat_induct)



text ‹
The following results will be moved to sessionRestriction_Spaces in the future.
›

lemma restriction_shift_iterated :
  restriction_shift (f ^^ k) (int k * m)
  if restriction_shift f m for f :: 'a  'a :: restriction_space
proof (induct k)
  show restriction_shift (f ^^ 0) (int 0 * m)
    by (simp add: restriction_shiftI)
next
  fix k assume * : restriction_shift (f ^^ k) (int k * m)
  have restriction_shift (f ^^ Suc k) (int (Suc k) * m) 
        restriction_shift (λx. f ((f ^^ k) x)) (int k * m + m)
    by (simp add: comp_def distrib_left mult.commute add.commute)
  also have  by (fact restriction_shift_comp_restriction_shift[OF that "*"])
  finally show restriction_shift (f ^^ Suc k) (int (Suc k) * m) .
qed

lemma non_destructive_iterated :
  non_destructive f  non_destructive (f ^^ k)
  for f :: 'a  'a :: restriction_space
  by (metis mult.commute mult_zero_left non_destructive_def non_destructive_on_def
      restriction_shift_def restriction_shift_iterated)

lemma constructive_iterated :
  constructive (f ^^ k) if 0 < k constructive f
for f :: 'a  'a :: restriction_space
proof -
  from constructive f have restriction_shift f 1
    unfolding constructive_def constructive_on_def restriction_shift_def by blast
  with restriction_shift_iterated
  have restriction_shift (f ^^ k) (int k * 1) .
  hence restriction_shift (f ^^ k) (int k) by simp
  with 0 < k show constructive (f ^^ k)
    by (metis One_nat_def constructive_def constructive_on_def
        less_eq_Suc_le nat_int_comparison(3) of_nat_1_eq_iff
        restriction_shift_def restriction_shift_imp_restriction_shift_le )
qed


lemma restriction_fix_unique_iterated :
  0 < k; constructive f; (f ^^ k) x = x  (υ x. f x) = x
  by (metis constructive_iterated funpow_swap1 restriction_fix_unique)


lemma restriction_fix_iterated :
  0 < k  constructive f  (υ x. (f ^^ k) x) = (υ x. f x)
  by (metis constructive_iterated restriction_fix_eq restriction_fix_unique_iterated)



corollary restriction_fix_ind_iterated
  [consumes 1, case_names constructive adm base step]:
  P (υ x. f x) if 0 < k constructive f adm P P x x. P x  P ((f ^^ k) x)
proof -
  from constructive_iterated that(1, 2) have constructive (f ^^ k) .
  from restriction_fix_ind[OF this that(3-5)] have P (υ x. (f ^^ k) x) .
  also from restriction_fix_iterated that(1, 2) have (υ x. (f ^^ k) x) = (υ x. f x) .
  finally show P (υ x. f x) .
qed




section ‹Definitions›


subsection ‹Non-deterministic and deterministic Automata›

unbundle option_type_syntax

type_synonym (, 'a) enabl  =   'a set
type_synonym (, 'a, 'σ') trans =   'a  'σ'
type_synonym (, 'a) transd  = (, 'a,  option) trans
type_synonym (, 'a) transnd = (, 'a,  set) trans

record (, 'a, 'σ', 'r) A =
  τ :: (, 'a, 'σ') trans
  ω ::   'r

type_synonym (, 'a, 'r) Ad = (, 'a,  option, 'r option) A
type_synonym (, 'a, 'r, ) Ad_scheme = (, 'a,  option, 'r option, ) A_scheme
type_synonym (, 'a, 'r) And = (, 'a,  set, 'r set) A
type_synonym (, 'a, 'r, ) And_scheme = (, 'a,  set, 'r set, ) A_scheme



subsection ‹Enableness›

consts ε :: (, 'a, 'σ', 'r', ) A_scheme  (, 'a) enabl
overloading
  εd  ε :: (, 'a,  option, 'r', ) A_scheme  (, 'a) enabl
  εnd  ε :: (, 'a,  set, 'r', ) A_scheme  (, 'a) enabl
begin
fun εd :: (, 'a,  option, 'r', ) A_scheme  (, 'a) enabl
  where εd A σ = {a. τ A σ a  }
fun εnd :: (, 'a,  set, 'r', ) A_scheme  (, 'a) enabl
  where εnd A σ = {a. τ A σ a  {}}
end

lemmas ε_simps[simp del] = εd.simps εnd.simps


subsection ‹States allowing Termination›

consts ρ :: (, 'a, 'σ', 'r', ) A_scheme   set
overloading
  ρd  ρ :: (, 'a, 'σ', 'r option, ) A_scheme   set
  ρnd  ρ :: (, 'a, 'σ', 'r set, ) A_scheme   set
begin
fun ρd :: (, 'a, 'σ', 'r option, ) A_scheme   set
  where ρd A = {σ. ω A σ  }
fun ρnd :: (, 'a, 'σ', 'r set, ) A_scheme   set
  where ρnd A = {σ. ω A σ  {}}
end

lemmas ρ_simps[simp del] = ρd.simps ρnd.simps


subsection ‹Reachability›

inductive_set d :: (, 'a, 'r, ) Ad_scheme     set
  for A :: (, 'a, 'r, ) Ad_scheme and σ :: 
  where init : σ   d A σ
  |     step : σ'  d A σ  σ'' = τ A σ' a  σ''  d A σ

inductive_set nd :: (, 'a, 'r, ) And_scheme     set
  for A :: (, 'a, 'r, ) And_scheme and σ :: 
  where init : σ   nd A σ
  |     step : σ'  nd A σ  σ''  τ A σ' a  σ''  nd A σ


lemma d_trans: σ''  d A σ'  σ'  d A σ  σ''  d A σ
  by (induct rule: d.induct, simp add: d.init) (meson d.step)

lemma nd_trans: σ''  nd A σ'  σ'  nd A σ  σ''  nd A σ
  by (induct rule: nd.induct, simp add: nd.init) (meson nd.step)



subsection ‹Morphisms›

text ‹
Our morphisms are defined considering that,
except from constτ, the fields remain unchanged.›

definition from_det_to_ndet ::
  (, 'a, 'r, ) Ad_scheme  (, 'a, 'r, ) And_scheme
  where from_det_to_ndet A 
         τ = λσ a. case τ A σ a of σ'  {σ'} |   {},
          ω = λσ. case ω A σ of r  {r} |   {},  = more A
definition from_ndet_to_det ::
  (, 'a, 'r, ) And_scheme  (, 'a, 'r, ) Ad_scheme
  where from_ndet_to_det A 
         τ = λσ a. if τ A σ a = {} then  else THE σ'. σ'  τ A σ a,
          ω = λσ. if ω A σ = {} then  else THE r. r  ω A σ,  = more A

definition from_σ_to_σsd ::
  (, 'a, 'r, ) Ad_scheme  ( list, 'a, 'r, ) Ad_scheme
  where from_σ_to_σsd A 
         τ = λσs a. case τ A (hd σs) a of σ'  [σ'] |   ,
          ω = λσs. ω A (hd σs),  = more A
definition from_σ_to_σsnd ::
  (, 'a, 'r, ) And_scheme  ( list, 'a, 'r, ) And_scheme
  where from_σ_to_σsnd A 
         τ = λσs a. {[σ'] |σ'. σ'  τ A (hd σs) a},
          ω = λσs. ω A (hd σs),  = more A

definition from_σs_to_σd ::
  ( list, 'a, 'r, ) Ad_scheme  (, 'a, 'r, ) Ad_scheme
  where from_σs_to_σd A 
         τ = λσ a. case τ A [σ] a of σs'  hd σs' |   ,
          ω = λσ. ω A [σ],  = more A
definition from_σs_to_σnd ::
  ( list, 'a, 'r, ) And_scheme  (, 'a, 'r, ) And_scheme
  where from_σs_to_σnd A 
         τ = λσ a. {hd σs' |σs'. σs'  τ A [σ] a},
          ω = λσ. ω A [σ],  = more A

definition from_singl_to_listd ::
  (, 'a, 'r, ) Ad_scheme  ( list, 'a, 'r list, ) Ad_scheme
  where from_singl_to_listd A 
         τ = λσs a. case τ A (hd σs) a of σ'  [σ'] |   ,
          ω = λσs. case ω A (hd σs) of r  [r] |   ,  = more A
definition from_singl_to_listnd ::
  (, 'a, 'r, ) And_scheme  ( list, 'a, 'r list, ) And_scheme
  where from_singl_to_listnd A 
         τ = λσs a. {[σ'] |σ'. σ'  τ A (hd σs) a},
          ω = λσs. {[r] |r. r  ω A (hd σs)},  = more A

definition from_list_to_singld ::
  ( list, 'a, 'r list, ) Ad_scheme  (, 'a, 'r, ) Ad_scheme
  where from_list_to_singld A 
         τ = λσ a. case τ A [σ] a of σs'  hd σs' |   ,
          ω = λσ. case ω A [σ] of rs  hd rs |   ,  = more A
definition from_list_to_singlnd ::
  ( list, 'a, 'r list, ) And_scheme  (, 'a, 'r, ) And_scheme
  where from_list_to_singlnd A 
         τ = λσ a. {hd σs' |σs'. σs'  τ A [σ] a},
          ω = λσ. {hd rs |rs. rs  ω A [σ]},  = more A

lemmas det_ndet_conv_defs  = from_det_to_ndet_def from_ndet_to_det_def
  and      σ_σs_conv_defs  = from_σ_to_σsd_def from_σ_to_σsnd_def
  from_σs_to_σd_def from_σs_to_σnd_def
  and singl_list_conv_defs = from_singl_to_listd_def from_singl_to_listnd_def
  from_list_to_singld_def from_list_to_singlnd_def


bundle functional_automata_morphisms_syntax begin

notation from_det_to_ndet (_dnd [0])
notation from_ndet_to_det (_ndd [0])
notation from_σ_to_σsd (d_σσs [0])
notation from_σ_to_σsnd (nd_σσs [0])
notation from_σs_to_σd (d_σsσ [0])
notation from_σs_to_σnd (nd_σsσ [0])
notation from_singl_to_listd (d_singllist [0])
notation from_singl_to_listnd (nd_singllist [0])
notation from_list_to_singld (d_listsingl [0])
notation from_list_to_singlnd (nd_listsingl [0])

end


unbundle functional_automata_morphisms_syntax


lemma morphisms_A_scheme_more_simps [simp] :
  more Adnd = more A more Bndd = more B
  more dCσσs = more C more ndDσσs = more D
  more dEσsσ = more E more ndFσsσ = more F
  more dGsingllist = more G more ndHsingllist = more H
  more dIlistsingl = more I more ndJlistsingl = more J
  by (simp_all add: det_ndet_conv_defs σ_σs_conv_defs singl_list_conv_defs)


subsection ‹Generic update Functions›

definition update_both  where update_both  A0 A1 σ0 σ1 e f  f (τ A0 σ0 e) (τ A1 σ1 e)

definition update_left  where update_left  A0 σ0 σ1 e f g   f (τ A0 σ0 e) (g σ1)

definition update_right where update_right A1 σ0 σ1 e f g   f (g σ0) (τ A1 σ1 e)

lemmas update_defs[simp] = update_both_def update_left_def update_right_def

abbreviation f_up_set where f_up_set f B C  {f s t| s t. (s, t)  B × C}

abbreviation f_up_opt where f_up_opt f s t  case s of    | s'  map_option (f s') t





subsection ‹Assumptions on Automata›

(*required hypothesis when we will need continuity*)
definition finite_trans :: (, 'a, 'r, ) And_scheme  bool
  where finite_trans A  σ a. finite (τ A σ a)

lemma finite_trans_morphisms_simps[simp]: 
  finite_trans Adnd
  finite_trans B  finite_trans ndBσσs
  finite_trans C  finite_trans ndCσsσ
  finite_trans D  finite_trans ndDsingllist
  finite_trans E  finite_trans ndElistsingl                       
  unfolding det_ndet_conv_defs σ_σs_conv_defs singl_list_conv_defs finite_trans_def
  by (simp_all add: option.case_eq_if)


definition at_most_1_elem :: (, 'a, 'r, ) And_scheme  bool
  where at_most_1_elem A 
         (σ a. τ A σ a = {}  (σ'. τ A σ a = {σ'})) 
         (σ. ω A σ = {}  (r. ω A σ = {r}))

lemma at_most_1_elem_def_bis :
  at_most_1_elem A  (σ a. σ'. τ A σ a  {σ'})  (σ. r. ω A σ  {r})
  by (auto simp add: at_most_1_elem_def subset_iff)
    (((metis empty_iff singleton_iff)+)[2],
      ((metis equals0D is_singletonI' is_singleton_some_elem)+)[2])

lemma at_most_1_elemI :
  σ a. τ A σ a = {}  (σ'. τ A σ a = {σ'});
    σ. ω A σ = {}  (r. ω A σ = {r})  at_most_1_elem A
  by (simp add: at_most_1_elem_def)

lemma at_most_1_elemE :
  τ A σ a = {}  thesis; σ'. τ A σ a = {σ'}  thesis  thesis
  ω A σ = {}  thesis; r. ω A σ = {r}  thesis  thesis
  if at_most_1_elem A
  by (meson at_most_1_elem_def at_most_1_elem A)+



definition at_most_1_elem_trans :: (, 'a, 'r, ) And_scheme  bool
  where at_most_1_elem_trans A  σ a. τ A σ a = {}  (σ'. τ A σ a = {σ'})

lemma at_most_1_elem_trans_def_bis :
  at_most_1_elem_trans A  (σ a. σ'. τ A σ a  {σ'})
  by (auto simp add: at_most_1_elem_trans_def subset_iff)
    (metis empty_iff singleton_iff,
      metis equals0D is_singletonI' is_singleton_some_elem)

lemma at_most_1_elem_transI :
  σ a. τ A σ a = {}  (σ'. τ A σ a = {σ'})  at_most_1_elem_trans A
  by (simp add: at_most_1_elem_trans_def)

lemma at_most_1_elem_transE :
  τ A σ a = {}  thesis; σ'. τ A σ a = {σ'}  thesis  thesis
  if at_most_1_elem_trans A
  by (meson at_most_1_elem_trans_def at_most_1_elem_trans A)+

lemma at_most_1_elem_imp_at_most_1_elem_trans :
  at_most_1_elem A  at_most_1_elem_trans A
  by (simp add: at_most_1_elem_def at_most_1_elem_trans_def)



definition length_1_transd :: ( list, 'a, 'r, ) Ad_scheme  bool
  where length_1_transd A 
         σs a. case τ A σs a of   True | σs'  length σs' = Suc 0

lemma length_1_transdI :
  σs a σs'. τ A σs a = σs'  length σs' = Suc 0  length_1_transd A
  by (simp add: length_1_transd_def split: option.split)

lemma length_1_transdE :
  length_1_transd A; τ A σs a = σs'; σ. σs' = [σ]  thesis  thesis
  by (simp add: length_1_transd_def split: option.split_asm)
    (metis (no_types) length_0_conv length_Suc_conv)


definition length_1_transnd :: ( list, 'a, 'r, ) And_scheme  bool
  where length_1_transnd A  σs a. σs'  τ A σs a. length σs' = Suc 0

lemma length_1_transndI :
  σs a σs'. σs'  τ A σs a  length σs' = Suc 0  length_1_transnd A
  by (simp add: length_1_transnd_def split: option.split)

lemma length_1_transndE :
  length_1_transnd A; σs'  τ A σs a; σ. σs' = [σ]  thesis  thesis
  by (simp add: length_1_transnd_def split: option.split_asm)
    (metis (no_types) length_0_conv length_Suc_conv)


definition length_1d :: ( list, 'a, 'r list, ) Ad_scheme  bool
  where length_1d A 
         (σs a. case τ A σs a of   True | σs'  length σs' = Suc 0) 
         (σs. case ω A σs of   True | rs  length rs = Suc 0)

lemma length_1dI :
  σs a σs'. τ A σs a = σs'  length σs' = Suc 0;
    σs rs. ω A σs = rs  length rs = Suc 0  length_1d A
  by (simp add: length_1d_def split: option.split)

lemma length_1dE :
  length_1d A; τ A σs a = σs'; σ. σs' = [σ]  thesis  thesis
  length_1d A; ω A σs = rs; r. rs = [r]  thesis  thesis
  by (simp add: length_1d_def split: option.split_asm,
      metis (no_types) length_0_conv length_Suc_conv)+


definition length_1nd :: ( list, 'a, 'r list, ) And_scheme  bool
  where length_1nd A  (σs a. σs'  τ A σs a. length σs' = Suc 0) 
                        (σs. rs  ω A σs. length rs = Suc 0)

lemma length_1ndI :
  σs a σs'. σs'  τ A σs a  length σs' = Suc 0;
    σs rs. rs  ω A σs  length rs = Suc 0  length_1nd A
  by (simp add: length_1nd_def split: option.split)

lemma length_1ndE :
  length_1nd A; σs'  τ A σs a; σ. σs' = [σ]  thesis  thesis
  length_1nd A; rs  ω A σs; r. rs = [r]  thesis  thesis
  by (simp add: length_1nd_def split: option.split_asm,
      metis (no_types) length_0_conv length_Suc_conv)+

(* abbreviation card_trans_le1 :: ‹('σ, 'a, 'r, 'α) And_scheme ⇒ bool›
  where ‹card_trans_le1 A ≡ ∀s e. card (τ A s e) ≤ Suc 0›

lemma finite_trans_conj_card_trans_le1_is:
  ‹finite_trans A ∧ card_trans_le1 A ⟷ (∀s e.  τ A s e = {} ∨ (∃!t. τ A s e = {t}))›
  by (auto, metis card_1_singleton_iff card_mono empty_subsetI insert_subset le_antisym,
      metis finite.simps, metis card_1_singleton_iff card_eq_0_iff le_eq_less_or_eq less_Suc_eq_le)

lemma finite_trans_imp_card_trans_le1_is:
  ‹finite_trans A ⟹ card_trans_le1 A ⟷ (∀s e.  τ A s e = {} ∨ (∃!t. τ A s e = {t}))›
  by (simp add: finite_trans_conj_card_trans_le1_is[symmetric]) *)


(*when this hypothesis is not verified, the product of two deterministic automata can become non deterministic*)
definition indep_enabl :: (0, 'a, 'r0, ) Ad_scheme  0  'a set  (1, 'a, 'r1, ) Ad_scheme  1  bool
  where indep_enabl A0 σ0 E A1 σ1  t0  d A0 σ0. t1  d A1 σ1. ε A0 t0  ε A1 t1  E

lemma indep_enablI :
  (t0 t1. t0  d A0 σ0  t1  d A1 σ1  ε A0 t0  ε A1 t1  E)
    indep_enabl A0 σ0 E A1 σ1
  and indep_enablD :
  indep_enabl A0 σ0 E A1 σ1; t0  d A0 σ0; t1  d A1 σ1  ε A0 t0  ε A1 t1  E
  by (simp_all add: indep_enabl_def)


definition ρ_disjoint_ε :: (, 'a, 'σ', 'r', ) A_scheme  bool
  where ρ_disjoint_ε A  σ  ρ A. ε A σ = {}

lemma ρ_disjoint_εI : (σ. σ  ρ A  ε A σ = {})  ρ_disjoint_ε A
  and ρ_disjoint_εD : ρ_disjoint_ε A  σ  ρ A  ε A σ = {}
  by (simp_all add: ρ_disjoint_ε_def)



definition at_most_1_elem_term :: (, 'a, 'r, ) And_scheme  bool
  where at_most_1_elem_term A  σ. ω A σ = {}  (r. ω A σ = {r})

lemma at_most_1_elem_term_def_bis :
  at_most_1_elem_term A  (σ. r. ω A σ  {r})
  by (auto simp add: at_most_1_elem_term_def subset_iff)
    (metis empty_iff singleton_iff,
      metis equals0D is_singletonI' is_singleton_some_elem)

lemma at_most_1_elem_termI :
  σ. ω A σ = {}  (r. ω A σ = {r})  at_most_1_elem_term A
  by (simp add: at_most_1_elem_term_def)

lemma at_most_1_elem_termE :
  ω A σ = {}  thesis; r. ω A σ = {r}  thesis  thesis
  if at_most_1_elem_term A
  by (meson at_most_1_elem_term_def at_most_1_elem_term A)+

lemma at_most_1_elem_imp_at_most_1_elem_term :
  at_most_1_elem A  at_most_1_elem_term A
  by (simp add: at_most_1_elem_def at_most_1_elem_term_def)




section ‹First Properties›

subsection constε, constρ and constω first equalities›

lemma base_trans_ε[simp]:
  ε (τ = λσ a. , ω = λσ. ,  = some :: (, 'a, 'r, ) Ad_scheme) σ = {}
  ε (τ = λσ a. {}, ω = λσ. {},  = some :: (, 'a, 'r, ) And_scheme) σ = {}
  by (simp_all add: ε_simps)

lemma base_trans_ρ[simp]:
  ρ (τ = λσ a. , ω = λσ. ,  = some :: (, 'a, 'r, ) Ad_scheme) = {}
  ρ (τ = λσ a. {}, ω = λσ. {},  = some :: (, 'a, 'r, ) And_scheme) = {}
  by (simp_all add: ρ_simps)


lemma σ_σs_conv_ε[simp]:
  ε dAσσs σs = ε A (hd σs) ε ndBσσs σs = ε B (hd σs)
  ε dCσsσ σ = ε C [σ] ε ndDσsσ σ = ε D [σ]
  by (simp_all add: σ_σs_conv_defs ε_simps option.case_eq_if)

lemma σ_σs_conv_ρ[simp]:
  ρ dAσσs = {σs. hd σs  ρ A} ρ ndBσσs = {σs. hd σs  ρ B}
  ρ dCσsσ = {σ. [σ]  ρ C} ρ ndDσsσ = {σ. [σ]  ρ D}
  by (simp_all add: σ_σs_conv_defs ρ_simps option.case_eq_if)


lemma singl_list_conv_ε[simp]:
  ε dAsingllist σs = ε A (hd σs) ε ndBsingllist σs = ε B (hd σs)
  ε dClistsingl σ = ε C [σ] ε ndDlistsingl σ = ε D [σ]
  by (simp_all add: singl_list_conv_defs ε_simps option.case_eq_if)

lemma singl_list_conv_ρ[simp]:
  ρ dAsingllist = {σs. hd σs  ρ A} ρ ndBsingllist = {σs. hd σs  ρ B}
  ρ dClistsingl = {σ. [σ]  ρ C} ρ ndDlistsingl = {σ. [σ]  ρ D}
  by (simp_all add: singl_list_conv_defs ρ_simps option.case_eq_if)


lemma det_ndet_conv_ε[simp]: ε Adnd = ε A ε Bndd = ε B
  by (rule ext, simp add: det_ndet_conv_defs ε_simps option.case_eq_if)+

lemma det_ndet_conv_ρ[simp]: ρ Adnd = ρ A ρ Bndd = ρ B
  by (simp_all add: det_ndet_conv_defs ρ_simps option.case_eq_if)

lemma ω_from_det_to_ndet :
  ω Adnd = (λσ. case ω A σ of r  {r} |   {})
  by (auto simp add: det_ndet_conv_defs)


lemma ε_ω_useless [simp] :
  ε (Aω := some_ω) = ε A ε (Bω := some_ω') = ε B
  for A :: (, 'a,  option, 'r option, ) A_scheme
    and B :: (, 'a,  set, 'r set, ) A_scheme
  by (rule ext, simp add: ε_simps)+

lemma ρ_disjoint_ε_updated_ω [simp] :
  ρ_disjoint_ε (Aω := λσ. )
  ρ_disjoint_ε (Bω := λσ. {})
  by (simp_all add: ρ_disjoint_ε_def ρ_simps)

lemma ρ_disjoint_ε_det_ndet_conv_iff [simp] :
  ρ_disjoint_ε Adnd  ρ_disjoint_ε A
  ρ_disjoint_ε Bndd  ρ_disjoint_ε B
  by (simp_all add: ρ_disjoint_ε_def)

lemma at_most_1_elem_term_updated_ω [simp] :
  at_most_1_elem_term (Aω := λσ. {})
  by (simp add: at_most_1_elem_term_def)

lemma at_most_1_elem_term_from_det_to_ndet [simp] :
  at_most_1_elem_term Adnd
  by (simp add: det_ndet_conv_defs at_most_1_elem_term_def split: option.split)

lemma at_most_1_elem_term_unit [simp] :
  at_most_1_elem_term (A :: (, 'a, unit, ) And_scheme)
  by (auto simp add: at_most_1_elem_term_def)



subsection ‹Properties of our morphisms›

method expand_A_scheme =
  match conclusion in A = B for A B :: (, 'a, 'σ', 'r', ) A_scheme 
  cases A, cases B


lemma base_trans_det_ndet_conv:
  τ = λσ a. , ω = λσ. ,  = somednd =
    τ = λσ a. {}, ω = λσ. {},  = some
  τ = λσ a. {}, ω = λσ. {},  = somendd =
   τ = λσ a. , ω = λσ. ,  = some
  unfolding det_ndet_conv_defs by simp_all


lemma from_det_to_ndet_σ_σs_conv_commute:
  ndAdndσσs = dAσσsdnd ndBdndσsσ = dBσsσdnd
  by (simp add: det_ndet_conv_defs σ_σs_conv_defs, rule ext,
      auto simp add: option.case_eq_if split: if_splits)+

lemma from_det_to_ndet_singl_list_conv_commute:
  ndAdndsingllist = dAsingllistdnd ndBdndlistsingl = dBlistsingldnd
  by (simp add: det_ndet_conv_defs singl_list_conv_defs,
      solves intro conjI ext, auto split: option.split)+


lemma from_ndet_to_det_σ_σs_conv_commute:
  at_most_1_elem_trans A  dAnddσσs = ndAσσsndd
  at_most_1_elem_trans B  dBnddσsσ = ndBσsσndd
proof -
  assume * : at_most_1_elem_trans A
  from "*" have τ dAnddσσs σs a = τ ndAσσsndd σs a for σs a
    by (auto simp add: det_ndet_conv_defs σ_σs_conv_defs
        elim: at_most_1_elem_transE)
  moreover have ω dAnddσσs σs = ω ndAσσsndd σs for σs
    by (auto simp add: det_ndet_conv_defs σ_σs_conv_defs)
  moreover have more dAnddσσs = more ndAσσsndd by simp
  ultimately show dAnddσσs = ndAσσsndd by expand_A_scheme auto
next
  assume * : at_most_1_elem_trans B
  from "*" have τ dBnddσsσ σ a = τ ndBσsσndd σ a for σ a
    by (auto simp add: det_ndet_conv_defs σ_σs_conv_defs
        elim: at_most_1_elem_transE)
  moreover have ω dBnddσsσ σ = ω ndBσsσndd σ for σ
    by (auto simp add: det_ndet_conv_defs σ_σs_conv_defs)
  moreover have more dBnddσsσ = more ndBσsσndd by simp
  ultimately show dBnddσsσ = ndBσsσndd by expand_A_scheme auto
qed

lemma from_ndet_to_det_singl_list_conv_commute:
  at_most_1_elem A  dAnddsingllist = ndAsingllistndd
  at_most_1_elem B  dBnddlistsingl = ndBlistsinglndd
proof -
  assume * : at_most_1_elem A
  from "*" have τ dAnddsingllist σs a = τ ndAsingllistndd σs a for σs a
    by (auto simp add: det_ndet_conv_defs singl_list_conv_defs
        elim: at_most_1_elemE(1))
  moreover from "*" have ω dAnddsingllist σs = ω ndAsingllistndd σs for σs
    by (auto simp add: det_ndet_conv_defs singl_list_conv_defs
        elim: at_most_1_elemE(2))
  moreover have more dAnddsingllist = more ndAsingllistndd by simp
  ultimately show dAnddsingllist = ndAsingllistndd by expand_A_scheme auto
next
  assume * : at_most_1_elem B
  from "*" have τ dBnddlistsingl σ a = τ ndBlistsinglndd σ a for σ a
    by (auto simp add: det_ndet_conv_defs singl_list_conv_defs
        elim: at_most_1_elemE(1))
  moreover from "*" have ω dBnddlistsingl σ = ω ndBlistsinglndd σ for σ
    by (auto simp add: det_ndet_conv_defs singl_list_conv_defs
        elim: at_most_1_elemE(2))
  moreover have more dBnddlistsingl = more ndBlistsinglndd by simp
  ultimately show dBnddlistsingl = ndBlistsinglndd by expand_A_scheme auto
qed



lemma behaviour_σ_σs_conv:
  ε dAσσs [σ] = ε A σ
  τ dAσσs [σ] a = (case τ A σ a of    | t  [t])
  ρ dAσσs = {σs. hd σs  ρ A}
  ω dAσσs [σ] = ω A σ
  ε ndBσσs [σ] = ε B σ
  τ ndBσσs [σ] a = {[σ'] |σ'. σ'  τ B σ a}
  ρ ndBσσs = {σs. hd σs  ρ B}
  ω ndBσσs [σ] = ω B σ
  ε dCσsσ σ = ε C [σ]
  τ dCσsσ σ a = (case τ C [σ] a of    | σs'  hd σs')
  ρ dCσsσ = {σ. [σ]  ρ C}
  ω dCσsσ σ = ω C [σ]
  ε ndDσsσ σ = ε D [σ]
  τ ndDσsσ σ a = {hd σs'| σs'. σs'  τ D [σ] a}
  ρ ndDσsσ = {σ. [σ]  ρ D} ω ndDσsσ σ = ω D [σ]
  by simp_all (simp_all add: σ_σs_conv_defs)


lemma behaviour_singl_list_conv:
  ε dAsingllist [σ] = ε A σ
  τ dAsingllist [σ] a = (case τ A σ a of    | t  [t])
  ρ dAsingllist = {σs. hd σs  ρ A}
  ω dAsingllist [σ] = (case ω A σ of    | r  [r])
  ε ndBsingllist [σ] = ε B σ
  τ ndBsingllist [σ] a = {[σ'] |σ'. σ'  τ B σ a}
  ρ ndBsingllist = {σs. hd σs  ρ B}
  ω ndBsingllist [σ] = {[r] |r. r  ω B σ}
  ε dClistsingl σ = ε C [σ]
  τ dClistsingl σ a = (case τ C [σ] a of    | σs'  hd σs')
  ρ dClistsingl = {σ. [σ]  ρ C}
  ω dClistsingl σ = (case ω C [σ] of    | rs  hd rs)
  ε ndDlistsingl σ = ε D [σ]
  τ ndDlistsingl σ a = {hd σs'| σs'. σs'  τ D [σ] a}
  ρ ndDlistsingl = {σ. [σ]  ρ D}
  ω ndDlistsingl σ = {hd rs |rs. rs  ω D [σ]}
  by simp_all (simp_all add: singl_list_conv_defs)


lemma empty_from_det_to_ndet_is_None_trans [simp] : τ Adnd σ a = {}  τ A σ a = 
  by (simp add: ε_simps det_ndet_conv_defs option.case_eq_if)


lemma at_most_1_elem_from_det_to_ndet [simp] : at_most_1_elem Adnd
  by (rule at_most_1_elemI)
    (simp_all add: det_ndet_conv_defs split: option.split)


lemma from_ndet_to_det_from_det_to_ndet [simp] : Adndndd = A
  by (cases A, simp add: det_ndet_conv_defs)
    (intro conjI ext, simp_all split: option.split)

lemma from_det_to_ndet_from_ndet_to_det [simp] :
  Andddnd = A if at_most_1_elem A
proof -
  from that have τ Andddnd σ a = τ A σ a for σ a
    by (auto simp add: det_ndet_conv_defs elim: at_most_1_elemE(1))
  moreover from that have ω Andddnd σ = ω A σ for σ
    by (auto simp add: det_ndet_conv_defs elim: at_most_1_elemE(2))
  moreover have more Andddnd = more A by simp
  ultimately show Andddnd = A by expand_A_scheme fastforce
qed


theorem bij_betw_from_det_to_ndet :
  bij_betw (λA. Adnd) UNIV {A. at_most_1_elem A}
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. Andd]) simp

lemma bij_betw_from_ndet_to_det :
  bij_betw (λA. Andd) {A. at_most_1_elem A} UNIV
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. Adnd]) simp


lemma length_1_trans_from_σ_to_σs [simp] :
  length_1_transd dAσσs length_1_transnd ndBσσs
  by (rule length_1_transdI, solves auto simp add: σ_σs_conv_defs split: option.split_asm)
    (rule length_1_transndI, solves auto simp add: σ_σs_conv_defs split: option.split_asm)

lemma τ_hd_from_σ_to_σs_eq [simp] :
  τ dAσσs [hd σs] a = τ dAσσs σs a
  τ ndBσσs [hd σs] a = τ ndBσσs σs a
  by (simp_all add: σ_σs_conv_defs)

lemma ω_hd_from_σ_to_σs_eq [simp] :
  ω dAσσs [hd σs] = ω dAσσs σs
  ω ndBσσs [hd σs] = ω ndBσσs σs
  by (simp_all add: σ_σs_conv_defs)


lemma from_σs_to_σ_from_σ_to_σs [simp] :
  ddAσσsσsσ = A ndndBσσsσsσ = B
  by (cases A, simp add: σ_σs_conv_defs, intro conjI ext;
      simp add: option.case_eq_if set_eq_iff; metis list.sel(1))
    (cases B, simp add: σ_σs_conv_defs, intro conjI ext;
      simp add: option.case_eq_if set_eq_iff; metis list.sel(1))

lemma from_σ_to_σs_from_σs_to_σ [simp] :
  length_1_transd A; σs a. τ A [hd σs] a = τ A σs a;
    σs. ω A [hd σs] = ω A σs  ddAσsσσσs = A
  length_1_transnd B; σs a. τ B [hd σs] a = τ B σs a;
    σs. ω B [hd σs] = ω B σs  ndndBσsσσσs = B
proof -
  assume * : length_1_transd A σs a. τ A [hd σs] a = τ A σs a
    σs. ω A [hd σs] = ω A σs
  from "*"(1) have τ ddAσsσσσs σs a = τ A σs a for σs a
    by (auto simp add: σ_σs_conv_defs "*"(2) split: option.split
        elim: length_1_transdE)
  moreover have ω ddAσsσσσs σs = ω A σs for σs
    by (simp add: σ_σs_conv_defs "*"(3))
  moreover have more ddAσsσσσs = more A by simp
  ultimately show ddAσsσσσs = A by expand_A_scheme auto
next
  assume * : length_1_transnd B σs a. τ B [hd σs] a = τ B σs a
    σs. ω B [hd σs] = ω B σs
  from "*"(1) have τ ndndBσsσσσs σs a = τ B σs a for σs a
    by (auto simp add: σ_σs_conv_defs "*"(2) elim: length_1_transndE)
      (metis length_1_transndE list.sel(1))
  moreover have ω ndndBσsσσσs σs = ω B σs for σs
    by (simp add: σ_σs_conv_defs "*"(3))
  moreover have more ndndBσsσσσs = more B by simp
  ultimately show ndndBσsσσσs = B by expand_A_scheme fastforce
qed

theorem bij_betw_from_σ_to_σs : 
  bij_betw (λA. dAσσs) UNIV
   {A. length_1_transd A  (σs a. τ A [hd σs] a = τ A σs a)  (σs. ω A [hd σs] = ω A σs)}
  (is bij_betw (λA. dAσσs) UNIV ?Sd)
  bij_betw (λB. ndBσσs) UNIV
   {B. length_1_transnd B  (σs a. τ B σs a = τ B [hd σs] a)  (σs. ω B [hd σs] = ω B σs)}
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. dAσsσ], simp)
    (rule exI[where x = λA. ndAσsσ], simp)


lemma bij_betw_from_σs_to_σ : 
  bij_betw (λA. dAσsσ)
   {A. length_1_transd A  (σs a. τ A [hd σs] a = τ A σs a)  (σs. ω A [hd σs] = ω A σs)} UNIV
  bij_betw (λB. ndBσsσ)
   {B. length_1_transnd B  (σs a. τ B σs a = τ B [hd σs] a)  (σs. ω B [hd σs] = ω B σs)} UNIV
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. dAσσs], simp)
    (rule exI[where x = λA. ndAσσs], simp)



lemma length_1_from_singl_to_list [simp] :
  length_1d dAsingllist length_1nd ndBsingllist
  by (rule length_1dI; solves auto simp add: singl_list_conv_defs split: option.split_asm)
    (rule length_1ndI; solves auto simp add: singl_list_conv_defs split: option.split_asm)

lemma τ_hd_from_singl_to_list_eq [simp] :
  τ dAsingllist [hd σs] a = τ dAsingllist σs a
  τ ndBsingllist [hd σs] a = τ ndBsingllist σs a
  by (simp_all add: singl_list_conv_defs)

lemma ω_hd_from_singl_to_list_eq [simp] :
  ω dAsingllist [hd σs] = ω dAsingllist σs
  ω ndBsingllist [hd σs] = ω ndBsingllist σs
  by (simp_all add: singl_list_conv_defs)


lemma from_list_to_singl_from_singl_to_list [simp] :
  ddAsingllistlistsingl = A ndndBsingllistlistsingl = B
  by (cases A, simp add: singl_list_conv_defs, intro conjI ext;
      simp add: option.case_eq_if set_eq_iff; metis list.sel(1))
    (cases B, simp add: singl_list_conv_defs, intro conjI ext;
      simp add: option.case_eq_if set_eq_iff; metis list.sel(1))

lemma from_singl_to_list_from_list_to_singl [simp] :
  length_1d A; σs a. τ A [hd σs] a = τ A σs a;
    σs. ω A [hd σs] = ω A σs  ddAlistsinglsingllist = A
  length_1nd B; σs a. τ B [hd σs] a = τ B σs a;
    σs. ω B [hd σs] = ω B σs  ndndBlistsinglsingllist = B
proof -
  assume * : length_1d A σs a. τ A [hd σs] a = τ A σs a
    σs. ω A [hd σs] = ω A σs
  from "*"(1) have τ ddAlistsinglsingllist σs a = τ A σs a for σs a
    by (auto simp add: singl_list_conv_defs "*"(2)
        split: option.split elim: length_1dE(1))
  moreover from "*"(1) have ω ddAlistsinglsingllist σs = ω A σs for σs
    by (auto simp add: singl_list_conv_defs "*"(3)
        split: option.split elim: length_1dE(2))
  moreover have more ddAlistsinglsingllist = more A by simp
  ultimately show ddAlistsinglsingllist = A by expand_A_scheme auto
next
  assume * : length_1nd B σs a. τ B [hd σs] a = τ B σs a
    σs. ω B [hd σs] = ω B σs
  from "*"(1) have τ ndndBlistsinglsingllist σs a = τ B σs a for σs a
    by (auto simp add: singl_list_conv_defs "*"(2) elim: length_1ndE(1))
      (metis length_1ndE(1) list.sel(1))
  moreover from "*"(1) have ω ndndBlistsinglsingllist σs = ω B σs for σs
    by (auto simp add: singl_list_conv_defs "*"(3) elim: length_1ndE(2))
      (metis length_1ndE(2) list.sel(1))
  moreover have more ndndBlistsinglsingllist = more B by simp
  ultimately show ndndBlistsinglsingllist = B by expand_A_scheme fastforce
qed


theorem bij_betw_from_singl_to_list : 
  bij_betw (λA. dAsingllist) UNIV
   {A. length_1d A  (σs a. τ A [hd σs] a = τ A σs a)  (σs. ω A [hd σs] = ω A σs)}
  (is bij_betw (λA. dAsingllist) UNIV ?Sd)
  bij_betw (λB. ndBsingllist) UNIV
   {B. length_1nd B  (σs a. τ B σs a = τ B [hd σs] a)  (σs. ω B [hd σs] = ω B σs)}
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. dAlistsingl], simp)
    (rule exI[where x = λA. ndAlistsingl], simp)

lemma bij_betw_from_list_to_singl : 
  bij_betw (λA. dAlistsingl)
   {A. length_1d A  (σs a. τ A [hd σs] a = τ A σs a)  (σs. ω A [hd σs] = ω A σs)} UNIV
  bij_betw (λB. ndBlistsingl)
   {B. length_1nd B  (σs a. τ B σs a = τ B [hd σs] a)  (σs. ω B [hd σs] = ω B σs)} UNIV
  unfolding bij_betw_iff_bijections
  by (rule exI[where x = λA. dAsingllist], simp)
    (rule exI[where x = λA. ndAsingllist], simp)



subsection ‹Reachability results (for constd and constnd)›

lemma ℛ_base_trans[simp]: d  τ = λσ a. , ω = λσ. ,  = some = (λσ. {σ})
  nd τ = λσ a. {}, ω = λσ. {},  = some = (λσ. {σ})
  by (rule ext, safe, subst (asm) d.simps nd.simps, simp_all add: d.init nd.init)+


theorem nd_from_det_to_ndet : nd Adnd σ = d A σ
proof safe
  show σ'  nd Adnd σ  σ'  d A σ for σ'
    by (induct rule: nd.induct, fact d.init, erule d.step)
      (simp add: from_det_to_ndet_def option.case_eq_if split: if_split_asm)
next
  show σ'  d A σ  σ'  nd Adnd σ for σ'
    by (induct rule: d.induct, fact nd.init)
      (metis nd.step det_ndet_conv_defs(1) option.case(2) 
        option.set_intros option.simps(15) select_convs(1))
qed


(*TODO: see where this can be useful*)
lemma bij_betw_ℛnd_if_same_τ : bij_betw f (nd B0 σ0) (nd B1 (f σ0))
  if inj_on f (nd B0 σ0) and σ0' a. σ0'  nd B0 σ0  τ B1 (f σ0') a = f ` τ B0 σ0' a
proof (rule bij_betw_imageI, fact that(1), auto simp add: image_def, goal_cases)
  show s  nd B0 σ0  f s  nd B1 (f σ0) for s
    by (induct rule: nd.induct, simp add: nd.init, metis nd.step that(2) image_eqI)
next
  show s  nd B1 (f σ0)  t  nd B0 σ0. s = f t for s
    by (induct rule: nd.induct, metis nd.simps, metis (mono_tags, lifting) nd.step that(2) image_iff)
qed

lemma bij_betw_ℛd_if_same_τ: bij_betw f (d A0 σ0) (d A1 (f σ0))
  if inj_on f (d A0 σ0) and σ0' a. σ0'  d A0 σ0  τ A1 (f σ0') a = map_option f (τ A0 σ0' a)
  by (subst (1 2) nd_from_det_to_ndet[symmetric], rule bij_betw_ℛnd_if_same_τ)
    (simp_all add: nd_from_det_to_ndet that(1),
      simp add: det_ndet_conv_defs that(2) option.case_eq_if map_option_case)

lemmas same_τ_implies_same_ℛnd = bij_betw_ℛnd_if_same_τ[where f = id, simplified bij_betw_def, simplified]
  and same_τ_implies_same_ℛd = bij_betw_ℛd_if_same_τ[where f = id, simplified bij_betw_def option.map_id, simplified]

corollary d_ω_useless [simp] : d (Aω := some_ω) σ = d A σ
  by (auto intro!: same_τ_implies_same_ℛd)

corollary nd_ω_useless [simp] : nd (Aω := some_ω) σ = nd A σ
  by (auto intro!: same_τ_implies_same_ℛnd)

corollary indep_enabl_ω_useless [simp] :
  indep_enabl (A0ω := some_ω) σ0 E A1 σ1  indep_enabl A0 σ0 E A1 σ1
  indep_enabl A0 σ0 E (A1ω := some_ω) σ1  indep_enabl A0 σ0 E A1 σ1
  by (simp_all add: indep_enabl_def)


method ℛ_subset_method uses defs opt induct init simps = 
  induct rule: induct, auto simp add: init defs ε_simps split: if_splits,
  (metis (no_types, opaque_lifting) simps)+

method d_subset_method uses defs opt =
  ℛ_subset_method defs: defs opt: opt induct: d.induct init: d.init simps: d.simps

method nd_subset_method uses defs opt =
  ℛ_subset_method defs: defs opt: opt induct: nd.induct init: nd.init simps: nd.simps


lemma nd_from_σ_to_σs_description: nd ndBσσs [σ] = {[σ']| σ'. σ'  nd B σ}
proof safe
  show σs  nd ndBσσs [σ]  σ'. σs = [σ']  σ'  nd B σ for σs
    by (induct rule: nd.induct, auto simp add: nd.init behaviour_σ_σs_conv(6), metis nd.step)
next
  show σ'  nd B σ  [σ']  nd ndBσσs [σ] for σ'
    by (induct rule: nd.induct) (simp_all add: nd.init nd.step behaviour_σ_σs_conv(6))
qed


lemma d_from_σ_to_σs_description: d dAσσs [σ] = {[σ']| σ'. σ'  d A σ}
  by (simp add: nd_from_σ_to_σs_description
      flip: nd_from_det_to_ndet from_det_to_ndet_σ_σs_conv_commute(1))


lemma nd_from_singl_to_list_description: nd ndBsingllist [σ] = {[σ']| σ'. σ'  nd B σ}
proof safe
  show σs  nd ndBsingllist [σ]  σ'. σs = [σ']  σ'  nd B σ for σs
    by (induct rule: nd.induct, auto simp add: nd.init behaviour_singl_list_conv(6), metis nd.step)
next
  show σ'  nd B σ  [σ']  nd ndBsingllist [σ] for σ'
    by (induct rule: nd.induct) (simp_all add: nd.init nd.step behaviour_singl_list_conv(6))
qed


lemma d_from_singl_to_list_description: d dAsingllist [σ] = {[σ']| σ'. σ'  d A σ}
  by (simp add: nd_from_singl_to_list_description
      flip: nd_from_det_to_ndet from_det_to_ndet_singl_list_conv_commute(1))



lemma length_ℛd_from_σ_to_σs:
  σs'  d dAσσs σs  σs' = σs  length σs' = 1  
  by (simp add: σ_σs_conv_defs)
    (induct rule: d.induct, simp_all split: option.split_asm)

lemma length_ℛnd_from_σ_to_σs:
  σs'  nd ndBσσs σs  σs' = σs  length σs' = 1
  by (simp add: σ_σs_conv_defs)
    (induct rule: nd.induct, auto)

lemma length_ℛd_from_singl_to_list:
  σs'  d dAsingllist σs  σs' = σs  length σs' = 1
  by (simp add: singl_list_conv_defs)
    (induct rule: d.induct, simp_all split: option.split_asm)

lemma length_ℛnd_from_singl_to_list:
  σs'  nd ndBsingllist σs  σs' = σs  length σs' = 1
  by (simp add: singl_list_conv_defs)
    (induct rule: nd.induct, auto)



section ‹Normalization› 

subsection ‹Non-deterministic Case›

text ‹First version, without final state notion›

abbreviation P_nd_step :: [(, 'a) enabl, (, 'a) transnd,   ('a, 'r) processptick, ]  ('a, 'r) processptick
  where P_nd_step εA τA X σ   e  εA σ   σ'  τA σ e. X σ'

definition P_nd :: (, 'a, 'r, ) And_scheme    ('a, 'r) processptick (P⟪_nd 1000)
  where P⟪And  υ X. P_nd_step (ε A) (τ A) X


lemma P_nd_step_constructive [simp] : constructive (P_nd_step εA τA) by simp

lemma P_nd_step_cont [simp] : σ a. finite (τA σ a)  cont (P_nd_step εA τA)
  by (simp add: cont_fun)

lemma P_nd_step_constructive_bis : constructive (P_nd_step (ε A) (τ A)) by simp

lemma P_nd_step_cont_bis [simp] : finite_trans A  cont (P_nd_step (ε A) (τ A))
  by (simp add: finite_trans_def)

lemma P_nd_rec: P⟪And = (λσ. P_nd_step (ε A) (τ A) P⟪And σ)
  by (unfold P_nd_def, rule ext, subst restriction_fix_eq, simp_all)

lemma P_nd_is_fix : finite_trans A  P⟪And = (μ X. P_nd_step (ε A) (τ A) X)
  by (simp add: P_nd_def restriction_fix_is_fix)

lemma non_destructive_imp_restriction_cont [simp] :
  non_destructive f  restriction_cont f
  by (simp add: non_destructive_on_def)



lemma P_nd_ω_useless: P⟪And = P⟪Aω := some_ωnd
  by (simp add: P_nd_def ε_simps)

lemma P_nd_ω_useless_bis : P⟪And = P⟪Aω := λσ. {}nd
  by (fact P_nd_ω_useless)

lemma P_nd_induct [case_names adm base step] :
  adm P  P σ  (X. P X  P (P_nd_step (ε A) (τ A) X))  P P⟪And
  unfolding P_nd_def
  by (rule restriction_fix_ind[OF P_nd_step_constructive_bis]) simp_all

lemma P_nd_induct_iterated [consumes 1, case_names adm base step] :
  0 < k; adm P; P σ; X. P X  P ((P_nd_step (ε A) (τ A) ^^ k) X)  P P⟪And
  unfolding P_nd_def
  by (rule restriction_fix_ind_iterated[where f = P_nd_step (ε A) (τ A)]) auto



text ‹New version with final state notion where we just have constSKIPS.›

abbreviation PSKIPS_nd_step ::
  [(, 'a) enabl, (, 'a) transnd,   'r set,   ('a, 'r) processptick, ]  ('a, 'r) processptick
  where PSKIPS_nd_step εA τA ωA X σ  if ωA σ = {} then P_nd_step εA τA X σ else SKIPS (ωA σ)

definition PSKIPS_nd :: (, 'a, 'r, ) And_scheme    ('a, 'r) processptick (PSKIPS_nd 1000)
  where PSKIPSAnd  υ X. PSKIPS_nd_step (ε A) (τ A) (ω A) X



lemma PSKIPS_nd_step_constructive [simp] : constructive (PSKIPS_nd_step εA τA ωA) by auto


lemma PSKIPS_nd_step_cont [simp] : σ a. finite (τA σ a)  cont (PSKIPS_nd_step εA τA ωA)
  by (simp add: cont_fun)

lemma PSKIPS_nd_step_constructive_bis : constructive (PSKIPS_nd_step (ε A) (τ A) (ω A)) by simp

lemma PSKIPS_nd_step_cont_bis [simp] : finite_trans A  cont (PSKIPS_nd_step (ε A) (τ A) (ω A))
  by (simp add: finite_trans_def)

lemma PSKIPS_nd_rec: PSKIPSAnd = (λσ. PSKIPS_nd_step (ε A) (τ A) (ω A) PSKIPSAnd σ)
  by (unfold PSKIPS_nd_def, rule ext, subst restriction_fix_eq, simp_all)

lemma PSKIPS_nd_is_fix : finite_trans A  PSKIPSAnd = (μ X. PSKIPS_nd_step (ε A) (τ A) (ω A) X)
  by (simp add: PSKIPS_nd_def restriction_fix_is_fix)

lemma PSKIPS_nd_induct [case_names adm base step] :
  adm P  P σ  (X. P X  P (PSKIPS_nd_step (ε A) (τ A) (ω A) X))  P PSKIPSAnd
  unfolding PSKIPS_nd_def
  by (rule restriction_fix_ind[OF PSKIPS_nd_step_constructive_bis]) simp_all

lemma PSKIPS_nd_induct_iterated [consumes 1, case_names adm base step] :
  0 < k; adm P; P σ; X. P X  P ((PSKIPS_nd_step (ε A) (τ A) (ω A) ^^ k) X)  P PSKIPSAnd
  unfolding PSKIPS_nd_def
  by (rule restriction_fix_ind_iterated[where f = PSKIPS_nd_step (ε A) (τ A) (ω A)]) auto



text ‹Correspondence when we always have termω A σ = {}.›

lemma PSKIPS_nd_empty_ρ : ρ A = {}  PSKIPSAnd = P⟪And
  by (simp add: PSKIPS_nd_def P_nd_def ρ_simps)

lemma PSKIPS_nd_updated_ω: P⟪And = PSKIPSAω := λσ. {}nd
  by (metis (mono_tags, lifting) PSKIPS_nd_empty_ρ P_nd_ω_useless_bis ρnd.simps
      empty_Collect_eq select_convs(2) surjective update_convs(2))

lemma PSKIPS_nd_empty_ρ_inter_ℛnd:
  PSKIPSAnd σ = P⟪And σ if ρ A  nd A σ = {}
proof -
  have σ'  nd A σ  PSKIPSAnd σ' = P⟪And σ' for σ'
  proof (induct A arbitrary: σ' rule: PSKIPS_nd_induct)
    case adm show ?case by simp
  next
    case base show P⟪And σ' = P⟪And σ' ..
  next
    case (step X)
    from step.prems(1) that have σ'  ρ A by blast
    hence ω A σ' = {} by (simp add: ρ_simps)
    thus ?case
      by (subst P_nd_rec, auto intro!: mono_Mprefix_eq mono_GlobalNdet_eq)
        (metis (lifting) nd.simps step.hyps step.prems)
  qed
  thus PSKIPSAnd σ = P⟪And σ by (simp add: nd.init)
qed



lemma PSKIPS_nd_rec_notin_ρ:
  σ  ρ A  PSKIPSAnd σ = P_nd_step (ε A) (τ A) PSKIPSAnd σ
  by (subst PSKIPS_nd_rec) (simp add: ρ_simps)

lemma PSKIPS_nd_rec_in_ρ: σ  ρ A  PSKIPSAnd σ = SKIPS (ω A σ)
  by (subst PSKIPS_nd_rec, simp add: ρ_simps)



subsection ‹Deterministic Case›

text ‹First version, without final state notion.›

abbreviation P_d_step :: [(, 'a) enabl, (, 'a) transd,   ('a, 'r) processptick, ]  ('a, 'r) processptick
  where P_d_step εA τA X s   e  εA s  X τA s e

definition P_d :: (, 'a, 'r, ) Ad_scheme    ('a, 'r) processptick (P⟪_d 1000)
  where P⟪Ad  υ X. P_d_step (ε A) (τ A) X


lemma P_d_step_constructive[simp] : constructive (P_d_step εA τA) by simp

lemmas P_d_step_constructive_bis = P_d_step_constructive[of ε A τ A] for A

lemma P_d_step_cont[simp]: cont (P_d_step εA τA)
  by (simp add: cont_fun)

lemmas P_d_step_cont_bis = P_d_step_cont[of ε A τ A] for A

lemma P_d_rec: P⟪Ad = (λs. P_d_step (ε A) (τ A) P⟪Ad s)
  by (unfold P_d_def, subst restriction_fix_eq) simp_all

lemma P_d_is_fix : P⟪Ad = (μ X. P_d_step (ε A) (τ A) X)
  by (simp add: P_d_def restriction_fix_is_fix)


lemma P_d_ω_useless: P⟪Ad = P⟪Aω := some_ωd
  by (simp add: P_d_def ε_simps)

lemma P_d_ω_useless_bis: P⟪Ad = P⟪Aω := λσ. d
  by (fact P_d_ω_useless)

lemma P_d_induct [case_names adm base step] :
  adm P; P σ; X. P X  P (P_d_step (ε A) (τ A) X)  P P⟪Ad
  unfolding P_d_def
  by (rule restriction_fix_ind[OF P_d_step_constructive_bis]) simp_all

lemma P_d_induct_iterated [consumes 1, case_names adm base step] :
  0 < k; adm P; P σ; X. P X  P ((P_d_step (ε A) (τ A) ^^ k) X)  P P⟪Ad
  unfolding P_d_def
  by (rule restriction_fix_ind_iterated[where f = P_d_step (ε A) (τ A)]) auto



text ‹New version with final state notion where we just constSKIP.›

abbreviation PSKIPS_d_step ::
  [(, 'a) enabl, (, 'a) transd,   'r option,   ('a, 'r) processptick, ]  ('a, 'r) processptick
  where PSKIPS_d_step εA τA ωA X σ  case ωA σ of r  SKIP r |   P_d_step εA τA X σ

definition PSKIPS_d :: (, 'a, 'r, ) Ad_scheme    ('a, 'r) processptick (PSKIPS_d 1000)
  where PSKIPSAd  υ X. PSKIPS_d_step (ε A) (τ A) (ω A) X

lemma PSKIPS_d_step_constructive[simp]: constructive (PSKIPS_d_step εA τA 𝒮FA)
  by (auto simp add: option.case_eq_if)

lemmas PSKIPS_d_step_constructive_bis = PSKIPS_d_step_constructive[of ε A τ A ω A] for A


lemma PSKIPS_d_step_cont[simp]: cont (PSKIPS_d_step εA τA 𝒮FA)
  by (simp add: cont_fun option.case_eq_if)

lemmas PSKIPS_d_step_cont_bis = PSKIPS_d_step_cont[of ε A τ A ω A] for A

lemma PSKIPS_d_rec: PSKIPSAd = (λσ. PSKIPS_d_step (ε A) (τ A) (ω A) PSKIPSAd σ)
  by (unfold PSKIPS_d_def, subst restriction_fix_eq) auto

lemma PSKIPS_d_is_fix : PSKIPSAd = (μ X. PSKIPS_d_step (ε A) (τ A) (ω A) X)
  by (simp add: PSKIPS_d_def restriction_fix_is_fix)


lemma PSKIPS_d_induct [case_names adm base step] :
  adm P  P σ  (X. P X  P (PSKIPS_d_step (ε A) (τ A) (ω A) X))  P PSKIPSAd
  unfolding PSKIPS_d_def
  by (rule restriction_fix_ind[OF PSKIPS_d_step_constructive_bis]) simp_all

lemma PSKIPS_d_induct_iterated [consumes 1, case_names adm base step] :
  0 < k; adm P; P σ; X. P X  P ((PSKIPS_d_step (ε A) (τ A) (ω A) ^^ k) X)  P PSKIPSAd
  unfolding PSKIPS_d_def
  by (rule restriction_fix_ind_iterated[where f = PSKIPS_d_step (ε A) (τ A) (ω A)]) auto



text ‹Correspondence when we always have termω A σ = {}.›

lemma PSKIPS_d_empty_ρ : ρ A = {}  PSKIPSAd = P⟪Ad
  by (simp add: ρ_simps P_d_def PSKIPS_d_def)

lemma PSKIPS_d_updated_ω: P⟪Ad = PSKIPSAω := λσ. d
  by (simp add: PSKIPS_d_empty_ρ P_d_ω_useless ρ_simps)


lemma PSKIPS_d_empty_ρ_inter_ℛd:
  PSKIPSAd σ = P⟪Ad σ if ρ A  d A σ = {}
proof -
  have σ'  d A σ  PSKIPSAd σ' = P⟪Ad σ' for σ'
  proof (induct A arbitrary: σ' rule: PSKIPS_d_induct)
    case adm show ?case by simp
  next
    case base show P⟪Ad σ' = P⟪Ad σ' ..
  next
    case (step X)
    from step.prems(1) that have σ'  ρ A by blast
    hence ω A σ' =  by (simp add: ρ_simps)
    thus ?case
      by (subst P_d_rec, auto intro!: mono_Mprefix_eq mono_GlobalNdet_eq)
        (subst (asm) ε_simps, auto, metis (lifting) d.step step.hyps step.prems)
  qed
  thus PSKIPSAd σ = P⟪Ad σ by (simp add: d.init)
qed



lemma PSKIPS_d_rec_notin_ρ:
  σ  ρ A  PSKIPSAd σ = P_d_step (ε A) (τ A) PSKIPSAd σ
  by (subst PSKIPS_d_rec) (simp add: ρ_simps)

lemma PSKIPS_d_rec_in_ρ: σ  ρ A  PSKIPSAd σ = SKIP ω A σ
  by (subst PSKIPS_d_rec, simp add: ρ_simps split: option.split)



subsection ‹Link between deterministic and non-deterministic ProcOmata›

lemma PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d : PSKIPSAdndnd = PSKIPSAd
proof (subst PSKIPS_nd_def, rule restriction_fix_unique)
  show constructive (PSKIPS_nd_step (ε Adnd) (τ Adnd) (ω Adnd)) by simp
next
  show PSKIPS_nd_step (ε Adnd) (τ Adnd) (ω Adnd) PSKIPSAd = PSKIPSAd
    by (subst (3) PSKIPS_d_rec)
      (rule ext, auto simp add: from_det_to_ndet_def ε_simps
        split: option.split intro: mono_Mprefix_eq)
qed


corollary P_nd_from_det_to_ndet_is_P_d : P⟪Adndnd = P⟪Ad
proof -
  have P⟪Adndnd = PSKIPSAdndω := λσ. {}nd
    by (fact PSKIPS_nd_updated_ω)
  also have Adndω := λσ. {} = Aω := λσ. dnd
    by (simp add: from_det_to_ndet_def)
  finally show P⟪Adndnd = P⟪Ad
    by (simp add: PSKIPS_d_updated_ω PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d)
qed



subsection ‹Prove Equality between ProcOmata›

subsubsection ‹This is the easiest method we can think about.›

lemma P_d_eqI : (σ a. τ A σ a = τ B σ a)  P⟪Ad = P⟪Bd
  by (simp add: P_d_def ε_simps)

lemma P_nd_eqI : (σ a. τ A σ a = τ B σ a)  P⟪And = P⟪Bnd
  by (simp add: P_nd_def ε_simps)

lemma PSKIPS_d_eqI :
  (σ a. σ  ρ A  τ A σ a = τ B σ a)  (σ. ω A σ = ω B σ)  PSKIPSAd = PSKIPSBd
  by (subst PSKIPS_d_def, rule restriction_fix_unique, simp)
    (subst (2) PSKIPS_d_rec, auto simp add: ε_simps ρ_simps split: option.split)

lemma PSKIPS_nd_eqI :
  (σ a. σ  ρ A  τ A σ a = τ B σ a)  (σ. ω A σ = ω B σ)  PSKIPSAnd = PSKIPSBnd
  by (subst PSKIPS_nd_def, rule restriction_fix_unique[OF PSKIPS_nd_step_constructive])
    (subst (2) PSKIPS_nd_rec, auto simp add: ε_simps ρ_simps split: option.split)



subsubsection ‹We establish now a much more powerful theorem.›

theorem PSKIPS_nd_eqI_strong:
  (* TODO: see if we can obtain better by looking at ρ *)
  assumes inj_on_f : inj_on f (nd A0 σ0)
    and eq_trans : σ0' a. σ0'  nd A0 σ0  τ A1 (f σ0') a = f ` (τ A0 σ0' a)
    and eq_fin : σ0'. σ0'  nd A0 σ0  ω A1 (f σ0') = ω A0 σ0'
  shows PSKIPSA0nd σ0 = PSKIPSA1nd (f σ0)
proof -
  have σ0'  nd A0 σ0  PSKIPSA1nd (f σ0') = PSKIPSA0nd σ0' for σ0'
  proof (induct A1 arbitrary: σ0' rule: PSKIPS_nd_induct)
    case adm show ?case by simp
  next
    show σ0'  nd A0 σ0  PSKIPSA0nd (inv_into (nd A0 σ0) f (f σ0')) = PSKIPSA0nd σ0' for σ0'
      by (simp add: inj_on_f)
  next
    case (step X)
    from step.prems eq_trans have ε A0 σ0' = ε A1 (f σ0')
      by (auto simp add: ε_simps)
    moreover have ω A1 (f σ0') = ω A0 σ0' by (simp add: eq_fin step.prems)
    ultimately show ?case
      by (subst PSKIPS_nd_rec, auto)
        (metis (mono_tags, lifting) nd.step eq_trans mono_GlobalNdet_eq2
          step.hyps step.prems)
  qed
  thus PSKIPSA0nd σ0 = PSKIPSA1nd (f σ0) by (simp add: nd.init)
qed


theorem P_nd_eqI_strong:
  inj_on f (nd A0 σ0);
    σ0' a. σ0'  nd A0 σ0  τ A1 (f σ0') a = f ` (τ A0 σ0' a)
   P⟪A0nd σ0 = P⟪A1nd (f σ0)
  by (unfold PSKIPS_nd_updated_ω, rule PSKIPS_nd_eqI_strong) simp_all


theorem PSKIPS_d_eqI_strong:
  assumes inj_on f (d A0 σ0)
    and σ0' a. σ0'  d A0 σ0  τ A1 (f σ0') a = map_option f (τ A0 σ0' a)
    and σ0'. σ0'  d A0 σ0  ω A1 (f σ0') = ω A0 σ0'
  shows PSKIPSA0d σ0 = PSKIPSA1d (f σ0)
  by (fold PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d, rule PSKIPS_nd_eqI_strong)
    (unfold nd_from_det_to_ndet,
      simp_all add: assms from_det_to_ndet_def map_option_case split: option.split)


theorem P_d_eqI_strong:
  inj_on f (d A0 σ0);
    σ0' a. σ0'  d A0 σ0  τ A1 (f σ0') a = map_option f (τ A0 σ0' a)
    P⟪A0d σ0 = P⟪A1d (f σ0)
  by (unfold PSKIPS_d_updated_ω, rule PSKIPS_d_eqI_strong) simp_all


lemmas PSKIPS_nd_eqI_strong_id = PSKIPS_nd_eqI_strong[of id, simplified]
  and  PSKIPS_d_eqI_strong_id = PSKIPS_d_eqI_strong
  [of id, simplified id_def option.map_ident, simplified]
  and  P_nd_eqI_strong_id = P_nd_eqI_strong[of id, simplified]
  and  P_d_eqI_strong_id = P_d_eqI_strong
  [of id, simplified id_def option.map_ident, simplified]


corollary PSKIPS_nd_from_σ_to_σs_is_PSKIPS_nd : PSKIPSndAσσsnd [σ] = PSKIPSAnd σ
  by (auto simp add: image_iff behaviour_σ_σs_conv(6, 8)
      intro!: inj_onI PSKIPS_nd_eqI_strong[symmetric])

corollary PSKIPS_d_from_σ_to_σs_is_PSKIPS_d : PSKIPSdAσσsd [σ] = PSKIPSAd σ
  by (auto simp add: image_iff behaviour_σ_σs_conv(2, 4)
      intro!: inj_onI PSKIPS_d_eqI_strong[symmetric] split: option.split)

corollary P_nd_from_σ_to_σs_is_P_nd : P⟪ndAσσsnd [σ] = P⟪And σ
  by (auto simp add: image_iff behaviour_σ_σs_conv(6, 8)
      intro!: inj_onI P_nd_eqI_strong[symmetric])

corollary P_d_from_σ_to_σs_is_P_d : P⟪dAσσsd [σ] = P⟪Ad σ
  by (auto simp add: image_iff behaviour_σ_σs_conv(2, 4)
      intro!: inj_onI P_d_eqI_strong[symmetric] split: option.split)



text ‹Behaviour of normalizations. We will use the following methods in combining theories.›
  (* 
method PSKIPS_when_indep_method uses indep R_d_subset R_nd_subset trans_result defs = 
  subst PSKIPS_d_is_some_PSKIPS_nd, rule PSKIPS_ndI_strong_id[rule_format], simp_all,
  subst (asm) ℛnd_is_ℛd, drule set_mp[OF R_d_subset, rotated],
  (simp add: indep)?, (insert trans_result indep)[1], fastforce,
  rule arg_cong2[where f = ‹(∩)›], solves simp, simp add: det_ndet_conv_defs defs

method P_when_indep_method uses indep R_d_subset trans_result = 
  subst P_d_is_some_P_nd, rule P_ndI_strong_id[rule_format], simp_all, 
  subst (asm) ℛnd_is_ℛd, drule set_mp[OF R_d_subset, rotated], (simp add: indep)?, (*for arbitrary*)
  (insert trans_result indep)[1], fastforce
 *)



(* TODO: find a better place for the things below ? *)
fun recursive_modifier_fund :: [( × 'a)   option, (( × 'a) ×  option) list]  ( × 'a)   option
  where recursive_modifier_fund f [] = f
  |     recursive_modifier_fund f (((s, e), t) # 𝒢A) = recursive_modifier_fund (f((s, e) := t)) 𝒢A


abbreviation recursive_constructor_Ad :: [(( × 'a) ×  option) list,   'r option]  (, 'a, 'r) Ad
  where recursive_constructor_Ad 𝒢A ωA  τ = curry (recursive_modifier_fund (λ(s, e). ) 𝒢A), ω = ωA


lemma ε_det_breaker:
  ε (τ = curry (g((σ'::, a)  σ''::)), ω = some_ω,  = some_more ) σ = 
   (if σ = σ' then {a}  ε τ = curry g, ω = some_ω σ' else ε τ = curry g, ω = some_ω σ)
  by (auto simp add: ε_simps split: if_splits)

method ε_det_calc = (unfold recursive_modifier_fund.simps ε_det_breaker, simp cong: if_cong)[1]

method τ_det_calc = (unfold recursive_modifier_fund.simps, simp cong: if_cong)[1]







lemma bij_Renaming_PSKIPS_nd :
  fixes A :: (, 'a, 'r, ) And_scheme and f :: 'a  'b and g :: 'r  's
  assumes bij f
  defines B_def : B  τ = λσ b. τ A σ (inv f b), ω = λσ. g ` (ω A σ)
  shows Renaming (PSKIPSAnd σ) f g = PSKIPSBnd σ (is ?lhs σ = _)
proof (rule fun_cong[of ?lhs PSKIPSBnd σ])
  show ?lhs = PSKIPSBnd
  proof (rule restriction_fix_unique[OF PSKIPS_nd_step_constructive_bis[of B],
        symmetric, folded PSKIPS_nd_def])
    show PSKIPS_nd_step (ε B) (τ B) (ω B) ?lhs = ?lhs
    proof (rule ext)
      have * : ε B σ = f ` ε A σ for σ
        by (simp add: B_def ε_simps image_def) (metis bij f bij_inv_eq_iff)
      have ** : inv f (f a) = a for a
        by (metis bij f bij_inv_eq_iff)
      have *** : (THE a'. f a' = f a) = a for a
        by (rule the1_equality', metis (mono_tags, lifting) Uniq_I assms(1) bij_betw_def injD, simp)
      show PSKIPS_nd_step (ε B) (τ B) (ω B) ?lhs σ = ?lhs σ for σ
        by (subst (2) PSKIPS_nd_rec, simp add: "*")
          (auto simp add: "**" "***" B_def Renaming_distrib_GlobalNdet
            Renaming_Mprefix_image_inj[OF bij f[THEN bij_is_inj]]
            intro: mono_Mprefix_eq)
    qed
  qed
qed

lemma bij_Renaming_PSKIPS_d :
  bij f  Renaming (PSKIPSAd σ) f g =
             PSKIPSτ = λσ b. τ A σ (inv f b), ω = λσ. map_option g (ω A σ)d σ
  by (subst (1 2) PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d[symmetric],
      subst bij_Renaming_PSKIPS_nd, assumption)
    (rule fun_cong[of _ _ σ], rule PSKIPS_nd_eqI,
      simp_all add: from_det_to_ndet_def split: option.split)


lemma RenamingTick_PSKIPS_nd :
  RenamingTick (PSKIPSAnd σ) g = PSKIPSτ = τ A, ω = λσ. g ` ω A σnd σ
  by (simp add: bij_Renaming_PSKIPS_nd)

lemma RenamingTick_PSKIPS_d :
  RenamingTick (PSKIPSAd σ) g = PSKIPSτ = τ A, ω = λσ. map_option g (ω A σ)d σ
  by (simp add: bij_Renaming_PSKIPS_d)



(*<*)
end
  (*>*)