(*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *)

theory WPC
imports "WP_Pre"
keywords "wpc_setup" :: thy_decl

begin

definition
  wpc_helper :: "(('a \<Rightarrow> bool) \<times> 'b set)
                 \<Rightarrow> (('a \<Rightarrow> bool) \<times> 'b set) \<Rightarrow> bool \<Rightarrow> bool" where
 "wpc_helper \<equiv> \<lambda>(P, P') (Q, Q') R. ((\<forall>s. P s \<longrightarrow> Q s) \<and> P' \<subseteq> Q') \<longrightarrow> R"

lemma wpc_conj_process:
  "\<lbrakk> wpc_helper (P, P') (A, A') C; wpc_helper (P, P') (B, B') D \<rbrakk>
       \<Longrightarrow> wpc_helper (P, P') (\<lambda>s. A s \<and> B s, A' \<inter> B') (C \<and> D)"
  by (clarsimp simp add: wpc_helper_def)

lemma wpc_all_process:
  "\<lbrakk> \<And>x. wpc_helper (P, P') (Q x, Q' x) (R x) \<rbrakk>
       \<Longrightarrow> wpc_helper (P, P') (\<lambda>s. \<forall>x. Q x s, {s. \<forall>x. s \<in> Q' x}) (\<forall>x. R x)"
  by (clarsimp simp: wpc_helper_def subset_iff)

lemma wpc_all_process_very_weak:
  "\<lbrakk> \<And>x. wpc_helper (P, P') (Q, Q') (R x) \<rbrakk> \<Longrightarrow> wpc_helper (P, P') (Q, Q') (\<forall>x. R x)"
  by (clarsimp simp: wpc_helper_def)

lemma wpc_imp_process:
  "\<lbrakk> Q \<Longrightarrow> wpc_helper (P, P') (R, R') S \<rbrakk>
        \<Longrightarrow> wpc_helper (P, P') (\<lambda>s. Q \<longrightarrow> R s, {s. Q \<longrightarrow> s \<in> R'}) (Q \<longrightarrow> S)"
  by (clarsimp simp add: wpc_helper_def subset_iff)

lemma wpc_imp_process_weak:
  "\<lbrakk> wpc_helper (P, P') (R, R') S \<rbrakk> \<Longrightarrow> wpc_helper (P, P') (R, R') (Q \<longrightarrow> S)"
  by (clarsimp simp add: wpc_helper_def)

lemmas wpc_processors
  = wpc_conj_process wpc_all_process wpc_imp_process
lemmas wpc_weak_processors
  = wpc_conj_process wpc_all_process wpc_imp_process_weak
lemmas wpc_vweak_processors
  = wpc_conj_process wpc_all_process_very_weak wpc_imp_process_weak

lemma wpc_helperI:
  "wpc_helper (P, P') (P, P') Q \<Longrightarrow> Q"
  by (simp add: wpc_helper_def)

lemma wpc_foo: "\<lbrakk> undefined x; False \<rbrakk> \<Longrightarrow> P x"
  by simp

lemma foo:
  assumes foo_elim: "\<And>P Q h. \<lbrakk> foo Q h; \<And>s. P s \<Longrightarrow> Q s \<rbrakk> \<Longrightarrow> foo P h"
  shows
  "\<lbrakk> \<And>x. foo (Q x) (f x); foo R g \<rbrakk> \<Longrightarrow>
      foo (\<lambda>s. (\<forall>x. Q x s) \<and> (y = None \<longrightarrow> R s))
         (case y of Some x \<Rightarrow> f x | None \<Rightarrow> g)"
  by (auto split: option.split intro: foo_elim)

ML \<open>

signature WPC = sig
  exception WPCFailed of string * term list * thm list;

  val foo_thm: thm;
  val iffd2_thm: thm;
  val wpc_helperI: thm;

  val instantiate_concl_pred: Proof.context -> cterm -> thm -> thm;

  val detect_term: Proof.context -> int -> thm -> cterm -> (cterm * term) list;
  val detect_terms: Proof.context -> (term -> cterm -> thm -> int -> tactic) -> int -> tactic;

  val split_term: thm list -> Proof.context -> term -> cterm -> thm -> int -> tactic;

  val wp_cases_tac: thm list -> Proof.context -> int -> tactic;
  val wp_debug_tac: thm list -> Proof.context -> int -> tactic;
  val wp_cases_method: thm list -> (Proof.context -> Method.method) context_parser;

end;

structure WPCPredicateAndFinals = Theory_Data
(struct
    type T = (cterm * thm) list
    val empty = []
    fun merge (xs, ys) =
        (* Order of predicates is important, so we can't reorder *)
        let val tms = map (Thm.term_of o fst) xs
            fun inxs x = exists (fn y => x aconv y) tms
            val ys' = filter (not o inxs o Thm.term_of o fst) ys
        in
            xs @ ys'
        end
end);

structure WeakestPreCases : WPC =
struct

exception WPCFailed of string * term list * thm list;

val iffd2_thm = @{thm "iffD2"};
val wpc_helperI = @{thm "wpc_helperI"};
val foo_thm = @{thm "wpc_foo"};

(* it looks like cterm_instantiate would do the job better,
   but this handles the case where ?'a must be instantiated
   to ?'a \<times> ?'b *)
fun instantiate_concl_pred ctxt pred thm =
let
  val get_concl_pred  = (fst o strip_comb o HOLogic.dest_Trueprop o Thm.concl_of);
  val get_concl_predC = (Thm.cterm_of ctxt o get_concl_pred);

  val get_pred_tvar   = domain_type o Thm.typ_of o Thm.ctyp_of_cterm;
  val thm_pred        = get_concl_predC thm;
  val thm_pred_tvar   = Term.dest_TVar (get_pred_tvar thm_pred);
  val pred_tvar       = Thm.ctyp_of ctxt (get_pred_tvar pred);

  val thm2            = Thm.instantiate (TVars.make [(thm_pred_tvar, pred_tvar)], Vars.empty) thm;

  val thm2_pred       = Term.dest_Var (get_concl_pred thm2);
in
  Thm.instantiate (TVars.empty, Vars.make [(thm2_pred, pred)]) thm2
end;

fun detect_term ctxt n thm tm =
let
  val foo_thm_tm   = instantiate_concl_pred ctxt tm foo_thm;
  val matches      = resolve_tac ctxt [foo_thm_tm] n thm;
  val outcomes     = Seq.list_of matches;
  val get_goalterm = (HOLogic.dest_Trueprop o Logic.strip_assums_concl
                       o Envir.beta_eta_contract o hd o Thm.prems_of);
  val get_argument = hd o snd o strip_comb;
in
  map (pair tm o get_argument o get_goalterm) outcomes
end;

fun detect_terms ctxt tactic2 n thm =
let
  val pfs           = WPCPredicateAndFinals.get (Proof_Context.theory_of ctxt);
  val detects       = map (fn (tm, rl) => (detect_term ctxt n thm tm, rl)) pfs;
  val detects2      = filter (not o null o fst) detects;
  val ((pred, arg), fin)   = case detects2 of
                                [] => raise WPCFailed ("detect_terms: no match", [], [thm])
                               | ((d3, fin) :: _) => (hd d3, fin)
in
  tactic2 arg pred fin n thm
end;

(* give each rule in the list one possible resolution outcome *)
fun resolve_each_once_tac ctxt thms i
    = fold (curry (op APPEND'))
        (map (DETERM oo resolve_tac ctxt o single) thms)
        (K no_tac) i

fun resolve_single_tac ctxt rules n thm =
  case Seq.chop 2 (resolve_each_once_tac ctxt rules n thm)
  of ([], _) => raise WPCFailed
                        ("resolve_single_tac: no rules could apply",
                         [], thm :: rules)
   | (_ :: _ :: _, _) => raise WPCFailed
                        ("resolve_single_tac: multiple rules applied",
                         [], thm :: rules)
   | ([x], _) => Seq.single x;

fun split_term processors ctxt target pred fin =
let
  val hdTarget      = head_of target;
  val (constNm, _)  = dest_Const hdTarget handle TERM (_, tms)
                       => raise WPCFailed ("split_term: couldn't dest_Const", tms, []);
  val split = case (Ctr_Sugar.ctr_sugar_of_case ctxt constNm) of
      SOME sugar => #split sugar
    | _ => raise WPCFailed ("split_term: not a case", [hdTarget], []);
  val subst         = split RS iffd2_thm;
  val subst2        = instantiate_concl_pred ctxt pred subst;
in
 (resolve_tac ctxt [subst2])
   THEN'
 (resolve_tac ctxt [wpc_helperI])
   THEN'
 (REPEAT_ALL_NEW (resolve_tac ctxt processors)
    THEN_ALL_NEW
  resolve_single_tac ctxt [fin])
end;

(* n.b. need to concretise the lazy sequence via a list to ensure exceptions
  have been raised already and catch them *)
fun wp_cases_tac processors ctxt n thm =
  detect_terms ctxt (split_term processors ctxt) n thm
      |> Seq.list_of |> Seq.of_list
    handle WPCFailed _ => no_tac thm;

fun wp_debug_tac processors ctxt n thm =
  detect_terms ctxt (split_term processors ctxt) n thm
      |> Seq.list_of |> Seq.of_list
    handle WPCFailed e => (warning (@{make_string} (WPCFailed e)); no_tac thm);

fun wp_cases_method processors = Scan.succeed (fn ctxt =>
  Method.SIMPLE_METHOD' (wp_cases_tac processors ctxt));

local structure P = Parse and K = Keyword in

fun add_wpc tm thm lthy = let
  val ctxt = Local_Theory.target_of lthy
  val tm' = (Syntax.read_term ctxt tm) |> Thm.cterm_of ctxt o Logic.varify_global
  val thm' = Proof_Context.get_thm ctxt thm
in
  Local_Theory.background_theory (WPCPredicateAndFinals.map (fn xs => (tm', thm') :: xs)) lthy
end;

val _ =
    Outer_Syntax.command
        @{command_keyword "wpc_setup"}
        "Add wpc stuff"
        (P.term -- P.name >> (fn (tm, thm) => Toplevel.local_theory NONE NONE (add_wpc tm thm)))

end;
end;

\<close>

ML \<open>

val wp_cases_tactic_weak = WeakestPreCases.wp_cases_tac @{thms wpc_weak_processors};
val wp_cases_method_strong = WeakestPreCases.wp_cases_method @{thms wpc_processors};
val wp_cases_method_weak   = WeakestPreCases.wp_cases_method @{thms wpc_weak_processors};
val wp_cases_method_vweak  = WeakestPreCases.wp_cases_method @{thms wpc_vweak_processors};

\<close>

method_setup wpc0 = \<open>wp_cases_method_strong\<close>
  "case splitter for weakest-precondition proofs"

method_setup wpcw0 = \<open>wp_cases_method_weak\<close>
  "weak-form case splitter for weakest-precondition proofs"

method wpc = (wp_pre, wpc0)
method wpcw = (wp_pre, wpcw0)

definition
  wpc_test :: "'a set \<Rightarrow> ('a \<times> 'b) set \<Rightarrow> 'b set \<Rightarrow> bool"
  where
 "wpc_test P R S \<equiv> (R `` P) \<subseteq> S"

lemma wpc_test_weaken:
  "\<lbrakk> wpc_test Q R S; P \<subseteq> Q \<rbrakk> \<Longrightarrow> wpc_test P R S"
  by (simp add: wpc_test_def, blast)

lemma wpc_helper_validF:
  "wpc_test Q' R S \<Longrightarrow> wpc_helper (P, P') (Q, Q') (wpc_test P' R S)"
  by (simp add: wpc_test_def wpc_helper_def, blast)

setup \<open>
let
  val tm  = Thm.cterm_of @{context} (Logic.varify_global @{term "\<lambda>R. wpc_test P R S"});
  val thm = @{thm wpc_helper_validF};
in
  WPCPredicateAndFinals.map (fn xs => (tm, thm) :: xs)
end
\<close>

lemma set_conj_Int_simp:
  "{s \<in> S. P s} = S \<inter> {s. P s}"
  by auto

lemma case_options_weak_wp:
  "\<lbrakk> wpc_test P R S; \<And>x. wpc_test P' (R' x) S \<rbrakk>
    \<Longrightarrow> wpc_test (P \<inter> P') (case opt of None \<Rightarrow> R | Some x \<Rightarrow> R' x) S"
  apply (rule wpc_test_weaken)
   apply wpcw
    apply assumption
   apply assumption
  apply simp
  done

end


header {* Lift monad *}

theory Lift_Monad
imports Monad
begin

subsection {* Type definition *}

tycondef 'a\<cdot>lifted = Lifted (lazy "'a")

lemma coerce_lifted_abs [simp]: "coerce\<cdot>(lifted_abs\<cdot>x) = lifted_abs\<cdot>(coerce\<cdot>x)"
apply (simp add: lifted_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_lifted)
done

lemma coerce_Lifted [simp]: "coerce\<cdot>(Lifted\<cdot>x) = Lifted\<cdot>(coerce\<cdot>x)"
unfolding Lifted_def by simp

lemma fmapU_lifted_simps [simp]:
  "fmapU\<cdot>f\<cdot>(\<bottom>::udom\<cdot>lifted) = \<bottom>"
  "fmapU\<cdot>f\<cdot>(Lifted\<cdot>x) = Lifted\<cdot>(f\<cdot>x)"
unfolding fmapU_lifted_def lifted_map_def fix_const
apply simp
apply (simp add: Lifted_def)
done

subsection {* Class instance proofs *}

instance lifted :: "functor"
  by default (induct_tac xs rule: lifted.induct, simp_all)

instantiation lifted :: monad
begin

fixrec bindU_lifted :: "udom\<cdot>lifted \<rightarrow> (udom \<rightarrow> udom\<cdot>lifted) \<rightarrow> udom\<cdot>lifted"
  where "bindU_lifted\<cdot>(Lifted\<cdot>x)\<cdot>k = k\<cdot>x"

lemma bindU_lifted_strict [simp]: "bindU\<cdot>\<bottom>\<cdot>k = (\<bottom>::udom\<cdot>lifted)"
by fixrec_simp

definition returnU_lifted_def:
  "returnU = Lifted"

instance proof
  fix x :: "udom"
  fix f :: "udom \<rightarrow> udom"
  fix h k :: "udom \<rightarrow> udom\<cdot>lifted"
  fix xs :: "udom\<cdot>lifted"
  show "fmapU\<cdot>f\<cdot>xs = bindU\<cdot>xs\<cdot>(\<Lambda> x. returnU\<cdot>(f\<cdot>x))"
    by (induct xs rule: lifted.induct, simp_all add: returnU_lifted_def)
  show "bindU\<cdot>(returnU\<cdot>x)\<cdot>k = k\<cdot>x"
    by (simp add: returnU_lifted_def)
  show "bindU\<cdot>(bindU\<cdot>xs\<cdot>h)\<cdot>k = bindU\<cdot>xs\<cdot>(\<Lambda> x. bindU\<cdot>(h\<cdot>x)\<cdot>k)"
    by (induct xs rule: lifted.induct) simp_all
qed

end

subsection {* Transfer properties to polymorphic versions *}

lemma fmap_lifted_simps [simp]:
  "fmap\<cdot>f\<cdot>(\<bottom>::'a\<cdot>lifted) = \<bottom>"
  "fmap\<cdot>f\<cdot>(Lifted\<cdot>x) = Lifted\<cdot>(f\<cdot>x)"
unfolding fmap_def by simp_all

lemma bind_lifted_simps [simp]:
  "bind\<cdot>(\<bottom>::'a\<cdot>lifted)\<cdot>f = \<bottom>"
  "bind\<cdot>(Lifted\<cdot>x)\<cdot>f = f\<cdot>x"
unfolding bind_def by simp_all

lemma return_lifted_def: "return = Lifted"
unfolding return_def returnU_lifted_def
by (simp add: coerce_cfun cfcomp1 eta_cfun)

lemma join_lifted_simps [simp]:
  "join\<cdot>(\<bottom>::'a\<cdot>lifted\<cdot>lifted) = \<bottom>"
  "join\<cdot>(Lifted\<cdot>xs) = xs"
unfolding join_def by simp_all

end


(*
 * Copyright 2014, NICTA
 *
 * This software may be distributed and modified according to the terms of
 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
 * See "LICENSE_BSD2.txt" for details.
 *
 * @TAG(NICTA_BSD)
 *)

(* License: BSD, terms see file ./LICENSE *)

theory Addr_Type
imports "HOL-Word.Word"
begin

type_synonym addr_bitsize = "32"
type_synonym addr = "addr_bitsize word"
definition addr_bitsize :: nat where "addr_bitsize \<equiv> 32"
definition addr_align :: nat where "addr_align \<equiv> 2"
declare addr_align_def[simp]

definition addr_card :: nat where
  "addr_card \<equiv> card (UNIV::addr set)"

declare addr_bitsize_def[simp]

lemma addr_card:
  "addr_card = 2^addr_bitsize"
  by (simp add: addr_card_def card_word)

lemma len_of_addr_card:
  "2 ^ len_of TYPE(addr_bitsize) = addr_card"
  by (simp add: addr_card)

lemma of_nat_addr_card [simp]:
  "of_nat addr_card = (0::addr)"
  by (simp add: addr_card)

end


theory RangesTest imports Ranges begin

definition inst_codes :: ranges where
"inst_codes = mk_ranges [(0, 256)]"
declare inst_codes_def [simp]

typedef inst = "set_of_ranges inst_codes"
proof-
  have "(0 :: nat) \<in> {n . n < 256}" by auto
  thus "\<exists> (x :: nat) . x \<in> set_of_ranges inst_codes"
    unfolding inst_codes_def
    apply(transfer; auto)
    apply(rule_tac x = 0 in exI)
    apply(auto)
    done
qed
setup_lifting type_definition_inst

(* stop *)
definition stop_codes  :: "ranges"  where
"stop_codes = mk_ranges[(0, 1)]"
declare stop_codes_def [simp add]
typedef stop_inst = "set_of_ranges stop_codes"
  unfolding stop_codes_def by (transfer; auto)
setup_lifting type_definition_stop_inst

lift_definition STOP_INST :: "stop_inst \<Rightarrow> inst" is id
  unfolding stop_codes_def inst_codes_def
  by(transfer; auto)

(* arith*)
definition arith_codes :: "ranges" where
"arith_codes = mk_ranges [(0x01, 0x0c)]"

declare arith_codes_def [simp add]
typedef arith_inst = "set_of_ranges arith_codes"
  unfolding arith_codes_def
  apply(transfer)
  apply(auto)
  done

setup_lifting type_definition_arith_inst

lift_definition ARITH_INST :: "arith_inst \<Rightarrow> inst" is id
  unfolding arith_codes_def inst_codes_def
  by(transfer; auto)

(* bits *)
definition bits_codes :: ranges where
"bits_codes = mk_ranges [(0x10, 0x1e)]"

declare bits_codes_def [simp add]
typedef bits_inst = "set_of_ranges bits_codes"
  unfolding bits_codes_def
  by(transfer; auto)
setup_lifting type_definition_bits_inst

lift_definition BITS_INST :: "bits_inst => inst" is id
  unfolding bits_codes_def inst_codes_def
  by(transfer; auto)

definition keccak256_codes :: "ranges" where
"keccak256_codes = mk_ranges [(0x20, 0x21)]"

declare keccak256_codes_def [simp add]
typedef keccak256_inst = "set_of_ranges keccak256_codes"
  unfolding keccak256_codes_def
  by(transfer; auto)
setup_lifting type_definition_keccak256_inst

lift_definition KECCAK256_INST :: "keccak256_inst => inst" is id
  unfolding keccak256_codes_def inst_codes_def
  by(transfer; auto)

definition tx_data_codes :: "ranges" where
"tx_data_codes = mk_ranges [(0x30, 0x40)]"

declare tx_data_codes_def [simp add]
typedef tx_data_inst = "set_of_ranges tx_data_codes"
  unfolding tx_data_codes_def
  by(transfer; auto)
setup_lifting type_definition_tx_data_inst

lift_definition TX_DATA_INST :: "tx_data_inst => inst" is id
  unfolding tx_data_codes_def inst_codes_def
  by(transfer; auto)

definition chain_data_codes :: "ranges" where
"chain_data_codes = mk_ranges [(0x40, 0x48)]"

declare chain_data_codes_def [simp add]
typedef chain_data_inst = "set_of_ranges chain_data_codes"
  unfolding chain_data_codes_def
  by(transfer; auto)
setup_lifting type_definition_chain_data_inst

lift_definition CHAIN_DATA_INST :: "chain_data_inst => inst" is id
  unfolding chain_data_codes_def inst_codes_def
  by(transfer; auto)

definition state_control_codes :: "ranges" where
"state_control_codes = mk_ranges [(0x50, 0x5c)]"

declare state_control_codes_def [simp add]
typedef state_control_inst = "set_of_ranges state_control_codes"
  unfolding state_control_codes_def
  by(transfer; auto)
setup_lifting type_definition_state_control_inst

lift_definition STATE_CONTROL_INST :: "state_control_inst => inst" is id
  unfolding state_control_codes_def inst_codes_def
  by(transfer; auto)

definition push_codes :: "ranges" where
"push_codes = mk_ranges [(0x60, 0x80)]"

declare push_codes_def [simp add]
typedef push_inst = "set_of_ranges push_codes"
  unfolding push_codes_def
  by(transfer; auto)
setup_lifting type_definition_push_inst

lift_definition PUSH_INST :: "push_inst => inst" is id
  unfolding push_codes_def inst_codes_def
  by(transfer; auto)

definition dup_codes :: "ranges" where
"dup_codes = mk_ranges [(0x80, 0x90)]"

declare dup_codes_def [simp add]
typedef dup_inst = "set_of_ranges dup_codes"
  unfolding dup_codes_def
  by(transfer; auto)
setup_lifting type_definition_dup_inst

lift_definition DUP_INST :: "dup_inst => inst" is id
  unfolding dup_codes_def inst_codes_def
  by(transfer; auto)

definition swap_codes :: "ranges" where
"swap_codes = mk_ranges [(0x90, 0xa0)]"

declare swap_codes_def [simp add]
typedef swap_inst = "set_of_ranges swap_codes"
  unfolding swap_codes_def
  by(transfer; auto)
setup_lifting type_definition_swap_inst

lift_definition SWAP_INST :: "swap_inst => inst" is id
  unfolding swap_codes_def inst_codes_def
  by(transfer; auto)

definition log_codes :: "ranges" where
"log_codes = mk_ranges [(0xa0, 0xa5)]"

declare log_codes_def [simp add]
typedef log_inst = "set_of_ranges log_codes"
  unfolding log_codes_def
  by(transfer; auto)
setup_lifting type_definition_log_inst

lift_definition LOG_INST :: "log_inst => inst" is id
  unfolding log_codes_def inst_codes_def
by(transfer; auto)

definition comms_codes :: "ranges" where
"comms_codes = mk_ranges [(0xf0, 0xf6), (0xfa, 0xfb)]"

declare comms_codes_def [simp add]
typedef comms_inst = "set_of_ranges comms_codes"
  unfolding comms_codes_def
  by(transfer; auto)
setup_lifting type_definition_comms_inst

lift_definition COMMS_INST :: "comms_inst => inst" is id
  unfolding comms_codes_def inst_codes_def
  by(transfer; auto)

definition special_halt_codes :: "ranges" where
"special_halt_codes = mk_ranges [(0xfd, 0x100)]"

declare special_halt_codes_def [simp add]
typedef special_halt_inst = "set_of_ranges special_halt_codes"
  unfolding special_halt_codes_def
  by(transfer; auto)
setup_lifting type_definition_special_halt_inst

lift_definition SPECIAL_HALT_INST :: "special_halt_inst => inst" is id
  unfolding special_halt_codes_def inst_codes_def
by(transfer; auto)

definition good_codes :: "ranges" where
"good_codes = Union_ranges
  [stop_codes,
  arith_codes,
  bits_codes,
  keccak256_codes,
  tx_data_codes,
  chain_data_codes,
  state_control_codes,
  push_codes,
  dup_codes,
  swap_codes,
  log_codes,
  comms_codes,
  special_halt_codes]"

value "good_codes"

typedef good_inst = "set_of_ranges good_codes"
  apply(simp add: good_codes_def)
  apply(transfer) apply(auto)
  done
setup_lifting type_definition_good_inst

lift_definition GOOD_INST :: "good_inst \<Rightarrow> inst" is id
  apply(simp add: good_codes_def)
  apply(transfer) apply(auto)
  done

definition bad_codes :: "ranges" where
"bad_codes = diff_ranges inst_codes good_codes"
typedef bad_inst = "set_of_ranges bad_codes"
  apply(simp add: bad_codes_def good_codes_def)
  apply(transfer) apply(simp)
  apply(auto)
  done
setup_lifting type_definition_bad_inst

lift_definition BAD_INST :: "bad_inst \<Rightarrow> inst" is id
  unfolding bad_codes_def good_codes_def inst_codes_def
  apply(simp)
  apply(transfer) apply(simp)
  apply(auto)
  done

value "union_ranges good_codes bad_codes"

lemma good_bad_all :
  "union_ranges good_codes bad_codes = inst_codes"
  unfolding bad_codes_def good_codes_def
  by(transfer; simp; transfer; auto)

lemma good_bad_all' :
  "Union_ranges [
  stop_codes,
  arith_codes,
  bits_codes,
  keccak256_codes,
  tx_data_codes,
  chain_data_codes,
  state_control_codes,
  push_codes,
  dup_codes,
  swap_codes,
  log_codes,
  comms_codes,
  special_halt_codes,
  bad_codes] = inst_codes"
  unfolding bad_codes_def good_codes_def
  by(simp; transfer; auto)

lemma cases_inst_helper :
assumes H : "y \<in> set_of_ranges inst_codes"
assumes H1 :  "(\<And>x1. x1 \<in> set_of_ranges stop_codes \<Longrightarrow>
                  y = id x1 \<Longrightarrow> P)"
assumes H2 : "(\<And>x2. x2 \<in> set_of_ranges arith_codes \<Longrightarrow>
                  y = id x2 \<Longrightarrow> P)"
assumes H3 : "(\<And>x3. x3 \<in> set_of_ranges bits_codes \<Longrightarrow>
                  y = id x3 \<Longrightarrow> P)"
assumes H4: "(\<And>x4. x4 \<in> set_of_ranges keccak256_codes \<Longrightarrow>
                  y = id x4 \<Longrightarrow> P)"
assumes H5 : "(\<And>x5. x5 \<in> set_of_ranges tx_data_codes \<Longrightarrow>
                  y = id x5 \<Longrightarrow> P)"
assumes H6 : "(\<And>x6. x6 \<in> set_of_ranges chain_data_codes \<Longrightarrow>
                  y = id x6 \<Longrightarrow> P)"
assumes H7 : "(\<And>x7. x7 \<in> set_of_ranges state_control_codes \<Longrightarrow>
                  y = id x7 \<Longrightarrow> P)"
assumes H8 : "(\<And> x8. x8 \<in> set_of_ranges push_codes \<Longrightarrow>
                  y = id x8 \<Longrightarrow> P)"
assumes H9 : "(\<And>x9. x9 \<in> set_of_ranges dup_codes \<Longrightarrow>
                  y = id x9 \<Longrightarrow> P)"
assumes H10 : "(\<And>x10. x10 \<in> set_of_ranges swap_codes \<Longrightarrow>
                   y = id x10 \<Longrightarrow> P)"
assumes H11 : "(\<And>x11. x11 \<in> set_of_ranges log_codes \<Longrightarrow>
                   y = id x11 \<Longrightarrow> P)"
assumes H12 : "(\<And>x12. x12 \<in> set_of_ranges comms_codes \<Longrightarrow>
                   y = id x12 \<Longrightarrow> P)"
assumes H13 : "(\<And>x13. x13 \<in> set_of_ranges special_halt_codes \<Longrightarrow>
                   y = id x13 \<Longrightarrow> P)"
assumes H14 : "(\<And>x14. x14 \<in> set_of_ranges bad_codes \<Longrightarrow>
                   y = id x14 \<Longrightarrow> P)"
shows P
proof-

  have H' : "member_ranges y (Union_ranges [
          stop_codes,
          arith_codes,
          bits_codes,
          keccak256_codes,
          tx_data_codes,
          chain_data_codes,
          state_control_codes,
          push_codes,
          dup_codes,
          swap_codes,
          log_codes,
          comms_codes,
          special_halt_codes,
          bad_codes])"
    using H unfolding sym[OF good_bad_all'] member_ranges_spec by(auto)

  have Conc' : "(y = y \<longrightarrow> P)"
  proof(rule Union_ranges_cases[OF _ H', of "\<lambda> x . (y = x \<longrightarrow> P)"] )
    fix r x
    assume R_cases :
    "r \<in> set [stop_codes, arith_codes, bits_codes,
                    keccak256_codes, tx_data_codes,
                    chain_data_codes, state_control_codes,
                    push_codes, dup_codes, swap_codes, log_codes,
                    comms_codes, special_halt_codes,
                    bad_codes]"
    assume R_mem : "member_ranges x r"
    consider (1) "r = stop_codes" |
             (2) "r = arith_codes" |
             (3) "r = bits_codes" |
             (4) "r = keccak256_codes" |
             (5) "r = tx_data_codes" |
             (6) "r = chain_data_codes" |
             (7) "r = state_control_codes" |
             (8) "r = push_codes" |
             (9) "r = dup_codes" |
             (10) "r = swap_codes" |
             (11) "r = log_codes" |
             (12) "r = comms_codes" |
             (13) "r = special_halt_codes" |
             (14) "r = bad_codes"
      using R_cases by(auto)
    thus "y = x \<longrightarrow> P"
    proof cases
    case 1
      then show ?thesis using H1 R_mem unfolding member_ranges_spec by(auto)
    next
      case 2
      then show ?thesis using H2 R_mem unfolding member_ranges_spec by(auto)
    next
      case 3
      then show ?thesis using H3 R_mem unfolding member_ranges_spec by(auto)
    next
      case 4
      then show ?thesis using H4 R_mem unfolding member_ranges_spec by(auto)
    next
      case 5
      then show ?thesis using H5 R_mem unfolding member_ranges_spec by(auto)
    next
      case 6
      then show ?thesis using H6 R_mem unfolding member_ranges_spec by(auto)
    next
      case 7
      then show ?thesis using H7 R_mem unfolding member_ranges_spec by(auto)
    next
      case 8
      then show ?thesis using H8 R_mem unfolding member_ranges_spec by(auto)
    next
      case 9
      then show ?thesis using H9 R_mem unfolding member_ranges_spec by(auto)
    next
      case 10
      then show ?thesis using H10 R_mem unfolding member_ranges_spec by(auto)
    next
      case 11
      then show ?thesis using H11 R_mem unfolding member_ranges_spec by(auto)
    next
      case 12
      then show ?thesis using H12 R_mem unfolding member_ranges_spec by(auto)
    next
      case 13
      then show ?thesis using H13 R_mem unfolding member_ranges_spec by(auto)
    next
      case 14
      then show ?thesis using H14 R_mem unfolding member_ranges_spec by(auto)
    qed
  qed
  thus ?thesis by auto
qed

(*
  consider
  (1) "y \<in> set_of_ranges stop_codes" |
  (2) "y \<in> set_of_ranges arith_codes" |
  (3) "y \<in> set_of_ranges bits_codes" |
  (4) "y \<in> set_of_ranges keccak256_codes" |
  (5) "y \<in> set_of_ranges tx_data_codes" |
  (6) "y \<in> set_of_ranges chain_data_codes" |
  (7) "y \<in> set_of_ranges state_control_codes" |
  (8) "y \<in> set_of_ranges push_codes" |
  (9) "y \<in> set_of_ranges dup_codes" |
  (10) "y \<in> set_of_ranges swap_codes" |
  (11) "y \<in> set_of_ranges log_codes" |
  (12) "y \<in> set_of_ranges comms_codes" |
  (13) "y \<in> set_of_ranges special_halt_codes" |
  (14) "y \<in> set_of_ranges bad_codes"
    apply(simp add: good_codes_def bad_codes_def)
    apply(transfer)
    apply(auto)
    done
  then show P
  proof cases
    case 1
  then show ?thesis usin
next
  case 2
  then show ?thesis sorry
next
  case 3
  then show ?thesis sorry
next
  case 4
  then show ?thesis sorry
next
  case 5
  then show ?thesis sorry
next
  case 6
  then show ?thesis sorry
next
  case 7
  then show ?thesis sorry
next
    case 8
    then show ?thesis sorry
  next
    case 9
    then show ?thesis sorry
  next
    case 10
    then show ?thesis sorry
  next
    case 11
    then show ?thesis sorry
  next
    case 12
    then show ?thesis sorry
  next
    case 13
    then show ?thesis sorry
  next
    case 14
    then show ?thesis sorry
  qed
*)
(*
free_constructors cases_inst for
STOP_INST
| ARITH_INST
| BITS_INST
| KECCAK256_INST
| TX_DATA_INST
| CHAIN_DATA_INST
| STATE_CONTROL_INST
| PUSH_INST
| DUP_INST
| SWAP_INST
| LOG_INST
| COMMS_INST
| SPECIAL_HALT_INST
| BAD_INST
  apply(transfer)
  apply(rule cases_inst_helper; blast)
  apply(transfer; simp add: bad_codes_def good_codes_def; transfer; force; fail)+
  done
*)
end


(*
    Author:   Benedikt Seidl
    License:  BSD
*)

section \<open>Syntactic Fragments and Stability\<close>

theory Syntactic_Fragments_and_Stability
imports
  LTL.LTL "HOL-Library.Sublist"
begin

\<comment> \<open>We use prefix and suffix on infinite words.\<close>
hide_const Sublist.prefix Sublist.suffix

subsection \<open>The fragments @{term \<mu>LTL} and @{term \<nu>LTL}\<close>

fun is_\<mu>LTL :: "'a ltln \<Rightarrow> bool"
where
  "is_\<mu>LTL true\<^sub>n = True"
| "is_\<mu>LTL false\<^sub>n = True"
| "is_\<mu>LTL prop\<^sub>n(_) = True"
| "is_\<mu>LTL nprop\<^sub>n(_) = True"
| "is_\<mu>LTL (\<phi> and\<^sub>n \<psi>) = (is_\<mu>LTL \<phi> \<and> is_\<mu>LTL \<psi>)"
| "is_\<mu>LTL (\<phi> or\<^sub>n \<psi>) = (is_\<mu>LTL \<phi> \<and> is_\<mu>LTL \<psi>)"
| "is_\<mu>LTL (X\<^sub>n \<phi>) = is_\<mu>LTL \<phi>"
| "is_\<mu>LTL (\<phi> U\<^sub>n \<psi>) = (is_\<mu>LTL \<phi> \<and> is_\<mu>LTL \<psi>)"
| "is_\<mu>LTL (\<phi> M\<^sub>n \<psi>) = (is_\<mu>LTL \<phi> \<and> is_\<mu>LTL \<psi>)"
| "is_\<mu>LTL _ = False"

fun is_\<nu>LTL :: "'a ltln \<Rightarrow> bool"
where
  "is_\<nu>LTL true\<^sub>n = True"
| "is_\<nu>LTL false\<^sub>n = True"
| "is_\<nu>LTL prop\<^sub>n(_) = True"
| "is_\<nu>LTL nprop\<^sub>n(_) = True"
| "is_\<nu>LTL (\<phi> and\<^sub>n \<psi>) = (is_\<nu>LTL \<phi> \<and> is_\<nu>LTL \<psi>)"
| "is_\<nu>LTL (\<phi> or\<^sub>n \<psi>) = (is_\<nu>LTL \<phi> \<and> is_\<nu>LTL \<psi>)"
| "is_\<nu>LTL (X\<^sub>n \<phi>) = is_\<nu>LTL \<phi>"
| "is_\<nu>LTL (\<phi> W\<^sub>n \<psi>) = (is_\<nu>LTL \<phi> \<and> is_\<nu>LTL \<psi>)"
| "is_\<nu>LTL (\<phi> R\<^sub>n \<psi>) = (is_\<nu>LTL \<phi> \<and> is_\<nu>LTL \<psi>)"
| "is_\<nu>LTL _ = False"

definition \<mu>LTL :: "'a ltln set" where
  "\<mu>LTL = {\<phi>. is_\<mu>LTL \<phi>}"

definition \<nu>LTL :: "'a ltln set" where
  "\<nu>LTL = {\<phi>. is_\<nu>LTL \<phi>}"

lemma \<mu>LTL_simp[simp]:
  "\<phi> \<in> \<mu>LTL \<longleftrightarrow> is_\<mu>LTL \<phi>"
  unfolding \<mu>LTL_def by simp

lemma \<nu>LTL_simp[simp]:
  "\<phi> \<in> \<nu>LTL \<longleftrightarrow> is_\<nu>LTL \<phi>"
  unfolding \<nu>LTL_def by simp

subsubsection \<open>Subformulas in @{term \<mu>LTL} and @{term \<nu>LTL}\<close>

fun subformulas\<^sub>\<mu> :: "'a ltln \<Rightarrow> 'a ltln set"
where
  "subformulas\<^sub>\<mu> (\<phi> and\<^sub>n \<psi>) = subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> (\<phi> or\<^sub>n \<psi>) = subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> (X\<^sub>n \<phi>) = subformulas\<^sub>\<mu> \<phi>"
| "subformulas\<^sub>\<mu> (\<phi> U\<^sub>n \<psi>) = {\<phi> U\<^sub>n \<psi>} \<union> subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> (\<phi> R\<^sub>n \<psi>) = subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> (\<phi> W\<^sub>n \<psi>) = subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> (\<phi> M\<^sub>n \<psi>) = {\<phi> M\<^sub>n \<psi>} \<union> subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<mu> \<psi>"
| "subformulas\<^sub>\<mu> _ = {}"

fun subformulas\<^sub>\<nu> :: "'a ltln \<Rightarrow> 'a ltln set"
where
  "subformulas\<^sub>\<nu> (\<phi> and\<^sub>n \<psi>) = subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> (\<phi> or\<^sub>n \<psi>) = subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> (X\<^sub>n \<phi>) = subformulas\<^sub>\<nu> \<phi>"
| "subformulas\<^sub>\<nu> (\<phi> U\<^sub>n \<psi>) = subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> (\<phi> R\<^sub>n \<psi>) = {\<phi> R\<^sub>n \<psi>} \<union> subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> (\<phi> W\<^sub>n \<psi>) = {\<phi> W\<^sub>n \<psi>} \<union> subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> (\<phi> M\<^sub>n \<psi>) = subformulas\<^sub>\<nu> \<phi> \<union> subformulas\<^sub>\<nu> \<psi>"
| "subformulas\<^sub>\<nu> _ = {}"

lemma subformulas\<^sub>\<mu>_semantics:
  "subformulas\<^sub>\<mu> \<phi> = {\<psi> \<in> subfrmlsn \<phi>. \<exists>\<psi>\<^sub>1 \<psi>\<^sub>2. \<psi> = \<psi>\<^sub>1 U\<^sub>n \<psi>\<^sub>2 \<or> \<psi> = \<psi>\<^sub>1 M\<^sub>n \<psi>\<^sub>2}"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<nu>_semantics:
  "subformulas\<^sub>\<nu> \<phi> = {\<psi> \<in> subfrmlsn \<phi>. \<exists>\<psi>\<^sub>1 \<psi>\<^sub>2. \<psi> = \<psi>\<^sub>1 R\<^sub>n \<psi>\<^sub>2 \<or> \<psi> = \<psi>\<^sub>1 W\<^sub>n \<psi>\<^sub>2}"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>_subfrmlsn:
  "subformulas\<^sub>\<mu> \<phi> \<subseteq> subfrmlsn \<phi>"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<nu>_subfrmlsn:
  "subformulas\<^sub>\<nu> \<phi> \<subseteq> subfrmlsn \<phi>"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>_finite:
  "finite (subformulas\<^sub>\<mu> \<phi>)"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>_subset:
  "\<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> subformulas\<^sub>\<mu> \<psi> \<subseteq> subformulas\<^sub>\<mu> \<phi>"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<nu>_subset:
  "\<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> subformulas\<^sub>\<nu> \<psi> \<subseteq> subformulas\<^sub>\<nu> \<phi>"
  by (induction \<phi>) auto

lemma subfrmlsn_\<mu>LTL:
  "\<phi> \<in> \<mu>LTL \<Longrightarrow> subfrmlsn \<phi> \<subseteq> \<mu>LTL"
  by (induction \<phi>) auto

lemma subfrmlsn_\<nu>LTL:
  "\<phi> \<in> \<nu>LTL \<Longrightarrow> subfrmlsn \<phi> \<subseteq> \<nu>LTL"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>\<^sub>\<nu>_disjoint:
  "subformulas\<^sub>\<mu> \<phi> \<inter> subformulas\<^sub>\<nu> \<phi> = {}"
  unfolding subformulas\<^sub>\<mu>_semantics subformulas\<^sub>\<nu>_semantics
  by fastforce

lemma subformulas\<^sub>\<mu>\<^sub>\<nu>_subfrmlsn:
  "subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<nu> \<phi> \<subseteq> subfrmlsn \<phi>"
  using subformulas\<^sub>\<mu>_subfrmlsn subformulas\<^sub>\<nu>_subfrmlsn by blast

lemma subformulas\<^sub>\<mu>\<^sub>\<nu>_card:
  "card (subformulas\<^sub>\<mu> \<phi> \<union> subformulas\<^sub>\<nu> \<phi>) = card (subformulas\<^sub>\<mu> \<phi>) + card (subformulas\<^sub>\<nu> \<phi>)"
  by (simp add: subformulas\<^sub>\<mu>\<^sub>\<nu>_disjoint subformulas\<^sub>\<mu>_finite subformulas\<^sub>\<nu>_finite card_Un_disjoint)

subsection \<open>Stability\<close>

definition "GF_singleton w \<phi> \<equiv> if w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<phi>) then {\<phi>} else {}"
definition "F_singleton w \<phi> \<equiv> if w \<Turnstile>\<^sub>n F\<^sub>n \<phi> then {\<phi>} else {}"

declare GF_singleton_def [simp] F_singleton_def [simp]

fun \<G>\<F> :: "'a ltln \<Rightarrow> 'a set word \<Rightarrow> 'a ltln set"
where
  "\<G>\<F> (\<phi> and\<^sub>n \<psi>) w = \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> (\<phi> or\<^sub>n \<psi>) w = \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> (X\<^sub>n \<phi>) w = \<G>\<F> \<phi> w"
| "\<G>\<F> (\<phi> U\<^sub>n \<psi>) w = GF_singleton w (\<phi> U\<^sub>n \<psi>) \<union> \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> (\<phi> R\<^sub>n \<psi>) w = \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> (\<phi> W\<^sub>n \<psi>) w = \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> (\<phi> M\<^sub>n \<psi>) w = GF_singleton w (\<phi> M\<^sub>n \<psi>) \<union> \<G>\<F> \<phi> w \<union> \<G>\<F> \<psi> w"
| "\<G>\<F> _ _ = {}"

fun \<F> :: "'a ltln \<Rightarrow> 'a set word \<Rightarrow> 'a ltln set"
where
  "\<F> (\<phi> and\<^sub>n \<psi>) w = \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> (\<phi> or\<^sub>n \<psi>) w = \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> (X\<^sub>n \<phi>) w = \<F> \<phi> w"
| "\<F> (\<phi> U\<^sub>n \<psi>) w = F_singleton w (\<phi> U\<^sub>n \<psi>) \<union> \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> (\<phi> R\<^sub>n \<psi>) w = \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> (\<phi> W\<^sub>n \<psi>) w = \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> (\<phi> M\<^sub>n \<psi>) w = F_singleton w (\<phi> M\<^sub>n \<psi>) \<union> \<F> \<phi> w \<union> \<F> \<psi> w"
| "\<F> _ _ = {}"

lemma \<G>\<F>_semantics:
  "\<G>\<F> \<phi> w = {\<psi>. \<psi> \<in> subformulas\<^sub>\<mu> \<phi> \<and> w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>)}"
  by (induction \<phi>) force+

lemma \<F>_semantics:
  "\<F> \<phi> w = {\<psi>. \<psi> \<in> subformulas\<^sub>\<mu> \<phi> \<and> w \<Turnstile>\<^sub>n F\<^sub>n \<psi>}"
  by (induction \<phi>) force+

lemma \<G>\<F>_semantics':
  "\<G>\<F> \<phi> w = subformulas\<^sub>\<mu> \<phi> \<inter> {\<psi>. w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>)}"
  unfolding \<G>\<F>_semantics by auto

lemma \<F>_semantics':
  "\<F> \<phi> w = subformulas\<^sub>\<mu> \<phi> \<inter> {\<psi>. w \<Turnstile>\<^sub>n F\<^sub>n \<psi>}"
  unfolding \<F>_semantics by auto

lemma \<G>\<F>_\<F>_subset:
  "\<G>\<F> \<phi> w \<subseteq> \<F> \<phi> w"
  unfolding \<G>\<F>_semantics \<F>_semantics by force

lemma \<G>\<F>_finite:
  "finite (\<G>\<F> \<phi> w)"
  by (induction \<phi>) auto

lemma \<G>\<F>_subformulas\<^sub>\<mu>:
  "\<G>\<F> \<phi> w \<subseteq> subformulas\<^sub>\<mu> \<phi>"
  unfolding \<G>\<F>_semantics by force

lemma \<G>\<F>_subfrmlsn:
  "\<G>\<F> \<phi> w \<subseteq> subfrmlsn \<phi>"
  using \<G>\<F>_subformulas\<^sub>\<mu> subformulas\<^sub>\<mu>_subfrmlsn by blast

lemma \<G>\<F>_elim:
  "\<psi> \<in> \<G>\<F> \<phi> w \<Longrightarrow> w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>)"
  unfolding \<G>\<F>_semantics by simp

lemma \<G>\<F>_suffix(* [simp] *):
  "\<G>\<F> \<phi> (suffix i w) = \<G>\<F> \<phi> w"
proof
  show "\<G>\<F> \<phi> w \<subseteq> \<G>\<F> \<phi> (suffix i w)"
    unfolding \<G>\<F>_semantics by auto
next
  show "\<G>\<F> \<phi> (suffix i w) \<subseteq> \<G>\<F> \<phi> w"
    unfolding \<G>\<F>_semantics GF_Inf_many
  proof auto
    fix \<psi>
    assume "\<exists>\<^sub>\<infinity>j. suffix (i + j) w \<Turnstile>\<^sub>n \<psi>"
    then have "\<exists>\<^sub>\<infinity>j. suffix (j + i) w \<Turnstile>\<^sub>n \<psi>"
      by (simp add: algebra_simps)
    then show "\<exists>\<^sub>\<infinity>j. suffix j w \<Turnstile>\<^sub>n \<psi>"
      using INFM_nat_add by blast
  qed
qed

lemma \<G>\<F>_subset:
  "\<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> \<G>\<F> \<psi> w \<subseteq> \<G>\<F> \<phi> w"
  unfolding \<G>\<F>_semantics using subformulas\<^sub>\<mu>_subset by blast

lemma \<F>_finite:
  "finite (\<F> \<phi> w)"
  by (induction \<phi>) auto

lemma \<F>_subformulas\<^sub>\<mu>:
  "\<F> \<phi> w \<subseteq> subformulas\<^sub>\<mu> \<phi>"
  unfolding \<F>_semantics by force

lemma \<F>_subfrmlsn:
  "\<F> \<phi> w \<subseteq> subfrmlsn \<phi>"
  using \<F>_subformulas\<^sub>\<mu> subformulas\<^sub>\<mu>_subfrmlsn by blast

lemma \<F>_elim:
  "\<psi> \<in> \<F> \<phi> w \<Longrightarrow> w \<Turnstile>\<^sub>n F\<^sub>n \<psi>"
  unfolding \<F>_semantics by simp

lemma \<F>_suffix:
  "\<F> \<phi> (suffix i w) \<subseteq> \<F> \<phi> w"
  unfolding \<F>_semantics by auto

lemma \<F>_subset:
  "\<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> \<F> \<psi> w \<subseteq> \<F> \<phi> w"
  unfolding \<F>_semantics using subformulas\<^sub>\<mu>_subset by blast

definition "\<mu>_stable \<phi> w \<longleftrightarrow> \<G>\<F> \<phi> w = \<F> \<phi> w"

lemma suffix_\<mu>_stable:
  "\<forall>\<^sub>\<infinity>i. \<mu>_stable \<phi> (suffix i w)"
proof -
  have "\<forall>\<psi> \<in> subformulas\<^sub>\<mu> \<phi>. \<forall>\<^sub>\<infinity>i. suffix i w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>) \<longleftrightarrow> suffix i w \<Turnstile>\<^sub>n F\<^sub>n \<psi>"
    using Alm_all_GF_F by blast

  then have "\<forall>\<^sub>\<infinity>i. \<forall>\<psi> \<in> subformulas\<^sub>\<mu> \<phi>. suffix i w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>) \<longleftrightarrow> suffix i w \<Turnstile>\<^sub>n F\<^sub>n \<psi>"
    using subformulas\<^sub>\<mu>_finite eventually_ball_finite by fast

  then have "\<forall>\<^sub>\<infinity>i. {\<psi> \<in> subformulas\<^sub>\<mu> \<phi>. suffix i w \<Turnstile>\<^sub>n G\<^sub>n (F\<^sub>n \<psi>)} = {\<psi> \<in> subformulas\<^sub>\<mu> \<phi>. suffix i w \<Turnstile>\<^sub>n F\<^sub>n \<psi>}"
    by (rule MOST_mono) (blast intro: Collect_cong)

  then show ?thesis
    unfolding \<mu>_stable_def \<G>\<F>_semantics \<F>_semantics
    by (rule MOST_mono) simp
qed

lemma \<mu>_stable_subfrmlsn:
  "\<mu>_stable \<phi> w \<Longrightarrow> \<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> \<mu>_stable \<psi> w"
proof -
  assume a1: "\<psi> \<in> subfrmlsn \<phi>" and a2: "\<mu>_stable \<phi> w"
  have "subformulas\<^sub>\<mu> \<psi> \<subseteq> subformulas\<^sub>\<mu> \<phi>"
    using a1 by (simp add: subformulas\<^sub>\<mu>_subset)
  moreover
  have "\<G>\<F> \<phi> w = \<F> \<phi> w"
    using a2 by (meson \<mu>_stable_def)
  ultimately show ?thesis
    by (metis (no_types) Un_commute \<F>_semantics' \<G>\<F>_semantics' \<mu>_stable_def inf_left_commute inf_sup_absorb sup.orderE)
qed

lemma \<mu>_stable_suffix:
  "\<mu>_stable \<phi> w \<Longrightarrow> \<mu>_stable \<phi> (suffix i w)"
  by (metis \<F>_suffix \<G>\<F>_\<F>_subset \<G>\<F>_suffix \<mu>_stable_def subset_antisym)

definition "FG_singleton w \<phi> \<equiv> if w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<phi>) then {\<phi>} else {}"
definition "G_singleton w \<phi> \<equiv> if w \<Turnstile>\<^sub>n G\<^sub>n \<phi> then {\<phi>} else {}"

declare FG_singleton_def [simp] G_singleton_def [simp]

fun \<F>\<G> :: "'a ltln \<Rightarrow> 'a set word \<Rightarrow> 'a ltln set"
where
  "\<F>\<G> (\<phi> and\<^sub>n \<psi>) w = \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> (\<phi> or\<^sub>n \<psi>) w = \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> (X\<^sub>n \<phi>) w = \<F>\<G> \<phi> w"
| "\<F>\<G> (\<phi> U\<^sub>n \<psi>) w = \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> (\<phi> R\<^sub>n \<psi>) w = FG_singleton w (\<phi> R\<^sub>n \<psi>) \<union> \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> (\<phi> W\<^sub>n \<psi>) w = FG_singleton w (\<phi> W\<^sub>n \<psi>) \<union> \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> (\<phi> M\<^sub>n \<psi>) w = \<F>\<G> \<phi> w \<union> \<F>\<G> \<psi> w"
| "\<F>\<G> _ _ = {}"

fun \<G> :: "'a ltln \<Rightarrow> 'a set word \<Rightarrow> 'a ltln set"
where
  "\<G> (\<phi> and\<^sub>n \<psi>) w = \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> (\<phi> or\<^sub>n \<psi>) w = \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> (X\<^sub>n \<phi>) w = \<G> \<phi> w"
| "\<G> (\<phi> U\<^sub>n \<psi>) w = \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> (\<phi> R\<^sub>n \<psi>) w = G_singleton w (\<phi> R\<^sub>n \<psi>) \<union> \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> (\<phi> W\<^sub>n \<psi>) w = G_singleton w (\<phi> W\<^sub>n \<psi>) \<union> \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> (\<phi> M\<^sub>n \<psi>) w = \<G> \<phi> w \<union> \<G> \<psi> w"
| "\<G> _ _ = {}"

lemma \<F>\<G>_semantics:
  "\<F>\<G> \<phi> w = {\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>)}"
  by (induction \<phi>) force+

lemma \<G>_semantics:
  "\<G> \<phi> w \<equiv> {\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. w \<Turnstile>\<^sub>n G\<^sub>n \<psi>}"
  by (induction \<phi>) force+

lemma \<F>\<G>_semantics':
  "\<F>\<G> \<phi> w = subformulas\<^sub>\<nu> \<phi> \<inter> {\<psi>. w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>)}"
  unfolding \<F>\<G>_semantics by auto

lemma \<G>_semantics':
  "\<G> \<phi> w = subformulas\<^sub>\<nu> \<phi> \<inter> {\<psi>. w \<Turnstile>\<^sub>n G\<^sub>n \<psi>}"
  unfolding \<G>_semantics by auto

lemma \<G>_\<F>\<G>_subset:
  "\<G> \<phi> w \<subseteq> \<F>\<G> \<phi> w"
  unfolding \<G>_semantics \<F>\<G>_semantics by force

lemma \<F>\<G>_finite:
  "finite (\<F>\<G> \<phi> w)"
  by (induction \<phi>) auto

lemma \<F>\<G>_subformulas\<^sub>\<nu>:
  "\<F>\<G> \<phi> w \<subseteq> subformulas\<^sub>\<nu> \<phi>"
  unfolding \<F>\<G>_semantics by force

lemma \<F>\<G>_subfrmlsn:
  "\<F>\<G> \<phi> w \<subseteq> subfrmlsn \<phi>"
  using \<F>\<G>_subformulas\<^sub>\<nu> subformulas\<^sub>\<nu>_subfrmlsn by blast

lemma \<F>\<G>_elim:
  "\<psi> \<in> \<F>\<G> \<phi> w \<Longrightarrow> w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>)"
  unfolding \<F>\<G>_semantics by simp

lemma \<F>\<G>_suffix:
  "\<F>\<G> \<phi> (suffix i w) = \<F>\<G> \<phi> w"
proof
  show "\<F>\<G> \<phi> (suffix i w) \<subseteq> \<F>\<G> \<phi> w"
    unfolding \<F>\<G>_semantics by auto
next
  show "\<F>\<G> \<phi> w \<subseteq> \<F>\<G> \<phi> (suffix i w)"
    unfolding \<F>\<G>_semantics FG_Alm_all
  proof auto
    fix \<psi>
    assume "\<forall>\<^sub>\<infinity>j. suffix j w \<Turnstile>\<^sub>n \<psi>"
    then have "\<forall>\<^sub>\<infinity>j. suffix (j + i) w \<Turnstile>\<^sub>n \<psi>"
      using MOST_nat_add by meson
    then show "\<forall>\<^sub>\<infinity>j. suffix (i + j) w \<Turnstile>\<^sub>n \<psi>"
      by (simp add: algebra_simps)
  qed
qed

lemma \<F>\<G>_subset:
  "\<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> \<F>\<G> \<psi> w \<subseteq> \<F>\<G> \<phi> w"
  unfolding \<F>\<G>_semantics using subformulas\<^sub>\<nu>_subset by blast

lemma \<G>_finite:
  "finite (\<G> \<phi> w)"
  by (induction \<phi>) auto

lemma \<G>_subformulas\<^sub>\<nu>:
  "\<G> \<phi> w \<subseteq> subformulas\<^sub>\<nu> \<phi>"
  unfolding \<G>_semantics by force

lemma \<G>_subfrmlsn:
  "\<G> \<phi> w \<subseteq> subfrmlsn \<phi>"
  using \<G>_subformulas\<^sub>\<nu> subformulas\<^sub>\<nu>_subfrmlsn by blast

lemma \<G>_elim:
  "\<psi> \<in> \<G> \<phi> w \<Longrightarrow> w \<Turnstile>\<^sub>n G\<^sub>n \<psi>"
  unfolding \<G>_semantics by simp

lemma \<G>_suffix:
  "\<G> \<phi> w \<subseteq> \<G> \<phi> (suffix i w)"
  unfolding \<G>_semantics by auto

definition "\<nu>_stable \<phi> w \<longleftrightarrow> \<F>\<G> \<phi> w = \<G> \<phi> w"

lemma suffix_\<nu>_stable:
  "\<forall>\<^sub>\<infinity>j. \<nu>_stable \<phi> (suffix j w)"
proof -
  have "\<forall>\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. \<forall>\<^sub>\<infinity>i. suffix i w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>) \<longleftrightarrow> suffix i w \<Turnstile>\<^sub>n G\<^sub>n \<psi>"
    using Alm_all_FG_G by blast

  then have "\<forall>\<^sub>\<infinity>i. \<forall>\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. suffix i w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>) \<longleftrightarrow> suffix i w \<Turnstile>\<^sub>n G\<^sub>n \<psi>"
    using subformulas\<^sub>\<nu>_finite eventually_ball_finite by fast

  then have "\<forall>\<^sub>\<infinity>i. {\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. suffix i w \<Turnstile>\<^sub>n F\<^sub>n (G\<^sub>n \<psi>)} = {\<psi> \<in> subformulas\<^sub>\<nu> \<phi>. suffix i w \<Turnstile>\<^sub>n G\<^sub>n \<psi>}"
    by (rule MOST_mono) (blast intro: Collect_cong)

  then show ?thesis
    unfolding \<nu>_stable_def \<F>\<G>_semantics \<G>_semantics
    by (rule MOST_mono) simp
qed

lemma \<nu>_stable_subfrmlsn:
  "\<nu>_stable \<phi> w \<Longrightarrow> \<psi> \<in> subfrmlsn \<phi> \<Longrightarrow> \<nu>_stable \<psi> w"
proof -
  assume a1: "\<psi> \<in> subfrmlsn \<phi>" and a2: "\<nu>_stable \<phi> w"
  have "subformulas\<^sub>\<nu> \<psi> \<subseteq> subformulas\<^sub>\<nu> \<phi>"
    using a1 by (simp add: subformulas\<^sub>\<nu>_subset)
  moreover
  have "\<F>\<G> \<phi> w = \<G> \<phi> w"
    using a2 by (meson \<nu>_stable_def)
  ultimately show ?thesis
    by (metis (no_types) Un_commute \<G>_semantics' \<F>\<G>_semantics' \<nu>_stable_def inf_left_commute inf_sup_absorb sup.orderE)
qed

lemma \<nu>_stable_suffix:
  "\<nu>_stable \<phi> w \<Longrightarrow> \<nu>_stable \<phi> (suffix i w)"
  by (metis \<F>\<G>_suffix \<G>_\<F>\<G>_subset \<G>_suffix \<nu>_stable_def antisym_conv)

subsection \<open>Definitions with Lists for Code Export\<close>

text \<open>The \<open>\<mu>\<close>- and \<open>\<nu>\<close>-subformulas as lists:\<close>

fun subformulas\<^sub>\<mu>_list :: "'a ltln \<Rightarrow> 'a ltln list"
where
  "subformulas\<^sub>\<mu>_list (\<phi> and\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>)"
| "subformulas\<^sub>\<mu>_list (\<phi> or\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>)"
| "subformulas\<^sub>\<mu>_list (X\<^sub>n \<phi>) = subformulas\<^sub>\<mu>_list \<phi>"
| "subformulas\<^sub>\<mu>_list (\<phi> U\<^sub>n \<psi>) = List.insert (\<phi> U\<^sub>n \<psi>) (List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>))"
| "subformulas\<^sub>\<mu>_list (\<phi> R\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>)"
| "subformulas\<^sub>\<mu>_list (\<phi> W\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>)"
| "subformulas\<^sub>\<mu>_list (\<phi> M\<^sub>n \<psi>) = List.insert (\<phi> M\<^sub>n \<psi>) (List.union (subformulas\<^sub>\<mu>_list \<phi>) (subformulas\<^sub>\<mu>_list \<psi>))"
| "subformulas\<^sub>\<mu>_list _ = []"

fun subformulas\<^sub>\<nu>_list :: "'a ltln \<Rightarrow> 'a ltln list"
where
  "subformulas\<^sub>\<nu>_list (\<phi> and\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>)"
| "subformulas\<^sub>\<nu>_list (\<phi> or\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>)"
| "subformulas\<^sub>\<nu>_list (X\<^sub>n \<phi>) = subformulas\<^sub>\<nu>_list \<phi>"
| "subformulas\<^sub>\<nu>_list (\<phi> U\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>)"
| "subformulas\<^sub>\<nu>_list (\<phi> R\<^sub>n \<psi>) = List.insert (\<phi> R\<^sub>n \<psi>) (List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>))"
| "subformulas\<^sub>\<nu>_list (\<phi> W\<^sub>n \<psi>) = List.insert (\<phi> W\<^sub>n \<psi>) (List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>))"
| "subformulas\<^sub>\<nu>_list (\<phi> M\<^sub>n \<psi>) = List.union (subformulas\<^sub>\<nu>_list \<phi>) (subformulas\<^sub>\<nu>_list \<psi>)"
| "subformulas\<^sub>\<nu>_list _ = []"

lemma subformulas\<^sub>\<mu>_list_set:
  "set (subformulas\<^sub>\<mu>_list \<phi>) = subformulas\<^sub>\<mu> \<phi>"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<nu>_list_set:
  "set (subformulas\<^sub>\<nu>_list \<phi>) = subformulas\<^sub>\<nu> \<phi>"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>_list_distinct:
  "distinct (subformulas\<^sub>\<mu>_list \<phi>)"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<nu>_list_distinct:
  "distinct (subformulas\<^sub>\<nu>_list \<phi>)"
  by (induction \<phi>) auto

lemma subformulas\<^sub>\<mu>_list_length:
  "length (subformulas\<^sub>\<mu>_list \<phi>) = card (subformulas\<^sub>\<mu> \<phi>)"
  by (metis subformulas\<^sub>\<mu>_list_set subformulas\<^sub>\<mu>_list_distinct distinct_card)

lemma subformulas\<^sub>\<nu>_list_length:
  "length (subformulas\<^sub>\<nu>_list \<phi>) = card (subformulas\<^sub>\<nu> \<phi>)"
  by (metis subformulas\<^sub>\<nu>_list_set subformulas\<^sub>\<nu>_list_distinct distinct_card)

text \<open>
  We define the list of advice sets as the product of all subsequences
  of the \<open>\<mu>\<close>- and \<open>\<nu>\<close>-subformulas of a formula.
\<close>

definition advice_sets :: "'a ltln \<Rightarrow> ('a ltln list \<times> 'a ltln list) list"
where
  "advice_sets \<phi> = List.product (subseqs (subformulas\<^sub>\<mu>_list \<phi>)) (subseqs (subformulas\<^sub>\<nu>_list \<phi>))"

lemma subset_subseq:
  "X \<subseteq> set ys \<longleftrightarrow> (\<exists>xs. X = set xs \<and> subseq xs ys)"
  by (metis (no_types, lifting) Pow_iff image_iff in_set_subseqs subseqs_powset)

lemma subseqs_subformulas\<^sub>\<mu>_list:
  "X \<subseteq> subformulas\<^sub>\<mu> \<phi> \<longleftrightarrow> (\<exists>xs. X = set xs \<and> xs \<in> set (subseqs (subformulas\<^sub>\<mu>_list \<phi>)))"
  by (metis subset_subseq subformulas\<^sub>\<mu>_list_set in_set_subseqs)

lemma subseqs_subformulas\<^sub>\<nu>_list:
  "Y \<subseteq> subformulas\<^sub>\<nu> \<phi> \<longleftrightarrow> (\<exists>ys. Y = set ys \<and> ys \<in> set (subseqs (subformulas\<^sub>\<nu>_list \<phi>)))"
  by (metis subset_subseq subformulas\<^sub>\<nu>_list_set in_set_subseqs)

lemma advice_sets_subformulas:
  "X \<subseteq> subformulas\<^sub>\<mu> \<phi> \<and> Y \<subseteq> subformulas\<^sub>\<nu> \<phi> \<longleftrightarrow> (\<exists>xs ys. X = set xs \<and> Y = set ys \<and> (xs, ys) \<in> set (advice_sets \<phi>))"
  unfolding advice_sets_def set_product subseqs_subformulas\<^sub>\<mu>_list subseqs_subformulas\<^sub>\<nu>_list by blast

(* TODO add to HOL/List.thy *)
lemma subseqs_not_empty:
  "subseqs xs \<noteq> []"
  by (metis empty_iff list.set(1) subseqs_refl)

(* TODO add to HOL/List.thy *)
lemma product_not_empty:
  "xs \<noteq> [] \<Longrightarrow> ys \<noteq> [] \<Longrightarrow> List.product xs ys \<noteq> []"
  by (induction xs) simp_all

lemma advice_sets_not_empty:
  "advice_sets \<phi> \<noteq> []"
  unfolding advice_sets_def using subseqs_not_empty product_not_empty by blast

lemma advice_sets_length:
  "length (advice_sets \<phi>) \<le> 2 ^ card (subfrmlsn \<phi>)"
  unfolding advice_sets_def length_product length_subseqs subformulas\<^sub>\<mu>_list_length subformulas\<^sub>\<nu>_list_length power_add[symmetric]
  by (metis Suc_1 card_mono lessI power_increasing_iff subformulas\<^sub>\<mu>\<^sub>\<nu>_card subformulas\<^sub>\<mu>\<^sub>\<nu>_subfrmlsn subfrmlsn_finite)

lemma advice_sets_element_length:
  "(xs, ys) \<in> set (advice_sets \<phi>) \<Longrightarrow> length xs \<le> card (subfrmlsn \<phi>)"
  "(xs, ys) \<in> set (advice_sets \<phi>) \<Longrightarrow> length ys \<le> card (subfrmlsn \<phi>)"
  unfolding advice_sets_def set_product
  by (metis SigmaD1 card_mono in_set_subseqs list_emb_length order_trans subformulas\<^sub>\<mu>_list_length subformulas\<^sub>\<mu>_subfrmlsn subfrmlsn_finite)
     (metis SigmaD2 card_mono in_set_subseqs list_emb_length order_trans subformulas\<^sub>\<nu>_list_length subformulas\<^sub>\<nu>_subfrmlsn subfrmlsn_finite)

lemma advice_sets_element_subfrmlsn:
  "(xs, ys) \<in> set (advice_sets \<phi>) \<Longrightarrow> set xs \<subseteq> subformulas\<^sub>\<mu> \<phi>"
  "(xs, ys) \<in> set (advice_sets \<phi>) \<Longrightarrow> set ys \<subseteq> subformulas\<^sub>\<nu> \<phi>"
  unfolding advice_sets_def set_product
  by (meson SigmaD1 subseqs_subformulas\<^sub>\<mu>_list)
     (meson SigmaD2 subseqs_subformulas\<^sub>\<nu>_list)

end


theory flash83Bra  imports flash83Rev

  begin
lemma onInv83:

   assumes

     b1:"r \<in> rules N" and b2:"invf=inv83 "
   shows  "invHoldForRule' s invf r (invariants   N)"
   proof -
have c1:"ex1P N (% iRule1 .  r=NI_Local_GetX_PutX1 N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_GetX  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Replace  iRule1 )\<or>ex0P N (  r=NI_ShWb N )\<or>ex0P N (  r=PI_Local_GetX_GetX2 )\<or>ex0P N (  r=NI_Local_PutXAcksDone )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX7 N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Nak2  iRule1 )\<or>ex0P N (  r=NI_ReplaceHomeShrVld )\<or>ex1P N (% iRule1 .  r=NI_Remote_Put  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX5 N  iRule1 )\<or>ex0P N (  r=NI_Wb )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Get  iRule1 )\<or>ex0P N (  r=PI_Local_Replace )\<or>ex1P N (% iRule1 .  r=NI_ReplaceShrVld  iRule1 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Local_GetX_PutX8 N  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=NI_InvAck_2 N  iRule1 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Remote_Get_Nak2  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=PI_Remote_Replace  iRule1 )\<or>ex0P N (  r=NI_Nak_Home )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Put2  iRule1 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_InvAck_1  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX11 N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX6 N  iRule1 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Remote_Get_Put2  iRule1  iRule2 )\<or>ex0P N (  r=PI_Local_Get_Put )\<or>ex0P N (  r=PI_Local_GetX_PutX1 N )\<or>ex1P N (% iRule1 .  r=NI_InvAck_1_Home  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Remote_Get_Nak1  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Nak1  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_Nak2  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX10_home N  iRule1 )\<or>ex1P N (% iRule1 .  r=PI_Remote_Get  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_Nak3  iRule1 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Local_GetX_PutX10 N  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX2 N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Remote_Get_Put1  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Remote_PutX  iRule1 )\<or>ex1P N (% iRule1 .  r=Store  iRule1 )\<or>ex0P N (  r=NI_FAck )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX3 N  iRule1 )\<or>ex0P N (  r=PI_Local_GetX_PutX3 )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Remote_GetX_PutX  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX8_home N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Put1 N  iRule1 )\<or>ex0P N (  r=PI_Local_GetX_GetX1 )\<or>ex0P N (  r=StoreHome )\<or>ex2P N (% iRule1  iRule2 .  r=NI_Remote_GetX_Nak  iRule1  iRule2 )\<or>ex1P N (% iRule1 .  r=NI_Inv  iRule1 )\<or>ex1P N (% iRule1 .  r=PI_Remote_PutX  iRule1 )\<or>ex0P N (  r=PI_Local_GetX_PutX4 )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX4 N  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Nak  iRule1 )\<or>ex0P N (  r=PI_Local_GetX_PutX2 N )\<or>ex0P N (  r=NI_Local_Put )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_Nak1  iRule1 )\<or>ex0P N (  r=NI_Nak_Clear )\<or>ex0P N (  r=PI_Local_PutX )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Nak3  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Remote_GetX_Nak_Home  iRule1 )\<or>ex0P N (  r=PI_Local_Get_Get )\<or>ex1P N (% iRule1 .  r=NI_Local_GetX_PutX9 N  iRule1 )\<or>ex1P N (% iRule1 .  r=PI_Remote_GetX  iRule1 )\<or>ex0P N (  r=NI_ReplaceHome )\<or>ex1P N (% iRule1 .  r=NI_Remote_GetX_PutX_Home  iRule1 )\<or>ex1P N (% iRule1 .  r=NI_Local_Get_Put3  iRule1 )"

        apply(cut_tac  b1)
        apply auto
        done      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX1 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX1 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX1 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_GetX  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_GetX  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_GetX  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_GetXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Replace  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Replace  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Replace  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_ReplaceVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_ShWb N )
"

         from c1 have c2:" r= NI_ShWb N "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_ShWb N ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_ShWbVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_GetX2 )
"

         from c1 have c2:" r= PI_Local_GetX_GetX2 "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_GetX2 ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_GetX2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= NI_Local_PutXAcksDone )
"

         from c1 have c2:" r= NI_Local_PutXAcksDone "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_PutXAcksDone ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_Local_PutXAcksDoneVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX7 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX7 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX7 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX7VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Nak2  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Nak2  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Nak2  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Nak2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_ReplaceHomeShrVld )
"

         from c1 have c2:" r= NI_ReplaceHomeShrVld "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_ReplaceHomeShrVld ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_ReplaceHomeShrVldVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_Put  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_Put  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_Put  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_PutVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX5 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX5 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX5 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX5VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_Wb )
"

         from c1 have c2:" r= NI_Wb "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_Wb ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_WbVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Get  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Get  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Get  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_GetVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_Replace )
"

         from c1 have c2:" r= PI_Local_Replace "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_Replace ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_ReplaceVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_ReplaceShrVld  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_ReplaceShrVld  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_ReplaceShrVld  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_ReplaceShrVldVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Local_GetX_PutX8 N  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Local_GetX_PutX8 N  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX8 N  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX8VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_InvAck_2 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_InvAck_2 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_InvAck_2 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_InvAck_2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Remote_Get_Nak2  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Remote_Get_Nak2  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_Get_Nak2  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_Get_Nak2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= PI_Remote_Replace  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= PI_Remote_Replace  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (PI_Remote_Replace  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis PI_Remote_ReplaceVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_Nak_Home )
"

         from c1 have c2:" r= NI_Nak_Home "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_Nak_Home ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_Nak_HomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Put2  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Put2  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Put2  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Put2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_InvAck_1  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_InvAck_1  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_InvAck_1  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_InvAck_1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX11 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX11 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX11 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX11VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX6 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX6 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX6 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX6VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Remote_Get_Put2  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Remote_Get_Put2  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_Get_Put2  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_Get_Put2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_Get_Put )
"

         from c1 have c2:" r= PI_Local_Get_Put "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_Get_Put ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_Get_PutVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_PutX1 N )
"

         from c1 have c2:" r= PI_Local_GetX_PutX1 N "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_PutX1 N ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_PutX1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_InvAck_1_Home  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_InvAck_1_Home  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_InvAck_1_Home  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_InvAck_1_HomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_Get_Nak1  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_Get_Nak1  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_Get_Nak1  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_Get_Nak1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Nak1  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Nak1  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Nak1  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Nak1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_Nak2  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_Nak2  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_Nak2  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_Nak2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX10_home N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX10_home N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX10_home N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX10_homeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= PI_Remote_Get  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= PI_Remote_Get  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (PI_Remote_Get  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis PI_Remote_GetVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_Nak3  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_Nak3  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_Nak3  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_Nak3VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Local_GetX_PutX10 N  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Local_GetX_PutX10 N  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX10 N  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX10VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX2 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX2 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX2 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_Get_Put1  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_Get_Put1  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_Get_Put1  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_Get_Put1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_PutX  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_PutX  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_PutX  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_PutXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= Store  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= Store  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (Store  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis StoreVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_FAck )
"

         from c1 have c2:" r= NI_FAck "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_FAck ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_FAckVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX3 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX3 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX3 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX3VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_PutX3 )
"

         from c1 have c2:" r= PI_Local_GetX_PutX3 "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_PutX3 ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_PutX3VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Remote_GetX_PutX  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Remote_GetX_PutX  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_GetX_PutX  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_GetX_PutXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX8_home N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX8_home N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX8_home N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX8_homeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Put1 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Put1 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Put1 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Put1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_GetX1 )
"

         from c1 have c2:" r= PI_Local_GetX_GetX1 "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_GetX1 ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_GetX1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= StoreHome )
"

         from c1 have c2:" r= StoreHome "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (StoreHome ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis StoreHomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex2P N (% iRule1  iRule2 .  r= NI_Remote_GetX_Nak  iRule1  iRule2 )
"

         from c1 obtain  iRule1  iRule2  where c2:"  iRule1~=iRule2    \<and>   iRule1 \<le> N \<and>   iRule2 \<le> N \<and>  r= NI_Remote_GetX_Nak  iRule1  iRule2 "
         by (auto simp add: ex2P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_GetX_Nak  iRule1  iRule2 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_GetX_NakVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Inv  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Inv  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Inv  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_InvVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= PI_Remote_PutX  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= PI_Remote_PutX  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (PI_Remote_PutX  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis PI_Remote_PutXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_PutX4 )
"

         from c1 have c2:" r= PI_Local_GetX_PutX4 "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_PutX4 ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_PutX4VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX4 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX4 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX4 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX4VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Nak  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Nak  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Nak  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_NakVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_GetX_PutX2 N )
"

         from c1 have c2:" r= PI_Local_GetX_PutX2 N "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_GetX_PutX2 N ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_GetX_PutX2VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= NI_Local_Put )
"

         from c1 have c2:" r= NI_Local_Put "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Put ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_Local_PutVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_Nak1  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_Nak1  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_Nak1  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_Nak1VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_Nak_Clear )
"

         from c1 have c2:" r= NI_Nak_Clear "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_Nak_Clear ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_Nak_ClearVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex0P N (  r= PI_Local_PutX )
"

         from c1 have c2:" r= PI_Local_PutX "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_PutX ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_PutXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Nak3  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Nak3  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Nak3  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Nak3VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_GetX_Nak_Home  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_GetX_Nak_Home  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_GetX_Nak_Home  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_GetX_Nak_HomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= PI_Local_Get_Get )
"

         from c1 have c2:" r= PI_Local_Get_Get "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (PI_Local_Get_Get ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis PI_Local_Get_GetVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_GetX_PutX9 N  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_GetX_PutX9 N  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_GetX_PutX9 N  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_GetX_PutX9VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= PI_Remote_GetX  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= PI_Remote_GetX  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (PI_Remote_GetX  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis PI_Remote_GetXVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex0P N (  r= NI_ReplaceHome )
"

         from c1 have c2:" r= NI_ReplaceHome "
         by (auto simp add: ex0P_def)

         have "invHoldForRule' s (inv83 ) (NI_ReplaceHome ) (invariants N) "
            apply(cut_tac    b2 c2 )
            by (metis NI_ReplaceHomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
	}      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Remote_GetX_PutX_Home  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Remote_GetX_PutX_Home  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Remote_GetX_PutX_Home  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Remote_GetX_PutX_HomeVsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }      moreover
        {assume c1: "ex1P N (% iRule1 .  r= NI_Local_Get_Put3  iRule1 )
"

         from c1 obtain  iRule1  where c2:"  iRule1 \<le> N \<and>  r= NI_Local_Get_Put3  iRule1 "
         by (auto simp add: ex1P_def)

         have "invHoldForRule' s (inv83 ) (NI_Local_Get_Put3  iRule1 ) (invariants N) "
            apply(cut_tac  c2  )
            by (metis NI_Local_Get_Put3VsInv83 )
          then have "invHoldForRule' s invf r (invariants N) "
            by(cut_tac c2 b2, metis)
        }ultimately show "invHoldForRule' s invf r (invariants N) "
          by blast
     qed
end