Theory Process_Normalization_Properties

(***********************************************************************************
 * 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 ‹Advanced Properties of ProcOmata›

(*<*)
theory Process_Normalization_Properties
  imports Process_Normalization Deterministic_Processes
begin
(*>*)


section ‹Determinism of deterministic ProcOmata›

lemma deterministic_PSKIPS_d : ‹deterministic (PSKIPS⟪A⟫d σ)›
proof (induct A arbitrary: σ rule: PSKIPS_d_induct)
  show ‹adm↓ (λf. ∀x. deterministic (f x))› by simp
next
  show ‹deterministic STOP› by simp
next
  show ‹(⋀σ. deterministic (X σ)) ⟹
        deterministic (PSKIPS_d_step (ε A) (τ A) (ω A) X σ)› for X σ
    by (simp add: deterministic_Mprefix_iff split: option.split)
qed

corollary deterministic_P_d : ‹deterministic (P⟪A⟫d σ)›
  by (subst PSKIPS_d_updated_ω) (fact deterministic_PSKIPS_d)

corollary PSKIPS_d_FD_imp_eq_PSKIPS_d : ‹PSKIPS⟪A⟫d σ ⊑FD P ⟹ P = PSKIPS⟪A⟫d σ›
  using deterministic_iff_maximal_for_leFD[THEN iffD1, OF deterministic_PSKIPS_d] by fast

corollary P_d_FD_imp_eq_P_d : ‹P⟪A⟫d σ ⊑FD P ⟹ P = P⟪A⟫d σ›
  using deterministic_iff_maximal_for_leFD[THEN iffD1, OF deterministic_P_d] by fast



section ‹No Divergence for ProcOmata›

lemma div_free_PSKIPS_nd : ‹𝒟 (PSKIPS⟪A⟫nd σ) = {}›
proof (induct A arbitrary: σ rule: PSKIPS_nd_induct)
  have ‹adm↓ (λX. ∀σ t. t ∈ 𝒟 (X σ) ⟶ False)›
    by (intro restriction_adm_all restriction_adm_imp) simp_all
  thus ‹adm↓ (λX. ∀σ. 𝒟 (X σ) = {})› by simp
next
  from D_STOP show ‹𝒟 STOP = {}› .
next
  show ‹(⋀σ. 𝒟 (X σ) = {}) ⟹ 𝒟 (PSKIPS_nd_step (ε A) (τ A) (ω A) X σ) = {}› for X σ
    by (simp add: D_Mprefix D_GlobalNdet D_SKIPS)
qed

corollary div_free_PSKIPS_d : ‹𝒟 (PSKIPS⟪A⟫d σ) = {}›
  by (simp add: deterministic_PSKIPS_d deterministic_div_free)

corollary div_free_P_nd : ‹𝒟 (P⟪A⟫nd s) = {}›
  by (simp add: PSKIPS_nd_updated_ω div_free_PSKIPS_nd)

corollary div_free_P_d : ‹𝒟 (P⟪A⟫d s) = {}›
  by (simp add: deterministic_P_d deterministic_div_free)



section ‹Reachability and Path›

text ‹We first need a preliminary result on const‹ℛnd›: a state term‹t› is reachable
      from a state term‹s› if and only if there exists a path between term‹s› and term‹t››

(* we may want to formalize the definition of a path, but not for the moment *)

lemma ℛnd_iff_Ex_path:
  ‹σ' ∈ ℛnd A σ ⟷ (∃path. path ≠ [] ∧ hd path = σ ∧ last path = σ' ∧
                            (∀i < length path - 1. ∃a. path ! Suc i ∈ τ A (path ! i) a))›
  (is ‹_ ⟷ (∃path. path ≠ [] ∧ ?rhs path σ σ')›)
proof (rule iffI)
  show ‹σ' ∈ ℛnd A σ ⟹ ∃path. path ≠ [] ∧ ?rhs path σ σ'›
  proof (induct rule: ℛnd.induct)
    case init
    have ‹[σ] ≠ [] ∧ ?rhs [σ] σ σ› by simp
    thus ?case ..
  next
    case (step σ' σ'' a)
    from this(2) obtain path where ‹path ≠ [] ∧ ?rhs path σ σ'› ..
    with step.hyps(3) have ‹path @ [σ''] ≠ [] ∧ ?rhs (path @ [σ'']) σ σ''›
      by (simp add: nth_append)
        (metis One_nat_def Suc_lessI diff_Suc_Suc diff_zero last_conv_nth)
    thus ?case ..
  qed
next
  assume ‹∃path. path ≠ [] ∧ ?rhs path σ σ'›
  then obtain path where ‹path ≠ []› ‹?rhs path σ σ'› by blast
  thus ‹σ' ∈ ℛnd A σ›
  proof (induct path arbitrary: σ rule: list_nonempty_induct)
    case (single u)
    thus ?case using ℛnd.init by fastforce
  next
    case (cons σ'' path)
    have ‹σ' ∈ ℛnd A (hd path)›
      by (rule cons.hyps(2))
        (use cons.hyps(1) cons.prems in simp,
          metis Suc_pred length_greater_0_conv not_less_eq nth_Cons_Suc)
    moreover have ‹hd path ∈ ℛnd A σ›
      using ℛnd.init ℛnd.step cons.hyps(1) cons.prems hd_conv_nth by fastforce
    ultimately show ?case by (fact ℛnd_trans)
  qed
qed

lemma ℛd_iff_Ex_path: 
  ‹σ' ∈ ℛd A σ ⟷ (∃path. path ≠ [] ∧ hd path = σ ∧ last path = σ' ∧
                            (∀i < length path - 1. ∃a. Some (path ! Suc i) = τ A (path ! i) a))›
  by (subst ℛnd_from_det_to_ndet[symmetric], subst ℛnd_iff_Ex_path)
    (simp add: from_det_to_ndet_def split: option.split, metis option.inject)



section ‹Deadlock Freeness and ProcOmata›

lemma deadlock_free_P_nd_step_iff :
  ‹deadlock_free (P_nd_step (ε A) (τ A) X σ) ⟷ 
      ε A σ ≠ {} ∧ (∀a ∈ ε A σ. ∀σ' ∈ τ A σ a. deadlock_free (X σ'))›
  and deadlock_freeSKIPS_P_nd_step_iff :
  ‹deadlock_freeSKIPS (P_nd_step (ε A) (τ A) X σ) ⟷ 
      ε A σ ≠ {} ∧ (∀a ∈ ε A σ. ∀σ' ∈ τ A σ a. deadlock_freeSKIPS (X σ'))›
  and deadlock_free_PSKIPS_nd_step_iff :
  ‹deadlock_free (PSKIPS_nd_step (ε A) (τ A) (ω A) X σ) ⟷
      σ ∉ ρ A ∧ ε A σ ≠ {} ∧ (∀a ∈ ε A σ. ∀σ' ∈ τ A σ a. deadlock_free (X σ'))›
  and deadlock_freeSKIPS_PSKIPS_nd_step_iff :
  ‹deadlock_freeSKIPS (PSKIPS_nd_step (ε A) (τ A) (ω A) X σ) ⟷
      σ ∈ ρ A ∨ ε A σ ≠ {} ∧ (∀a ∈ ε A σ. ∀σ' ∈ τ A σ a. deadlock_freeSKIPS (X σ'))›
  by (simp_all add: deadlock_free_Mprefix_iff deadlock_freeSKIPS_Mprefix_iff
      deadlock_free_GlobalNdet_iff deadlock_freeSKIPS_GlobalNdet_iff
      non_deadlock_free_SKIPS deadlock_freeSKIPS_SKIPS ε_simps ρ_simps)


lemma deadlock_free_P_d_step_iff :
  ‹deadlock_free (P_d_step (ε A) (τ A) X σ) ⟷ 
       ε A σ ≠ {} ∧ (∀a ∈ ε A σ. deadlock_free (X ⌈τ A σ a⌉))›
  and deadlock_freeSKIPS_P_d_step_iff :
  ‹deadlock_freeSKIPS (P_d_step (ε A) (τ A) X σ) ⟷ 
      ε A σ ≠ {} ∧ (∀a ∈ ε A σ. deadlock_freeSKIPS (X ⌈τ A σ a⌉))›
  and deadlock_free_PSKIPS_d_step_iff:
  ‹deadlock_free (PSKIPS_d_step (ε A) (τ A) (ω A) X σ) ⟷
      σ ∉ ρ A ∧ ε A σ ≠ {} ∧ (∀a ∈ ε A σ. deadlock_free (X ⌈τ A σ a⌉))›
  and deadlock_freeSKIPS_PSKIPS_d_step_iff:
  ‹deadlock_freeSKIPS (PSKIPS_d_step (ε A) (τ A) (ω A) X σ) ⟷
      σ ∈ ρ A ∨ ε A σ ≠ {} ∧ (∀a ∈ ε A σ. deadlock_freeSKIPS (X ⌈τ A σ a⌉))›
  by (simp_all add: deadlock_free_Mprefix_iff deadlock_freeSKIPS_Mprefix_iff
      non_deadlock_free_SKIP deadlock_freeSKIPS_SKIP ρ_simps
      split: option.split)


lemma deadlock_freeSKIPS_PSKIPS_nd :
  ‹(⋀σ'. σ' ∈ ℛnd A σ ⟹ σ' ∈ ρ A ∨ ε A σ' ≠ {}) ⟹
   deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ)›
proof (induct A arbitrary: σ rule: PSKIPS_nd_induct)
  case adm
  show ?case by (simp add: deadlock_freeSKIPS_def)  
next
  show ‹deadlock_freeSKIPS (SKIP undefined)›
    by (fact deadlock_freeSKIPS_SKIP)
next
  case (step X) thus ?case
    unfolding deadlock_freeSKIPS_PSKIPS_nd_step_iff
    by (meson ℛnd.init ℛnd.step ℛnd_trans)
qed

lemma deadlock_freeSKIPS_PSKIPS_d :
  ‹(⋀σ'. σ' ∈ ℛd A σ ⟹ σ' ∈ ρ A ∨ ε A σ' ≠ {}) ⟹
   deadlock_freeSKIPS (PSKIPS⟪A⟫d σ)›
proof (induct A arbitrary: σ rule: PSKIPS_d_induct)
  case adm
  show ?case by (simp add: deadlock_freeSKIPS_def)  
next
  show ‹deadlock_freeSKIPS (SKIP undefined)›
    by (fact deadlock_freeSKIPS_SKIP)
next
  case (step X)
  thus ?case
    unfolding deadlock_freeSKIPS_PSKIPS_d_step_iff
    by (simp add: ε_simps) (metis ℛd.init ℛd.step ℛd_trans option.sel)
qed


lemma deadlock_freeSKIPS_PSKIPS_nd_iff :
  assumes ‹ρ_disjoint_ε A›
  shows ‹deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ) ⟷ (∀σ' ∈ ℛnd A σ. σ' ∈ ρ A ∨ ε A σ' ≠ {})›
proof (intro iffI ballI)
  have ‹σ' ∈ ℛnd A σ ⟹ deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ) ⟹
        (σ' ∈ ρ A ∨ ε A σ' ≠ {}) ∧ deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ')› for σ'
  proof (induct rule: ℛnd.induct)
    case init
    thus ?case
      by (subst (asm) PSKIPS_nd_rec)
        (auto simp add: deadlock_freeSKIPS_PSKIPS_nd_step_iff init)
  next
    case (step σ' σ'' a)
    from step.hyps(2)[OF step.prems] step.hyps(3) ρ_disjoint_εD[OF assms]
    show ?case
      by (subst (asm) PSKIPS_nd_rec)
        (auto simp add: deadlock_freeSKIPS_PSKIPS_nd_step_iff ε_simps,
          metis (mono_tags, lifting) Mprefix_is_STOP_iff PSKIPS_nd_rec_notin_ρ
          SKIPS_empty ε_simps(2) deadlock_freeSKIPS_SKIPS empty_Collect_eq empty_iff)
  qed
  thus ‹σ' ∈ ℛnd A σ ⟹ deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ) ⟹ σ' ∈ ρ A ∨ ε A σ' ≠ {}› for σ' ..
next
  show ‹∀σ'∈ℛnd A σ. σ' ∈ ρ A ∨ ε A σ' ≠ {} ⟹ deadlock_freeSKIPS (PSKIPS⟪A⟫nd σ)›
    by (simp add: deadlock_freeSKIPS_PSKIPS_nd)
qed


lemma deadlock_freeSKIPS_PSKIPS_d_iff :
  ‹ρ_disjoint_ε A ⟹
   deadlock_freeSKIPS (PSKIPS⟪A⟫d σ) ⟷ (∀σ' ∈ ℛd A σ. σ' ∈ ρ A ∨ ε A σ' ≠ {})›
  by (fold PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d, subst deadlock_freeSKIPS_PSKIPS_nd_iff)
    (simp_all add: ρ_disjoint_ε_def ℛnd_from_det_to_ndet)




lemma deadlock_free_P_nd :
  ‹(⋀σ'. σ' ∈ ℛnd A σ ⟹ ε A σ' ≠ {}) ⟹ deadlock_free (P⟪A⟫nd σ)›
proof (induct A arbitrary: σ rule: P_nd_induct)
  case adm
  show ?case by (simp add: deadlock_free_def)  
next
  show ‹deadlock_free (DF UNIV)›
    by (simp add: deadlock_free_def)
next
  case (step X) thus ?case
    unfolding deadlock_free_P_nd_step_iff
    by (meson ℛnd.init ℛnd.step ℛnd_trans)
qed

lemma deadlock_free_P_d :
  ‹(⋀σ'. σ' ∈ ℛd A σ ⟹ ε A σ' ≠ {}) ⟹ deadlock_free (P⟪A⟫d σ)›
proof (induct A arbitrary: σ rule: P_d_induct)
  case adm
  show ?case by (simp add: deadlock_free_def)  
next
  show ‹deadlock_free (DF UNIV)›
    by (simp add: deadlock_free_def)
next
  case (step X) thus ?case
    unfolding deadlock_free_P_d_step_iff
    by (simp add: ε_simps)
      (metis ℛd.init ℛd.step ℛd_trans option.sel)
qed


lemma deadlock_free_P_nd_iff:
  ‹deadlock_free (P⟪A⟫nd σ) ⟷ (∀σ' ∈ ℛnd A σ. ε A σ' ≠ {})›
proof (intro iffI ballI)
  have ‹σ' ∈ ℛnd A σ ⟹ deadlock_free (P⟪A⟫nd σ) ⟹
        (ε A σ' ≠ {}) ∧ deadlock_free (P⟪A⟫nd σ')› for σ'
  proof (induct rule: ℛnd.induct)
    case init
    thus ?case
      by (subst (asm) P_nd_rec)
        (auto simp add: deadlock_free_P_nd_step_iff init)
  next
    case (step σ' σ'' a)
    from step.hyps(2)[OF step.prems] step.hyps(3)
    show ?case
      by (subst (asm) P_nd_rec, unfold deadlock_free_P_nd_step_iff)
        (simp add: ε_simps,
          metis (mono_tags, lifting) P_nd_rec ε_simps(2)
          deadlock_free_Mprefix_iff empty_Collect_eq empty_iff)
  qed
  thus ‹σ' ∈ ℛnd A σ ⟹ deadlock_free (P⟪A⟫nd σ) ⟹ ε A σ' ≠ {}› for σ' ..
next
  show ‹∀σ'∈ℛnd A σ. ε A σ' ≠ {} ⟹ deadlock_free (P⟪A⟫nd σ)›
    by (simp add: deadlock_free_P_nd)
qed



lemma deadlock_free_P_d_iff :
  ‹deadlock_free (P⟪A⟫d σ) ⟷ (∀σ' ∈ ℛd A σ. ε A σ' ≠ {})›
  by (fold P_nd_from_det_to_ndet_is_P_d, unfold deadlock_free_P_nd_iff)
    (simp add: ℛnd_from_det_to_ndet)



section ‹Events and Ticks of ProcOmata›

subsection ‹Events›

lemma events_of_PSKIPS_nd_subset :
  ‹α(PSKIPS⟪A⟫nd σ) ⊆ (⋃σ' ∈ ℛnd A σ. ε A σ')›
proof (rule subsetI)
  fix a assume ‹a ∈ α(PSKIPS⟪A⟫nd σ)›
  then obtain t where ‹t ≠ []› ‹t ∈ 𝒯 (PSKIPS⟪A⟫nd σ)› ‹ev a ∈ set t› 
    by (metis empty_iff empty_set events_of_memD)
  thus ‹a ∈ (⋃σ' ∈ ℛnd A σ. ε A σ')›
  proof (induct t arbitrary: σ rule: list_nonempty_induct)
    case (single t0)
    thus ?case by (subst (asm) PSKIPS_nd_rec)
        (auto simp add: T_SKIPS T_Mprefix ℛnd.init split: if_split_asm)
  next
    case (cons t0 t)
    from cons.prems(1) obtain b
      where ‹b ∈ ε A σ› ‹t0 = ev b› ‹t ∈ 𝒯 (⊓σ' ∈ τ A σ b. PSKIPS⟪A⟫nd σ')›
      by (subst (asm) (3) PSKIPS_nd_rec)
        (auto simp add: T_Mprefix T_GlobalNdet T_SKIPS cons.hyps(1) ε_simps split: if_split_asm)
    thus ?case
      by (simp add: T_GlobalNdet cons.hyps(1) split: if_split_asm)
        (metis (no_types, lifting) UN_iff ℛnd.simps ℛnd_trans cons.hyps(2)
          cons.prems(2) eventptick.inject(1) set_ConsD)
  qed
qed

corollary events_of_PSKIPS_d_subset :
  ‹α(PSKIPS⟪A⟫d σ) ⊆ (⋃σ' ∈ ℛd A σ. ε A σ')›
  by (metis PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d ℛnd_from_det_to_ndet
      det_ndet_conv_ε(1) events_of_PSKIPS_nd_subset)


lemma events_of_PSKIPS_nd :
  ‹α(PSKIPS⟪A⟫nd σ) = (⋃σ' ∈ ℛnd A σ. ε A σ')›
  if ρ_disjoint_ε : ‹ρ_disjoint_ε A›
proof (intro subset_antisym subsetI)
  show ‹a ∈ α(PSKIPS⟪A⟫nd σ) ⟹ a ∈ ⋃ (ε A ` ℛnd A σ)› for a
    by (meson events_of_PSKIPS_nd_subset in_mono)
next
  fix a assume ‹a ∈ (⋃σ' ∈ ℛnd A σ. ε A σ')›
  then obtain σ' where a1: ‹σ' ∈ ℛnd A σ› and a2: ‹a ∈ ε A σ'› by blast
  from a1[simplified ℛnd_iff_Ex_path] obtain path
    where ‹path ≠ []› ‹hd path = σ ∧ last path = σ' ∧ 
           (∀i<length path - 1. ∃e. path ! Suc i ∈ τ A (path ! i) e)› by blast
  from this a2 show ‹a ∈ α(PSKIPS⟪A⟫nd σ)›
  proof (induct path arbitrary: σ rule: list_nonempty_induct)
    case (single p)
    with ρ_disjoint_ε[THEN ρ_disjoint_εD] show ?case
      by (subst PSKIPS_nd_rec)
        (auto simp add: events_of_Mprefix events_of_SKIPS ρ_simps)
  next
    case (cons p path)
    from cons(3) obtain b where ‹b ∈ ε A σ› ‹hd path ∈ τ A σ b›
      by (auto simp add: ε_simps) (metis cons(1) empty_iff hd_conv_nth length_greater_0_conv nth_Cons_0)
    moreover have ‹a ∈ α(PSKIPS⟪A⟫nd (hd path))›
      using a2 cons.hyps(1, 2) cons.prems(1) less_diff_conv by fastforce
    ultimately show ?case
      using ρ_disjoint_ε[THEN ρ_disjoint_εD]
      by (subst PSKIPS_nd_rec)
        (auto simp add: events_of_Mprefix events_of_GlobalNdet events_of_SKIPS ρ_simps)
  qed
qed

corollary events_of_PSKIPS_d: ‹ρ_disjoint_ε A ⟹ α(PSKIPS⟪A⟫d σ) = ⋃ (ε A ` ℛd A σ)›
  by (fold PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d, subst events_of_PSKIPS_nd)
    (simp_all add: ρ_disjoint_ε_def ℛnd_from_det_to_ndet)



lemma events_of_P_nd: ‹α(P⟪A⟫nd σ) = ⋃ (ε A ` ℛnd A σ)›
  by (unfold PSKIPS_nd_updated_ω, subst events_of_PSKIPS_nd)
    (auto simp add: ρ_disjoint_ε_def ρ_simps ε_simps)


lemma events_of_P_d: ‹α(P⟪A⟫d σ) = ⋃ (ε A ` ℛd A σ)›
  by (metis P_nd_from_det_to_ndet_is_P_d ℛnd_from_det_to_ndet
      det_ndet_conv_ε(1) events_of_P_nd)



subsection ‹Strict Events›

corollary strict_events_of_PSKIPS_nd_subset :
  ‹α(PSKIPS⟪A⟫nd σ) ⊆ (⋃σ' ∈ ℛnd A σ. ε A σ')›
  by (meson events_of_PSKIPS_nd_subset order_trans strict_events_of_subset_events_of)

corollary strict_events_of_PSKIPS_d_subset :
  ‹α(PSKIPS⟪A⟫d σ) ⊆ (⋃σ' ∈ ℛd A σ. ε A σ')›
  by (meson events_of_PSKIPS_d_subset order_trans strict_events_of_subset_events_of)

lemma strict_events_of_PSKIPS_nd :
  ‹ρ_disjoint_ε A ⟹ α(PSKIPS⟪A⟫nd σ) = (⋃σ' ∈ ℛnd A σ. ε A σ')›
  by (metis div_free_PSKIPS_nd events_of_PSKIPS_nd events_of_is_strict_events_of_or_UNIV)

corollary strict_events_of_PSKIPS_d:
  ‹ρ_disjoint_ε A ⟹ α(PSKIPS⟪A⟫d σ) = (⋃σ' ∈ ℛd A σ. ε A σ')›
  by (metis div_free_PSKIPS_d events_of_PSKIPS_d events_of_is_strict_events_of_or_UNIV)

lemma strict_events_of_P_nd: ‹α(P⟪A⟫nd σ) = (⋃σ' ∈ ℛnd A σ. ε A σ')›
  by (metis div_free_P_nd events_of_P_nd events_of_is_strict_events_of_or_UNIV)

lemma strict_events_of_P_d: ‹α(P⟪A⟫d σ) = (⋃σ' ∈ ℛd A σ. ε A σ')›
  by (metis div_free_P_d events_of_P_d events_of_is_strict_events_of_or_UNIV)



subsection ‹Ticks›

lemma ticks_of_PSKIPS_nd_subset :
  ‹✓s(PSKIPS⟪A⟫nd σ) ⊆ (⋃σ' ∈ ℛnd A σ. ω A σ')›
proof (rule subsetI)
  fix r assume ‹r ∈ ✓s(PSKIPS⟪A⟫nd σ)›
  then obtain t where ‹t @ [✓(r)] ∈ 𝒯 (PSKIPS⟪A⟫nd σ)›
    by (blast dest: ticks_of_memD)
  thus ‹r ∈ (⋃σ' ∈ ℛnd A σ. ω A σ')›
  proof (induct t arbitrary: σ)
    case Nil thus ?case
      by (subst (asm) PSKIPS_nd_rec)
        (auto simp add: T_SKIPS ℛnd.init split: if_split_asm)
  next
    case (Cons t0 t)
    from Cons.prems(1) obtain b
      where ‹b ∈ ε A σ› ‹t0 = ev b› ‹t @ [✓(r)] ∈ 𝒯 (⊓σ' ∈ τ A σ b. PSKIPS⟪A⟫nd σ')›
      by (subst (asm) (3) PSKIPS_nd_rec)
        (auto simp add: T_Mprefix T_GlobalNdet T_SKIPS Cons.hyps(1) ε_simps split: if_split_asm)
    thus ?case
      by (simp add: T_GlobalNdet Cons.hyps(1) split: if_split_asm)
        (metis ℛnd.init[of σ A] UN_iff[of r ‹ω A› ‹ℛnd A _›]
          ℛnd_trans[of _ A _ σ] ℛnd.step[of σ A σ _ b] Cons.hyps)
  qed
qed

corollary ticks_of_PSKIPS_d_subset :
  ‹✓s(PSKIPS⟪A⟫d σ) ⊆ {⌈ω A σ'⌉ |σ'. σ' ∈ ℛd A σ ∧ ω A σ' ≠ ◇}›
proof (fold PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d,
    rule subset_trans[OF ticks_of_PSKIPS_nd_subset])
  show ‹(⋃σ' ∈ ℛnd ⟪A⟫d↪nd σ. ω ⟪A⟫d↪nd σ') ⊆ {⌈ω A σ'⌉ |σ'. σ' ∈ ℛd A σ ∧ ω A σ' ≠ ◇}›
    by (simp add: ℛnd_from_det_to_ndet subset_iff)
      (simp add: from_det_to_ndet_def split: option.split, metis option.sel)
qed


lemma ticks_of_PSKIPS_nd :
  ‹✓s(PSKIPS⟪A⟫nd σ) = (⋃σ' ∈ ℛnd A σ. ω A σ')›
  if ρ_disjoint_ε : ‹ρ_disjoint_ε A›
proof (intro subset_antisym subsetI)
  show ‹r ∈ ✓s(PSKIPS⟪A⟫nd σ) ⟹ r ∈ (⋃σ' ∈ ℛnd A σ. ω A σ')› for r
    by (meson in_mono ticks_of_PSKIPS_nd_subset)
next
  fix r assume ‹r ∈ (⋃σ' ∈ ℛnd A σ. ω A σ')›
  then obtain σ' where a1: ‹σ' ∈ ℛnd A σ› and a2: ‹r ∈ ω A σ'› by blast
  from a1[simplified ℛnd_iff_Ex_path] obtain path
    where ‹path ≠ []› ‹hd path = σ ∧ last path = σ' ∧ 
           (∀i<length path - 1. ∃e. path ! Suc i ∈ τ A (path ! i) e)› by blast
  from this a2 show ‹r ∈ ✓s(PSKIPS⟪A⟫nd σ)›
  proof (induct path arbitrary: σ rule: list_nonempty_induct)
    case (single p)
    thus ?case
      by (subst PSKIPS_nd_rec)
        (auto simp add: ticks_of_SKIPS)
  next
    case (cons p path)
    from cons(3) obtain b where ‹b ∈ ε A σ› ‹hd path ∈ τ A σ b›
      by (auto simp add: ε_simps) (metis cons(1) empty_iff hd_conv_nth length_greater_0_conv nth_Cons_0)
    moreover have ‹r ∈ ✓s(PSKIPS⟪A⟫nd (hd path))›
      using a2 cons.hyps(1, 2) cons.prems(1) less_diff_conv by fastforce
    ultimately show ?case
      using ρ_disjoint_ε[THEN ρ_disjoint_εD]
      by (subst PSKIPS_nd_rec)
        (auto simp add: ticks_of_Mprefix ticks_of_GlobalNdet events_of_SKIPS ρ_simps)
  qed
qed

corollary ticks_of_PSKIPS_d :
  ‹ρ_disjoint_ε A ⟹ ✓s(PSKIPS⟪A⟫d σ) = {⌈ω A σ'⌉ |σ'. σ' ∈ ℛd A σ ∧ ω A σ' ≠ ◇}›
  by (fold PSKIPS_nd_from_det_to_ndet_is_PSKIPS_d, subst ticks_of_PSKIPS_nd,
      simp_all add: ρ_disjoint_ε_def ℛnd_from_det_to_ndet)
    (auto simp add: from_det_to_ndet_def split: option.split_asm, metis option.sel)


lemma ticks_of_P_nd : ‹✓s(P⟪A⟫nd σ) = {}›
  by (simp add: PSKIPS_nd_updated_ω ticks_of_PSKIPS_nd)

lemma ticks_of_P_d: ‹✓s(P⟪A⟫d σ) = {}›
  by (metis P_nd_from_det_to_ndet_is_P_d ticks_of_P_nd)



lemma non_terminating_iff_empty_ticks_of :
  ‹non_terminating P ⟷ ✓s(P) = {}›
  by (simp add: non_terminating_is_right tickFree_traces_iff_empty_ticks_of)



corollary non_terminating_P_nd : ‹non_terminating (P⟪A⟫nd σ)›
  by (simp add: non_terminating_iff_empty_ticks_of ticks_of_P_nd)

corollary non_terminating_P_d : ‹non_terminating (P⟪A⟫d σ)›
  by (metis P_nd_from_det_to_ndet_is_P_d non_terminating_P_nd)

corollary non_terminating_PSKIPS_nd :
  ‹ρ A ∩ ℛnd A σ = {} ⟹ non_terminating (PSKIPS⟪A⟫nd σ)›
  by (simp add: PSKIPS_nd_empty_ρ_inter_ℛnd non_terminating_P_nd)

corollary non_terminating_PSKIPS_nd_iff :
  ‹ρ_disjoint_ε A ⟹ non_terminating (PSKIPS⟪A⟫nd σ) ⟷ ρ A ∩ ℛnd A σ = {}›
  by (auto simp add: non_terminating_iff_empty_ticks_of ticks_of_PSKIPS_nd ρ_simps)

corollary non_terminating_PSKIPS_d :
  ‹ρ A ∩ ℛd A σ = {} ⟹ non_terminating (PSKIPS⟪A⟫d σ)›
  by (simp add: PSKIPS_d_empty_ρ_inter_ℛd non_terminating_P_d)

corollary non_terminating_PSKIPS_d_iff :
  ‹ρ_disjoint_ε A ⟹ non_terminating (PSKIPS⟪A⟫d σ) ⟷ ρ A ∩ ℛd A σ = {}›
  by (auto simp add: non_terminating_iff_empty_ticks_of ticks_of_PSKIPS_d ρ_simps)



subsection ‹Strict Ticks›

corollary strict_ticks_of_PSKIPS_nd_subset :
  ‹✓s(PSKIPS⟪A⟫nd σ) ⊆ (⋃σ' ∈ ℛnd A σ. ω A σ')›
  by (metis div_free_PSKIPS_nd ticks_of_PSKIPS_nd_subset ticks_of_is_strict_ticks_of_or_UNIV)

corollary strict_ticks_of_PSKIPS_d_subset :
  ‹✓s(PSKIPS⟪A⟫d σ) ⊆ {⌈ω A σ'⌉ |σ'. σ' ∈ ℛd A σ ∧ ω A σ' ≠ ◇}›
  by (metis (mono_tags, lifting) strict_ticks_of_subset_ticks_of subset_trans ticks_of_PSKIPS_d_subset)

lemma strict_ticks_of_PSKIPS_nd :
  ‹ρ_disjoint_ε A ⟹ ✓s(PSKIPS⟪A⟫nd σ) = (⋃σ' ∈ ℛnd A σ. ω A σ')›
  by (metis div_free_PSKIPS_nd ticks_of_PSKIPS_nd ticks_of_is_strict_ticks_of_or_UNIV)

corollary strict_ticks_of_PSKIPS_d :
  ‹ρ_disjoint_ε A ⟹ ✓s(PSKIPS⟪A⟫d σ) = {⌈ω A σ'⌉ |σ'. σ' ∈ ℛd A σ ∧ ω A σ' ≠ ◇}›
  by (metis (mono_tags, lifting) div_free_PSKIPS_d ticks_of_PSKIPS_d ticks_of_is_strict_ticks_of_or_UNIV)

lemma strict_ticks_of_P_nd: ‹✓s(P⟪A⟫nd σ) = {}›
  by (metis div_free_P_nd ticks_of_P_nd ticks_of_is_strict_ticks_of_or_UNIV)

lemma strict_ticks_of_P_d: ‹✓s(P⟪A⟫d σ) = {}›
  by (metis P_nd_from_det_to_ndet_is_P_d strict_ticks_of_P_nd)



subsection ‹Ticks length›

lemma is_ticks_length_PSKIPS_nd :
  ‹⟦⋀σ' rs. σ' ∈ ℛnd A σ ⟹ rs ∈ ω A σ' ⟹ length rs = n⟧
   ⟹ length✓⇘n⇙(PSKIPS⟪A⟫nd σ)›
  by (auto simp add: is_ticks_length_def
      dest!: set_mp[OF strict_ticks_of_PSKIPS_nd_subset])

lemma is_ticks_length_PSKIPS_nd_iff :
  ‹ρ_disjoint_ε A ⟹
   length✓⇘n⇙(PSKIPS⟪A⟫nd σ) ⟷ (∀σ' ∈ ℛnd A σ. ∀rs ∈ ω A σ'. length rs = n)›
  by (simp add: is_ticks_length_def strict_ticks_of_PSKIPS_nd)

lemma is_ticks_length_PSKIPS_d :
  ‹⟦⋀σ'. σ' ∈ ℛd A σ ⟹ ω A σ' ≠ ◇ ⟹ length ⌈ω A σ'⌉ = n⟧
   ⟹ length✓⇘n⇙(PSKIPS⟪A⟫d σ)›
  by (auto simp add: is_ticks_length_def
      dest!: set_mp[OF strict_ticks_of_PSKIPS_d_subset])

lemma is_ticks_length_PSKIPS_d_iff :
  ‹ρ_disjoint_ε A ⟹
   length✓⇘n⇙(PSKIPS⟪A⟫d σ) ⟷ (∀σ' ∈ ℛd A σ. ω A σ' ≠ ◇ ⟶ length ⌈ω A σ'⌉ = n)›
  by (auto simp add: is_ticks_length_def strict_ticks_of_PSKIPS_d)



(*<*)
end
(*>*)