Theory Checker_Code

(*<*)
theory Checker_Code
  imports 
    Checker Event_Data 
    "HOL-Library.Code_Target_Nat" 
    "HOL.String"
    "HOL-Library.List_Lexorder" 
    "HOL-Library.AList_Mapping"
    "HOL-Library.Cardinality"
begin
(*>*)

section ‹Code Generation›

subsection ‹Type Class Instances›

class universe =
  fixes universe :: "'a list option"
  assumes infinite: "universe = None  infinite (UNIV :: 'a set)"
  and finite: "universe = Some xs  distinct xs  set xs = UNIV"
begin

lemma finite_coset: "finite (List.coset (xs :: 'a list)) = (case universe of None  False | _  True)"
  using infinite finite
  by (auto split: option.splits dest!: equalityD2 elim!: finite_subset)

end

declare [[code drop: finite]]
declare finite_set[THEN eqTrueI, code] finite_coset[code]

instantiation bool :: universe begin
definition universe_bool :: "bool list option" where "universe_bool = Some [True, False]"
instance by standard (auto simp: universe_bool_def)
end
instantiation char :: universe begin
definition universe_char :: "char list option" where "universe_char = Some (map char_of [0::nat..<256])"
instance by standard (use UNIV_char_of_nat in auto simp: universe_char_def distinct_map)
end
instantiation nat :: universe begin
definition universe_nat :: "nat list option" where "universe_nat = None"
instance by standard (auto simp: universe_nat_def)
end
instantiation list :: (type) universe begin
definition universe_list :: "'a list list option" where "universe_list = None"
instance by standard (auto simp: universe_list_def infinite_UNIV_listI)
end
instantiation String.literal :: universe begin
definition universe_literal :: "String.literal list option" where "universe_literal = None"
instance by standard (auto simp: universe_literal_def infinite_literal)
end
instantiation string8 :: universe begin
definition universe_string8 :: "string8 list option" where "universe_string8 = None"
lemma UNIV_string8: "UNIV = Abs_string8 ` UNIV"
  by (auto simp: image_iff intro: Abs_string8_cases)
instance by standard
  (auto simp: universe_string8_def UNIV_string8 finite_image_iff Abs_string8_inject inj_on_def infinite_UNIV_listI)
end
instantiation prod :: (universe, universe) universe begin
definition universe_prod :: "('a × 'b) list option" where "universe_prod =
  (case (universe, universe) of (Some xs, Some ys)  Some (List.product xs ys) | _  None)"
instance by standard
  (auto simp: universe_prod_def finite_prod distinct_product infinite finite split: option.splits)
end
instantiation sum :: (universe, universe) universe begin
definition universe_sum :: "('a + 'b) list option" where "universe_sum =
  (case (universe, universe) of (Some xs, Some ys)  Some (map Inl xs @ map Inr ys) | _  None)"
instance by standard
  (use UNIV_sum in auto simp: universe_sum_def distinct_map infinite finite split: option.splits)
end
instantiation option :: (universe) universe begin
definition "universe_option = (case universe of Some xs  Some (None # map Some xs) | _  None)"
instance by standard (auto simp: universe_option_def distinct_map finite infinite image_iff split: option.splits)
end
instantiation "fun" :: (universe, universe) universe begin
definition universe_fun :: "('a  'b) list option" where "universe_fun = 
  (case (universe, universe) of
    (Some xs, Some ys)  Some (map (λzs. the  map_of (zip xs zs)) (List.n_lists (length xs) ys))
  | (_, Some [x])  Some [λ_. x]
  | _  None)"
instance
proof -
  have 1: False if "infinite (UNIV::'a set)" "CARD('b) = Suc 0" "a  b" for a b :: 'b
    using that by (metis (full_types) UNIV_I card_1_singleton_iff singletonD)
  have 2: "ys = zs"
    if "distinct (xs::'a list)" and "length ys = length xs" and "length zs = length xs"
    and "a. the (map_of (zip xs ys) a) = the (map_of (zip xs zs) a)"
    for xs :: "'a list" and ys zs :: "'b list"
    using that by (metis map_fst_zip map_of_eqI map_of_zip_inject map_of_zip_is_None option.expand)
  have 3: "zs. length zs = length xs  set zs  set ys  (x. f x = the (map_of (zip xs zs) x))"
    if "x. x  set xs" "x. x  set ys"
    for xs ys and f :: "'a  'b"
    using that by (metis length_map map_of_zip_map option.sel subsetI)
  show "OFCLASS('a  'b, universe_class)"
    by standard
      (auto 0 3 simp: universe_fun_def set_eq_iff fun_eq_iff image_iff set_n_lists distinct_map
        inj_on_def distinct_n_lists finite_UNIV_fun dest!: infinite finite
        split: option.splits list.splits intro: 1 2 3)
qed
end
instantiation event_data :: universe begin
definition universe_event_data :: "event_data list option" where "universe_event_data = None"  
instance by standard (simp_all add: infinite_UNIV_event_data universe_event_data_def)
end

instantiation nat :: default begin
definition default_nat :: nat where "default_nat = 0"
instance proof qed
end

instantiation list :: (type) default begin
definition default_list :: "'a list" where "default_list = []"
instance proof qed
end

instance event_data :: equal by standard

instantiation String.literal :: default begin
definition default_literal :: String.literal where "default_literal = 0"
instance proof qed
end

instantiation event_data :: card_UNIV begin
definition "finite_UNIV = Phantom(event_data) False"
definition "card_UNIV = Phantom(event_data) 0"
instance by intro_classes (simp_all add: finite_UNIV_event_data_def card_UNIV_event_data_def infinite_UNIV_event_data)
end

subsection ‹Progress›

primrec progress :: "('n, 'd) trace  ('n, 'd) Formula.formula  nat  nat" where
  "progress σ Formula.TT j = j"
| "progress σ Formula.FF j = j"
| "progress σ (Formula.Eq_Const _ _) j = j"
| "progress σ (Formula.Pred _ _) j = j"
| "progress σ (Formula.Neg φ) j = progress σ φ j"
| "progress σ (Formula.Or φ ψ) j = min (progress σ φ j) (progress σ ψ j)"
| "progress σ (Formula.And φ ψ) j = min (progress σ φ j) (progress σ ψ j)"
| "progress σ (Formula.Imp φ ψ) j = min (progress σ φ j) (progress σ ψ j)"
| "progress σ (Formula.Iff φ ψ) j = min (progress σ φ j) (progress σ ψ j)"
| "progress σ (Formula.Exists _ φ) j = progress σ φ j"
| "progress σ (Formula.Forall _ φ) j = progress σ φ j"
| "progress σ (Formula.Prev I φ) j = (if j = 0 then 0 else min (Suc (progress σ φ j)) j)"
| "progress σ (Formula.Next I φ) j = progress σ φ j - 1"
| "progress σ (Formula.Once I φ) j = progress σ φ j"
| "progress σ (Formula.Historically I φ) j = progress σ φ j"
| "progress σ (Formula.Eventually I φ) j =
    Inf {i. k. k < j  k  (progress σ φ j)  (τ σ k - τ σ i)  right I}"
| "progress σ (Formula.Always I φ) j =
    Inf {i. k. k < j  k  (progress σ φ j)  (τ σ k - τ σ i)  right I}"
| "progress σ (Formula.Since φ I ψ) j = min (progress σ φ j) (progress σ ψ j)"
| "progress σ (Formula.Until φ I ψ) j =
    Inf {i. k. k < j  k  min (progress σ φ j) (progress σ ψ j)  (τ σ k - τ σ i)  right I}"

lemma Inf_Min:
  fixes P :: "nat  bool"
  assumes "P j"
  shows "Inf (Collect P) = Min (Set.filter P {..j})"
  using Min_in[where ?A="Set.filter P {..j}"] assms
  by (auto simp: Set.filter_def intro: cInf_lower intro!: antisym[OF _ Min_le])
    (metis Inf_nat_def1 empty_iff mem_Collect_eq)

lemma progress_Eventually_code: "progress σ (Formula.Eventually I φ) j =
  (let m = min j (Suc (progress σ φ j)) - 1 in Min (Set.filter (λi. enat (δ σ m i)  right I) {..j}))"
proof -
  define P where "P  (λi. k. k < j  k  (progress σ φ j)  enat (δ σ k i)  right I)"
  have P_j: "P j"
    by (auto simp: P_def enat_0)
  have all_wit: "(k  {..<m}. enat (δ σ k i)  right I)  (enat (δ σ (m - 1) i)  right I)" for i m
  proof (cases m)
    case (Suc ma)
    have "k < Suc ma  δ σ k i  δ σ ma i" for k
      using τ_mono
      unfolding less_Suc_eq_le
      by (rule diff_le_mono)
    then show ?thesis
      by (auto simp: Suc) (meson order.trans enat_ord_simps(1))
  qed (auto simp: enat_0)
  show ?thesis
    unfolding progress.simps Let_def P_def[symmetric] Inf_Min[where ?P=P, OF P_j] all_wit[symmetric]
    by (fastforce simp: P_def intro: arg_cong[where ?f=Min])
qed

lemma progress_Always_code: "progress σ (Formula.Always I φ) j =
  (let m = min j (Suc (progress σ φ j)) - 1 in Min (Set.filter (λi. enat (δ σ m i)  right I) {..j}))"
proof -
  define P where "P  (λi. k. k < j  k  (progress σ φ j)  enat (δ σ k i)  right I)"
  have P_j: "P j"
    by (auto simp: P_def enat_0)
  have all_wit: "(k  {..<m}. enat (δ σ k i)  right I)  (enat (δ σ (m - 1) i)  right I)" for i m
  proof (cases m)
    case (Suc ma)
    have "k < Suc ma  δ σ k i  δ σ ma i" for k
      using τ_mono
      unfolding less_Suc_eq_le
      by (rule diff_le_mono)
    then show ?thesis
      by (auto simp: Suc) (meson order.trans enat_ord_simps(1))
  qed (auto simp: enat_0)
  show ?thesis
    unfolding progress.simps Let_def P_def[symmetric] Inf_Min[where ?P=P, OF P_j] all_wit[symmetric]
    by (fastforce simp: P_def intro: arg_cong[where ?f=Min])
qed

lemma progress_Until_code: "progress σ (Formula.Until φ I ψ) j =
  (let m = min j (Suc (min (progress σ φ j) (progress σ ψ j))) - 1 in Min (Set.filter (λi. enat (δ σ m i)  right I) {..j}))"
proof -
  define P where "P  (λi. k. k < j  k  min (progress σ φ j) (progress σ ψ j)  enat (δ σ k i)  right I)"
  have P_j: "P j"
    by (auto simp: P_def enat_0)
  have all_wit: "(k  {..<m}. enat (δ σ k i)  right I)  (enat (δ σ (m - 1) i)  right I)" for i m
  proof (cases m)
    case (Suc ma)
    have "k < Suc ma  δ σ k i  δ σ ma i" for k
      using τ_mono
      unfolding less_Suc_eq_le
      by (rule diff_le_mono)
    then show ?thesis
      by (auto simp: Suc) (meson order.trans enat_ord_simps(1))
  qed (auto simp: enat_0)
  show ?thesis
    unfolding progress.simps Let_def P_def[symmetric] Inf_Min[where ?P=P, OF P_j] all_wit[symmetric]
    by (fastforce simp: P_def intro: arg_cong[where ?f=Min])
qed

lemmas progress_code[code] = progress.simps(1-15) progress_Eventually_code progress_Always_code progress.simps(18) progress_Until_code

subsection ‹Trace›

lemma snth_Stream_eq: "(x ## s) !! n = (case n of 0  x | Suc m  s !! m)"
  by (cases n) auto

lemma extend_is_stream: 
  assumes "sorted (map snd list)"
  and "x. x  set list  snd x  m"
  and "x. x  set list  finite (fst x)"
  shows "ssorted (smap snd (list @- smap (λn. ({}, n + m)) nats)) 
    sincreasing (smap snd (list @- smap (λn. ({}, n + m)) nats)) 
    sfinite (smap fst (list @- smap (λn. ({}, n + m)) nats))"
proof -
  have A: "xset list. n  snd x  n  m 
    n  (map snd list @- smap (λx. x + m) nats) !! i" for n i 
    and list :: "('a set × nat) list"
  proof (induction i arbitrary: n)
    case (Suc i)
    then show ?case
      by (auto simp: shift_snth nth_tl)
  qed (auto simp add: list.map_sel(1))
  then have "ssorted (smap snd (list @- smap (λn. ({}, n + m)) nats))"
    using assms
      by (induction list) (auto simp: stream.map_comp o_def ssorted_iff_mono snth_Stream_eq
        split: nat.splits)
  moreover have "sincreasing (smap snd (list @- smap (λn. ({}, n + m)) nats))"
    using assms
  proof (induction list)
    case Nil
    then show ?case
      by (simp add: sincreasing_def) presburger
  next
    case (Cons a as)
    have IH: "x. i. x < smap snd (as @- smap (λn. ({}, n + m)) nats) !! i"
      using Cons
      by (simp add: sincreasing_def)
    show ?case
      using IH
      by (simp add: sincreasing_def) 
        (metis snth_Stream)
  qed
  moreover have "sfinite (smap fst (list @- smap (λn. ({}, n + m)) nats))"
    using assms(3)
  proof (induction list)
    case Nil
    then show ?case by (simp add: sfinite_def)
  next
    case (Cons a as)
    then have fin: "finite (fst a)"
      by simp
    show ?case
      using Cons 
      by (auto simp add: sfinite_def snth_Stream_eq split: nat.splits)
  qed
  ultimately show ?thesis
    by simp
qed

typedef 'a trace_mapping = "{(n, m, t) :: (nat × nat × (nat, 'a set × nat) mapping) |
  n m t. Mapping.keys t = {..<n} 
  sorted (map (snd  (the  Mapping.lookup t)) [0..<n]) 
  (case n of 0  True | Suc n'  (case Mapping.lookup t n' of Some (X', t')  t'  m | None  False))  
  (n' < n. case Mapping.lookup t n' of Some (X', t')  finite X' | None  False)}"
  by (rule exI[of _ "(0, 0, Mapping.empty)"]) auto

setup_lifting type_definition_trace_mapping

lemma lookup_bulkload_Some: "i < length list 
  Mapping.lookup (Mapping.bulkload list) i = Some (list ! i)"
  by transfer auto

lift_definition trace_mapping_of_list :: "('a set × nat) list  'a trace_mapping" is
  "λxs. if sorted (map snd xs)  (x  set xs. finite (fst x)) then (if xs = [] then (0, 0, Mapping.empty)
  else (length xs, snd (last xs), Mapping.bulkload xs))
  else (0, 0, Mapping.empty)"
  by (auto simp: lookup_bulkload_Some sorted_iff_nth_Suc last_conv_nth
    list_all_iff in_set_conv_nth Ball_def Bex_def image_iff split: nat.splits)

lift_definition trace_mapping_nth :: "'a trace_mapping  nat  ('a set × nat)" is
  "λ(n, m, t) i. if i < n then the (Mapping.lookup t i) else ({}, (i - n) + m)" .

lift_definition Trace_Mapping :: "'a trace_mapping  'a Trace.trace" is
  "λ(n, m, t). map (the  Mapping.lookup t) [0..<n] @- smap (λn. ({} :: 'a set, n + m)) nats"
proof (goal_cases)
  case (1 prod)
  obtain n m t where prod_def: "prod = (n, m, t)"
    by (cases prod) auto
  have props: "Mapping.keys t = {..<n}"
    "sorted (map (snd  (the  Mapping.lookup t)) [0..<n])"
    "(case n of 0  True | Suc n'  (case Mapping.lookup t n' of Some (X', t')  t'  m | None  False))"
    "(n' < n. case Mapping.lookup t n' of Some (X', t')  finite X' | None  False)"
    using 1 by (auto simp add: prod_def)
  have aux: "x  set (map (the  Mapping.lookup t) [0..<n])  snd x  m" for x
    using props(2,3) less_Suc_eq_le
    by (fastforce simp: sorted_iff_nth_mono split: nat.splits option.splits)
  have aux2: "x  set (map (the  Mapping.lookup t) [0..<n])  finite (fst x)" for x
    using props(1,4)
    by (auto split: nat.splits option.splits)
  show ?case
    unfolding prod_def prod.case
    by (rule extend_is_stream[where ?m=m]) (use props aux aux2 in auto simp: prod_def)
qed

code_datatype Trace_Mapping

definition "trace_of_list xs = Trace_Mapping (trace_mapping_of_list xs)"

lemma Γ_rbt_code[code]: "Γ (Trace_Mapping t) i = fst (trace_mapping_nth t i)"
  by transfer (auto split: prod.splits)

lemma τ_rbt_code[code]: "τ (Trace_Mapping t) i = snd (trace_mapping_nth t i)"
  by transfer (auto split: prod.splits)
                                       
lemma trace_mapping_of_list_sound: "sorted (map snd xs)  (x  set xs. finite (fst x))  i < length xs 
  xs ! i = (Γ (trace_of_list xs) i, τ (trace_of_list xs) i)"
  unfolding trace_of_list_def
  by transfer (auto simp: lookup_bulkload_Some)

subsection ‹Auxiliary results›

definition "sum_proofs f xs = sum_list (map f xs)"

lemma sum_proofs_empty[simp]: "sum_proofs f [] = 0"
  by (auto simp: sum_proofs_def)

lemma sum_proofs_fundef_cong[fundef_cong]: "(x. x  set xs  f x = f' x) 
  sum_proofs f xs = sum_proofs f' xs"
  by (induction xs) (auto simp: sum_proofs_def)

lemma sum_proofs_Cons:
  fixes f :: "'a  nat"
  shows "sum_proofs f (p # qs) = f p + sum_proofs f qs"
  by (auto simp: sum_proofs_def split: list.splits)

lemma sum_proofs_app:
  fixes f :: "'a  nat"
  shows "sum_proofs f (qs @ [p]) = f p + sum_proofs f qs"
  by (auto simp: sum_proofs_def split: list.splits)

context
  fixes w :: "'n  nat"
begin

function (sequential) s_pred :: "('n, 'd) sproof  nat" 
  and v_pred :: "('n, 'd) vproof  nat" where
  "s_pred (STT _) = 1"
| "s_pred (SEq_Const _ _ _) = 1"
| "s_pred (SPred _ r _) = w r"
| "s_pred (SNeg vp) = (v_pred vp) + 1"
| "s_pred (SOrL sp1) = (s_pred sp1) + 1"
| "s_pred (SOrR sp2) = (s_pred sp2) + 1"
| "s_pred (SAnd sp1 sp2) = (s_pred sp1) + (s_pred sp2) + 1"
| "s_pred (SImpL vp1) = (v_pred vp1) + 1"
| "s_pred (SImpR sp2) = (s_pred sp2) + 1"
| "s_pred (SIffSS sp1 sp2) = (s_pred sp1) + (s_pred sp2) + 1"
| "s_pred (SIffVV vp1 vp2) = (v_pred vp1) + (v_pred vp2) + 1"
| "s_pred (SExists _ _ sp) = (s_pred sp) + 1"
| "s_pred (SForall _ part) = (sum_proofs s_pred (vals part)) + 1"
| "s_pred (SPrev sp) = (s_pred sp) + 1"
| "s_pred (SNext sp) = (s_pred sp) + 1"
| "s_pred (SOnce _ sp) = (s_pred sp) + 1"
| "s_pred (SEventually _ sp) = (s_pred sp) + 1"
| "s_pred (SHistorically _ _ sps) = (sum_proofs s_pred sps) + 1"
| "s_pred (SHistoricallyOut _) = 1"
| "s_pred (SAlways _ _ sps) = (sum_proofs s_pred sps) + 1"
| "s_pred (SSince sp2 sp1s) = (sum_proofs s_pred (sp2 # sp1s)) + 1"
| "s_pred (SUntil sp1s sp2) = (sum_proofs s_pred (sp1s @ [sp2])) + 1"
| "v_pred (VFF _ ) = 1"
| "v_pred (VEq_Const _ _ _) = 1"
| "v_pred (VPred _ r _) = w r"
| "v_pred (VNeg sp) = (s_pred sp) + 1"
| "v_pred (VOr vp1 vp2) = ((v_pred vp1) + (v_pred vp2)) + 1"
| "v_pred (VAndL vp1) = (v_pred vp1) + 1"
| "v_pred (VAndR vp2) = (v_pred vp2) + 1"
| "v_pred (VImp sp1 vp2) = ((s_pred sp1) + (v_pred vp2)) + 1"
| "v_pred (VIffSV sp1 vp2) = ((s_pred sp1) + (v_pred vp2)) + 1"
| "v_pred (VIffVS vp1 sp2) = ((v_pred vp1) + (s_pred sp2)) + 1"
| "v_pred (VExists _ part) = (sum_proofs v_pred (vals part)) + 1"
| "v_pred (VForall _ _ vp) = (v_pred vp) + 1"
| "v_pred (VPrev vp) = (v_pred vp) + 1"
| "v_pred (VPrevZ) = 1"
| "v_pred (VPrevOutL _) = 1"
| "v_pred (VPrevOutR _) = 1"
| "v_pred (VNext vp) = (v_pred vp) + 1"
| "v_pred (VNextOutL _) = 1"
| "v_pred (VNextOutR _) = 1"
| "v_pred (VOnceOut _) = 1"
| "v_pred (VOnce _ _ vps) = (sum_proofs v_pred vps) + 1"
| "v_pred (VEventually _ _ vps) = (sum_proofs v_pred vps) + 1"
| "v_pred (VHistorically _ vp) = (v_pred vp) + 1"
| "v_pred (VAlways _ vp) = (v_pred vp) + 1"
| "v_pred (VSinceOut _) = 1"
| "v_pred (VSince _ vp1 vp2s) = (sum_proofs v_pred (vp1 # vp2s)) + 1"
| "v_pred (VSinceInf _ _ vp2s) = (sum_proofs v_pred vp2s) + 1"
| "v_pred (VUntil _ vp2s vp1) = (sum_proofs v_pred (vp2s @ [vp1])) + 1"
| "v_pred (VUntilInf _ _ vp2s) = (sum_proofs v_pred vp2s) + 1"
  by pat_completeness auto
termination
  by (relation "measure (case_sum size size)")
    (auto simp add: termination_simp)

definition p_pred :: "('n, 'd) proof  nat" where
  "p_pred = case_sum s_pred v_pred"

end

subsection constv_check_exec setup›

lemma ETP_minus_le_iff: "ETP σ (τ σ i - n)  j  δ σ i j  n"
  by (simp add: add.commute i_ETP_tau le_diff_conv)

lemma ETP_minus_gt_iff: "j < ETP σ (τ σ i - n)  δ σ i j > n"
  by (meson ETP_minus_le_iff leD le_less_linear)

lemma nat_le_iff_less:
  fixes n :: nat
  shows "(j  n)  (j = 0  j - 1 < n)"
  by auto

lemma ETP_minus_eq_iff: "j = ETP σ (τ σ i - n)  ((j = 0  n < δ σ i (j - 1))  δ σ i j  n)"
  unfolding eq_iff[of j "ETP σ (τ σ i - n)"] nat_le_iff_less[of j] ETP_minus_le_iff ETP_minus_gt_iff
  by auto

lemma LTP_minus_ge_iff: "τ σ 0 + n  τ σ i  j  LTP σ (τ σ i - n) 
  (case n of 0  δ σ j i = 0 | _  j  i  δ σ i j  n)"
  using τ_mono[of i j σ]
  by (fastforce simp add: i_LTP_tau le_diff_conv2 Suc_le_eq split: nat.splits)

lemma LTP_plus_ge_iff: "j  LTP σ (τ σ i + n)  δ σ j i  n"
  by (simp add: add.commute i_LTP_tau le_diff_conv trans_le_add2)

lemma LTP_minus_lt_if:
  assumes "j  i" "τ σ 0 + n  τ σ i" "δ σ i j < n"
  shows "LTP σ (τ σ i - n) < j"
proof -
  have not_in: "k  {ia. τ σ ia  τ σ i - n}" if "j  k" for k
    using assms τ_mono[OF that, of σ]
    by auto
  then have "{ia. τ σ ia  τ σ i - n}  {0..<j}"
    using not_le_imp_less
    by (clarsimp; blast)
  then have "finite {ia. τ σ ia  τ σ i - n}"
    using subset_eq_atLeast0_lessThan_finite
    by blast
  moreover have "0  {ia. τ σ ia  τ σ i - n}"
    using assms(2)
    by auto
  ultimately show ?thesis
    unfolding LTP_def
    by (metis Max_in not_in empty_iff not_le_imp_less)
qed

lemma LTP_minus_lt_iff:
  assumes "τ σ 0 + n  τ σ i"
  shows "LTP σ (τ σ i - n) < j  (if ¬ j  i  n = 0 then δ σ j i > 0 else δ σ i j < n)"
proof (cases "j  i")
  case True
  then show ?thesis
    using assms i_le_LTPi_minus[of σ n i] LTP_minus_lt_if[of j i σ n]
    by (cases n)
      (auto simp add: i_LTP_tau linorder_not_less Suc_le_eq dest!: tau_LTP_k[rotated])
next
  case False
  have delta: "δ σ i j = 0"
    using False
    by auto
  show ?thesis
  proof (cases n)
    case 0
    then show ?thesis
      using False assms
      by (metis add.right_neutral diff_is_0_eq diff_zero i_LTP_tau linorder_not_less)
  next
    case (Suc n')
    then show ?thesis
      using False assms
      by (cases i)
        (auto simp: Suc_le_eq not_le elim!: order.strict_trans[rotated] intro!: i_le_LTPi_minus)
  qed
qed

lemma LTP_minus_eq_iff:
  assumes "τ σ 0 + n  τ σ i"
  shows "j = LTP σ (τ σ i - n) 
  (case n of 0  i  j  δ σ j i = 0  δ σ (Suc j) j > 0
  | _  j  i  n  δ σ i j  δ σ i (Suc j) < n)"
proof (cases n)
  case 0
  show ?thesis
    using assms 0 i_LTP_tau[of σ "τ σ i" "LTP σ (τ σ i)"]
      i_LTP_tau[of σ "τ σ i" "Suc (LTP σ (τ σ i))"] i_LTP_tau[of σ "τ σ i" "j"]
      less_τD[of σ "(LTP σ (τ σ i))" "Suc j"]
    by (auto simp: i_le_LTPi not_le elim!: antisym dest!:
      order_antisym_conv[of "τ σ i" "τ σ j", THEN iffD1, rotated]
      order_antisym_conv[of "τ σ i" "τ σ (LTP σ (τ σ i))", THEN iffD1, rotated])
next
  case (Suc n')
  show ?thesis
    using assms
    by (simp add: Suc eq_iff[of j "LTP σ (τ σ i - Suc n')"] less_Suc_eq_le[of "LTP σ (τ σ i - Suc n')" j, symmetric] LTP_minus_ge_iff LTP_minus_lt_iff)
qed

lemma LTP_plus_eq_iff:
  shows "j = LTP σ (τ σ i + n)  (δ σ j i  n  δ σ (Suc j) i > n)"
  unfolding eq_iff[of j "LTP σ (τ σ i + n)"]
  by (meson LTP_plus_ge_iff linorder_not_less not_less_eq_eq)

lemma LTP_p_def: "τ σ 0 + left I  τ σ i  LTP_p σ i I = (case left I of 0  i | _  LTP σ (τ σ i - left I))"
  using i_le_LTPi by (auto simp: min_def i_LTP_tau split: nat.splits)

definition "check_upt_LTP_p σ I li xs i  (case xs of [] 
  (case left I of 0  i < li | Suc n 
    (if ¬ li  i  left I = 0 then 0 < δ σ li i else δ σ i li < left I))
  | _  xs = [li..<li + length xs] 
    (case left I of 0  li + length xs - 1 = i | Suc n 
      (li + length xs - 1  i  left I  δ σ i (li + length xs - 1)  δ σ i (li + length xs) < left I)))"

lemma check_upt_l_cong:
  assumes "j. j  max i li  τ σ j = τ σ' j"
  shows "check_upt_LTP_p σ I li xs i = check_upt_LTP_p σ' I li xs i"
proof -
  have "li + length ys  i  Suc n  δ σ' i (li + length ys) 
    (Suc (li + length ys))  i" for ys :: "nat list" and n
    by (cases "li + length ys = i") auto
  then show ?thesis
    using assms
    by (fastforce simp: check_upt_LTP_p_def Let_def simp del: upt.simps split: list.splits nat.splits)
qed

lemma check_upt_LTP_p_eq:
  assumes "τ σ 0 + left I  τ σ i"
  shows "xs = [li..<Suc (LTP_p σ i I)]  check_upt_LTP_p σ I li xs i"
proof -
  have "li + length xs = Suc (LTP_p σ i I)  li + length xs - 1 = LTP_p σ i I" if "xs  []"
    using that
    by (cases xs) auto
  then have "xs = [li..<Suc (LTP_p σ i I)]  (xs = []  LTP_p σ i I < li) 
    (xs  []  xs = [li..<li + length xs]  li + length xs - 1 = LTP_p σ i I)"
    by auto
  moreover have "  (xs = [] 
    (case left I of 0  i < li | Suc n 
      (if ¬ li  i  left I = 0 then 0 < δ σ li i else δ σ i li < left I))) 
    (xs  []  xs = [li..<li + length xs] 
    (case left I of 0  li + length xs - 1 = i | Suc n 
      (case left I of 0  i  li + length xs - 1 
        δ σ (li + length xs - 1) i = 0  0 < δ σ (Suc (li + length xs - 1)) (li + length xs - 1)
      | Suc n  li + length xs - 1  i 
        left I  δ σ i (li + length xs - 1)  δ σ i (Suc (li + length xs - 1)) < left I)))"
    using LTP_p_def[OF assms, symmetric]
    unfolding LTP_minus_lt_iff[OF assms, symmetric]
    unfolding LTP_minus_eq_iff[OF assms, symmetric]
    by (auto split: nat.splits)
  moreover have "  (case xs of [] 
    (case left I of 0  i < li | Suc n 
      (if ¬ li  i  left I = 0 then 0 < δ σ li i else δ σ i li < left I))
    | _  xs = [li..<li + length xs] 
      (case left I of 0  li + length xs - 1 = i | Suc n 
        (li + length xs - 1  i  left I  δ σ i (li + length xs - 1)  δ σ i (li + length xs) < left I)))"
    by (auto split: nat.splits list.splits)
  ultimately show ?thesis
    unfolding check_upt_LTP_p_def
    by simp
qed

lemma v_check_exec_Once_code[code]: "v_check_exec σ vs (Formula.Once I φ) vp = (case vp of
  VOnce i li vps 
    (case right I of   li = 0 | enat b  ((li = 0  b < δ σ i (li - 1))  δ σ i li  b)) 
     τ σ 0 + left I  τ σ i
     check_upt_LTP_p σ I li (map v_at vps) i  Ball (set vps) (v_check_exec σ vs φ)
  | VOnceOut i  τ σ i < τ σ 0 + left I 
  | _  False)"
  by (auto simp: Let_def check_upt_LTP_p_eq ETP_minus_le_iff ETP_minus_eq_iff split: vproof.splits enat.splits simp del: upt_Suc)

lemma s_check_exec_Historically_code[code]: "s_check_exec σ vs (Formula.Historically I φ) vp = (case vp of
  SHistorically i li vps 
    (case right I of   li = 0 | enat b  ((li = 0  b < δ σ i (li - 1))  δ σ i li  b))
     τ σ 0 + left I  τ σ i
     check_upt_LTP_p σ I li (map s_at vps) i  Ball (set vps) (s_check_exec σ vs φ)
  | SHistoricallyOut i  τ σ i < τ σ 0 + left I 
  | _  False)"
  by (auto simp: Let_def check_upt_LTP_p_eq ETP_minus_le_iff ETP_minus_eq_iff split: sproof.splits enat.splits simp del: upt_Suc)

lemma v_check_exec_Since_code[code]: "v_check_exec σ vs (Formula.Since φ I ψ) vp = (case vp of
  VSince i vp1 vp2s 
    let j = v_at vp1 in
    (case right I of   True | enat b  δ σ i j  b)  j  i
     τ σ 0 + left I  τ σ i
     check_upt_LTP_p σ I j (map v_at vp2s) i
     v_check_exec σ vs φ vp1  Ball (set vp2s) (v_check_exec σ vs ψ)
  | VSinceInf i li vp2s 
    (case right I of   li = 0 | enat b  ((li = 0  b < δ σ i (li - 1))  δ σ i li  b)) 
    τ σ 0 + left I  τ σ i 
     check_upt_LTP_p σ I li (map v_at vp2s) i  Ball (set vp2s) (v_check_exec σ vs ψ)
  | VSinceOut i  τ σ i < τ σ 0 + left I 
  | _  False)"
  by (auto simp: Let_def check_upt_LTP_p_eq ETP_minus_le_iff ETP_minus_eq_iff split: vproof.splits enat.splits simp del: upt_Suc)

lemma ETP_f_le_iff: "max i (ETP σ (τ σ i + a))  j  i  j  δ σ j i  a"
  by (metis add.commute max.bounded_iff τ_mono i_ETP_tau le_diff_conv2)

lemma ETP_f_ge_iff: "j  max i (ETP σ (τ σ i + n))  (case n of 0  j  i
  | Suc n'  (case j of 0  True | Suc j'  δ σ j' i < n))"
proof (cases n)
  case 0
  then show ?thesis
    by (auto simp: max_def) (metis i_ge_etpi verit_la_disequality)
next
  case (Suc n')
  have max: "max i (ETP σ (τ σ i + n)) = ETP σ (τ σ i + n)"
    by (auto simp: max_def Suc)
       (metis Groups.ab_semigroup_add_class.add.commute ETP_ge less_or_eq_imp_le plus_1_eq_Suc)
  have "j  max i (ETP σ (τ σ i + n))  (ia. τ σ i + n  τ σ ia  j  ia)"
    unfolding max
    unfolding ETP_def
    by (meson LeastI_ex Least_le order.trans ex_le_τ)
  moreover have "  (case j of 0  True | Suc j'  ¬τ σ i + n  τ σ j')"
    by (auto split: nat.splits) (meson i_ETP_tau le_trans not_less_eq_eq)
  moreover have "  (case j of 0  True | Suc j'  δ σ j' i < n)"
    by (auto simp: Suc split: nat.splits)
  ultimately show ?thesis
    by (auto simp: Suc)
qed

definition "check_upt_ETP_f σ I i xs hi  (let j = Suc hi - length xs in
  (case xs of []  (case left I of 0  Suc hi  i | Suc n'  δ σ hi i < left I)
  | _  (xs = [j..<Suc hi] 
  (case left I of 0  j  i | Suc n' 
  (case j of 0  True | Suc j'  δ σ j' i < left I)) 
  i  j  left I  δ σ j i)))"

lemma check_upt_lu_cong:
  assumes "j. min i hi  j  j  max i hi  τ σ j = τ σ' j"
  shows "check_upt_ETP_f σ I i xs hi = check_upt_ETP_f σ' I i xs hi"
  using assms
  unfolding check_upt_ETP_f_def
  by (auto simp add: Let_def le_Suc_eq split: list.splits nat.splits)

lemma check_upt_ETP_f_eq: "xs = [ETP_f σ i I..<Suc hi]  check_upt_ETP_f σ I i xs hi"
proof -
  have F1: "(case left I of 0  Suc hi  i | Suc n'  δ σ hi i < left I) =
    (Suc hi  ETP_f σ i I)"
    unfolding ETP_f_ge_iff[where ?j="Suc hi" and ?n="left I"]
    by (auto split: nat.splits)
  have "xs = [ETP_f σ i I..<Suc hi]  (let j = Suc hi - length xs in
    (xs = []  (case left I of 0  Suc hi  i | Suc n'  δ σ hi i < left I)) 
    (xs  []  xs = [j..<Suc hi] 
    (case left I of 0  j  i | Suc n' 
    (case j of 0  True | Suc j'  δ σ j' i < left I)) 
    i  j  left I  δ σ j i))"
    unfolding F1
    unfolding Let_def
    unfolding ETP_f_ge_iff[where ?n="left I", symmetric]
    unfolding ETP_f_le_iff[symmetric]
    unfolding eq_iff[of _ "ETP_f σ i I", symmetric]
    by auto
  moreover have "  (let j = Suc hi - length xs in
    (case xs of []  (case left I of 0  Suc hi  i | Suc n'  δ σ hi i < left I)
    | _  (xs = [j..<Suc hi] 
    (case left I of 0  j  i | Suc n' 
    (case j of 0  True | Suc j'  δ σ j' i < left I)) 
    i  j  left I  δ σ j i)))"
    by (auto simp: Let_def split: list.splits)
  finally show ?thesis
    unfolding check_upt_ETP_f_def .
qed

lemma v_check_exec_Eventually_code[code]: "v_check_exec σ vs (Formula.Eventually I φ) vp = (case vp of
  VEventually i hi vps 
    (case right I of   False | enat b  (δ σ hi i  b  b < δ σ (Suc hi) i)) 
     check_upt_ETP_f σ I i (map v_at vps) hi  Ball (set vps) (v_check_exec σ vs φ)
  | _  False)"
  by (auto simp: Let_def LTP_plus_ge_iff LTP_plus_eq_iff check_upt_ETP_f_eq simp del: upt_Suc
      split: vproof.splits enat.splits)

lemma s_check_exec_Always_code[code]: "s_check_exec σ vs (Formula.Always I φ) sp = (case sp of
  SAlways i hi sps 
    (case right I of   False | enat b  (δ σ hi i  b  b < δ σ (Suc hi) i)) 
     check_upt_ETP_f σ I i (map s_at sps) hi  Ball (set sps) (s_check_exec σ vs φ)
  | _  False)"
  by (auto simp: Let_def LTP_plus_ge_iff LTP_plus_eq_iff check_upt_ETP_f_eq simp del: upt_Suc
      split: sproof.splits enat.splits)

lemma v_check_exec_Until_code[code]: "v_check_exec σ vs (Formula.Until φ I ψ) vp = (case vp of
  VUntil i vp2s vp1 
    let j = v_at vp1 in 
    (case right I of   True | enat b  j < LTP_f σ i b)
     i  j  check_upt_ETP_f σ I i (map v_at vp2s) j
     v_check_exec σ vs φ vp1  Ball (set vp2s) (v_check_exec σ vs ψ)
 | VUntilInf i hi vp2s 
    (case right I of   False | enat b  (δ σ hi i  b  b < δ σ (Suc hi) i)) 
     check_upt_ETP_f σ I i (map v_at vp2s) hi  Ball (set vp2s) (v_check_exec σ vs ψ)
 | _  False)"
  by (auto simp: Let_def LTP_plus_ge_iff LTP_plus_eq_iff check_upt_ETP_f_eq simp del: upt_Suc
      split: vproof.splits enat.splits)

subsection‹ETP/LTP setup›

lemma ETP_aux: "¬ t  τ σ i  i  (LEAST i. t  τ σ i)"
  by (meson LeastI_ex τ_mono ex_le_τ nat_le_linear order_trans)

function ETP_rec where
  "ETP_rec σ t i = (if τ σ i  t then i else ETP_rec σ t (i + 1))"
  by pat_completeness auto
termination
  using ETP_aux
  by (relation "measure (λ(σ, t, i). Suc (ETP σ t) - i)")
    (fastforce simp: ETP_def)+

lemma ETP_rec_sound: "ETP_rec σ t j = (LEAST i. i  j  t  τ σ i)"
proof (induction σ t j rule: ETP_rec.induct)
  case (1 σ t i)
  show ?case
  proof (cases "τ σ i  t")
    case True
    then show ?thesis
      by simp (metis (no_types, lifting) Least_equality order_refl)
  next
    case False
    then show ?thesis
      using 1[OF False]
      by (simp add: ETP_rec.simps[of _ _ i] del: ETP_rec.simps)
         (metis Suc_leD le_antisym not_less_eq_eq)
  qed
qed

lemma ETP_code[code]: "ETP σ t = ETP_rec σ t 0"
  using ETP_rec_sound[of σ t 0]
  by (auto simp: ETP_def)

lemma LTP_aux:
  assumes "τ σ (Suc i)  t"
  shows "i  Max {i. τ σ i  t}"
proof -
  have "finite {i. τ σ i  t}"
    by (smt (verit, del_insts) τ_mono finite_nat_set_iff_bounded_le i_LTP_tau le0 le_trans mem_Collect_eq) 
  moreover have "i  {i. τ σ i  t}"
    using le_trans[OF τ_mono[of i "Suc i" σ] assms]
    by auto
  ultimately show ?thesis
    by (rule Max_ge)
qed

function (sequential) LTP_rec where
  "LTP_rec σ t i = (if τ σ (Suc i)  t then LTP_rec σ t (i + 1) else i)"
  by pat_completeness auto
termination
  using LTP_aux
  by (relation "measure (λ(σ, t, i). Suc (LTP σ t) - i)") (fastforce simp: LTP_def)+

lemma LTP_rec_sound: "LTP_rec σ t j = Max ({i. i  j  (τ σ i)  t}  {j})"
proof (induction σ t j rule: LTP_rec.induct)
  case (1 σ t j)
  have fin: "finite {i. j  i  τ σ i  t}"
    by (smt (verit, del_insts) τ_mono finite_nat_set_iff_bounded_le i_LTP_tau le0 le_trans
        mem_Collect_eq)
  show ?case
  proof (cases "τ σ (Suc j)  t")
    case True
    have diffI: "{i. Suc j  i  τ σ i  t} = {i. j  i  τ σ i  t} - {j}"
      by auto
    show ?thesis
      using 1[OF True] True fin
      by (auto simp del: LTP_rec.simps simp add: LTP_rec.simps[of _ _ j] diffI intro: max_aux)
  next
    case False
    then show ?thesis
      using fin
      by (auto simp: not_le intro!: Max_insert2[symmetric]
        dest!: order.strict_trans1 less_τD)
  qed
qed

lemma LTP_code[code]: "LTP σ t = (if t < τ σ 0
  then Code.abort (STR ''LTP: undefined'') (λ_. LTP σ t)
  else LTP_rec σ t 0)"
  using LTP_rec_sound[of σ t 0]
  by (auto simp: LTP_def insert_absorb simp del: LTP_rec.simps)

lemma map_part_code[code]: "Rep_part (map_part f xs) = map (map_prod id f) (Rep_part xs)"
  using Rep_part[of xs]
  by (auto simp: map_part_def intro!: Abs_part_inverse)

lemma coset_subset_set_code[code]:
  "(List.coset (xs :: _ :: universe list)  set ys) = (case universe of None  False
  | Some zs  z  set zs. z  set xs  z  set ys)"
  using finite_compl finite_subset
  by (auto split: option.splits dest!: infinite finite)

lemma is_empty_coset[code]: "Set.is_empty (List.coset (xs :: _ :: universe list)) =
  (case universe of None  False
  | Some zs  z  set zs. z  set xs)"
  using coset_subset_set_code[of xs] by (auto simp: Set.is_empty_def split: option.splits dest: infinite finite)

subsection ‹Exported functions›

type_synonym name = string8

declare Formula.future_bounded.simps[code]

definition collect_paths :: "('n, 'd :: {default, linorder}) trace  ('n, 'd) formula  ('n, 'd) expl  'd set list set option" where
  "collect_paths σ φ e = (if (distinct_paths e  check_all_aux σ (λ_. UNIV) φ e) then None else Some (collect_paths_aux {[]} σ (λ_. UNIV) φ e))"

definition check :: "(name, event_data) trace  (name, event_data) formula  (name, event_data) expl  bool" where
  "check = check_all"

definition collect_paths_specialized :: "(name, event_data) trace  (name, event_data) formula  (name, event_data) expl  event_data set list set option" where
  "collect_paths_specialized = collect_paths"

definition trace_of_list_specialized :: "((name × event_data list) set × nat) list  (name, event_data) trace" where
  "trace_of_list_specialized xs = trace_of_list xs"

definition specialized_set :: "(name × event_data list) list  (name × event_data list) set" where
  "specialized_set = set"

definition ed_set :: "event_data list  event_data set" where
  "ed_set = set"

definition sum_nat :: "nat  nat  nat" where
  "sum_nat m n = m + n"

definition sub_nat :: "nat  nat  nat" where
  "sub_nat m n = m - n"

lift_definition abs_part :: "(event_data set × 'a) list  (event_data, 'a) part" is
  "λxs.
   let Ds = map fst xs in
   if {}  set Ds
    (D  set Ds. E  set Ds. D  E  D  E  {})
    ¬ distinct Ds
    (D  set Ds. D)  UNIV then [(UNIV, undefined)] else xs"
  by (auto simp: partition_on_def disjoint_def)

export_code interval enat nat_of_integer integer_of_nat
  STT Formula.TT Inl EInt Formula.Var Leaf set part_hd sum_nat sub_nat subsvals
  check trace_of_list_specialized specialized_set ed_set abs_part 
  collect_paths_specialized
  in OCaml module_name Checker file_prefix "checker"

(*<*)
end
(*>*)