diff --git a/lib/Monads/trace/Trace_Empty_Fail.thy b/lib/Monads/trace/Trace_Empty_Fail.thy new file mode 100644 index 000000000..02dd0b2a3 --- /dev/null +++ b/lib/Monads/trace/Trace_Empty_Fail.thy @@ -0,0 +1,343 @@ +(* + * Copyright 2023, Proofcraft Pty Ltd + * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + *) + +theory Trace_Empty_Fail + imports + Trace_Monad + WPSimp +begin + +section \Monads that are wellformed w.r.t. failure\ + +text \ + Usually, well-formed monads constructed from the primitives in Nondet_Monad will have the following + property: if they return an empty set of results, they will have the failure flag set.\ +definition empty_fail :: "('s,'a) nondet_monad \ bool" where + "empty_fail m \ \s. fst (m s) = {} \ snd (m s)" + +text \Useful in forcing otherwise unknown executions to have the @{const empty_fail} property.\ +definition mk_ef :: "'a set \ bool \ 'a set \ bool" where + "mk_ef S \ (fst S, fst S = {} \ snd S)" + + +subsection \WPC setup\ + +lemma wpc_helper_empty_fail_final: + "empty_fail f \ wpc_helper (P, P', P'') (Q, Q', Q'') (empty_fail f)" + by (clarsimp simp: wpc_helper_def) + +wpc_setup "\m. empty_fail m" wpc_helper_empty_fail_final + + +subsection \@{const empty_fail} intro/dest rules\ + +lemma empty_failI: + "(\s. fst (m s) = {} \ snd (m s)) \ empty_fail m" + by (simp add: empty_fail_def) + +lemma empty_failD: + "\ empty_fail m; fst (m s) = {} \ \ snd (m s)" + by (simp add: empty_fail_def) + +lemma empty_fail_not_snd: + "\ \ snd (m s); empty_fail m \ \ \v. v \ fst (m s)" + by (fastforce simp: empty_fail_def) + +lemmas empty_failD2 = empty_fail_not_snd[rotated] + +lemma empty_failD3: + "\ empty_fail f; \ snd (f s) \ \ fst (f s) \ {}" + by (drule(1) empty_failD2, clarsimp) + +lemma empty_fail_bindD1: + "empty_fail (a >>= b) \ empty_fail a" + unfolding empty_fail_def bind_def + by (fastforce simp: split_def image_image) + + +subsection \Wellformed monads\ + +(* + Collect generic empty_fail lemmas here: + - naming convention is emtpy_fail_NAME. + - add lemmas with assumptions to [empty_fail_cond] set + - add lemmas without assumption to [empty_fail_term] set +*) + +named_theorems empty_fail_term +named_theorems empty_fail_cond + +lemma empty_fail_K_bind[empty_fail_cond]: + "empty_fail f \ empty_fail (K_bind f x)" + by simp + +lemma empty_fail_fun_app[empty_fail_cond]: + "empty_fail (f x) \ empty_fail (f $ x)" + by simp + +(* empty_fail as such does not need context, but empty_fail_select_f does, so we need to build + up context in other rules *) +lemma empty_fail_If[empty_fail_cond]: + "\ P \ empty_fail f; \P \ empty_fail g \ \ empty_fail (if P then f else g)" + by (simp split: if_split) + +lemma empty_fail_If_applied[empty_fail_cond]: + "\ P \ empty_fail (f x); \P \ empty_fail (g x) \ \ empty_fail ((if P then f else g) x)" + by simp + +lemma empty_fail_put[empty_fail_term]: + "empty_fail (put f)" + by (simp add: put_def empty_fail_def) + +lemma empty_fail_modify[empty_fail_term]: + "empty_fail (modify f)" + by (simp add: empty_fail_def simpler_modify_def) + +lemma empty_fail_gets[empty_fail_term]: + "empty_fail (gets f)" + by (simp add: empty_fail_def simpler_gets_def) + +lemma empty_fail_select[empty_fail_cond]: + "S \ {} \ empty_fail (select S)" + by (simp add: empty_fail_def select_def) + +lemma empty_fail_select_f[empty_fail_cond]: + assumes ef: "fst S = {} \ snd S" + shows "empty_fail (select_f S)" + by (fastforce simp add: empty_fail_def select_f_def intro: ef) + +lemma empty_fail_bind[empty_fail_cond]: + "\ empty_fail a; \x. empty_fail (b x) \ \ empty_fail (a >>= b)" + by (fastforce simp: bind_def empty_fail_def split_def) + +lemma empty_fail_return[empty_fail_term]: + "empty_fail (return x)" + by (simp add: empty_fail_def return_def) + +lemma empty_fail_returnOk[empty_fail_term]: + "empty_fail (returnOk v)" + by (fastforce simp: returnOk_def empty_fail_term) + +lemma empty_fail_throwError[empty_fail_term]: + "empty_fail (throwError v)" + by (fastforce simp: throwError_def empty_fail_term) + +lemma empty_fail_lift[empty_fail_cond]: + "\ \x. empty_fail (f x) \ \ empty_fail (lift f x)" + unfolding lift_def + by (auto simp: empty_fail_term split: sum.split) + +lemma empty_fail_liftE[empty_fail_cond]: + "empty_fail f \ empty_fail (liftE f)" + by (simp add: liftE_def empty_fail_cond empty_fail_term) + +lemma empty_fail_bindE[empty_fail_cond]: + "\ empty_fail f; \rv. empty_fail (g rv) \ \ empty_fail (f >>=E g)" + by (simp add: bindE_def empty_fail_cond) + +lemma empty_fail_mapM[empty_fail_cond]: + assumes m: "\x. x \ set xs \ empty_fail (m x)" + shows "empty_fail (mapM m xs)" +using m +proof (induct xs) + case Nil + thus ?case by (simp add: mapM_def sequence_def empty_fail_term) +next + case Cons + have P: "\m x xs. mapM m (x # xs) = (do y \ m x; ys \ (mapM m xs); return (y # ys) od)" + by (simp add: mapM_def sequence_def Let_def) + from Cons + show ?case by (simp add: P m empty_fail_cond empty_fail_term) +qed + +lemma empty_fail_fail[empty_fail_term]: + "empty_fail fail" + by (simp add: fail_def empty_fail_def) + +lemma empty_fail_assert[empty_fail_term]: + "empty_fail (assert P)" + unfolding assert_def by (simp add: empty_fail_term) + +lemma empty_fail_assert_opt[empty_fail_term]: + "empty_fail (assert_opt x)" + by (simp add: assert_opt_def empty_fail_term split: option.splits) + +lemma empty_fail_mk_ef[empty_fail_term]: + "empty_fail (mk_ef o m)" + by (simp add: empty_fail_def mk_ef_def) + +lemma empty_fail_gets_the[empty_fail_term]: + "empty_fail (gets_the f)" + unfolding gets_the_def + by (simp add: empty_fail_cond empty_fail_term) + +lemma empty_fail_gets_map[empty_fail_term]: + "empty_fail (gets_map f p)" + unfolding gets_map_def + by (simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_whenEs[empty_fail_cond]: + "(P \ empty_fail f) \ empty_fail (whenE P f)" + "(\P \ empty_fail f) \ empty_fail (unlessE P f)" + by (auto simp add: whenE_def unlessE_def empty_fail_term) + +lemma empty_fail_assertE[empty_fail_term]: + "empty_fail (assertE P)" + by (simp add: assertE_def empty_fail_term) + +lemma empty_fail_get[empty_fail_term]: + "empty_fail get" + by (simp add: empty_fail_def get_def) + +lemma empty_fail_catch[empty_fail_cond]: + "\ empty_fail f; \x. empty_fail (g x) \ \ empty_fail (catch f g)" + by (simp add: catch_def empty_fail_cond empty_fail_term split: sum.split) + +lemma empty_fail_guard[empty_fail_term]: + "empty_fail (state_assert G)" + by (clarsimp simp: state_assert_def empty_fail_cond empty_fail_term) + +lemma empty_fail_spec[empty_fail_term]: + "empty_fail (state_select F)" + by (clarsimp simp: state_select_def empty_fail_def) + +lemma empty_fail_when[empty_fail_cond]: + "(P \ empty_fail x) \ empty_fail (when P x)" + unfolding when_def + by (simp add: empty_fail_term) + +lemma empty_fail_unless[empty_fail_cond]: + "(\P \ empty_fail f) \ empty_fail (unless P f)" + unfolding unless_def + by (simp add: empty_fail_cond) + +lemma empty_fail_liftM[empty_fail_cond]: + "empty_fail m \ empty_fail (liftM f m)" + unfolding liftM_def + by (fastforce simp: empty_fail_term empty_fail_cond) + +lemma empty_fail_liftME[empty_fail_cond]: + "empty_fail m \ empty_fail (liftME f m)" + unfolding liftME_def + by (simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_handleE[empty_fail_cond]: + "\ empty_fail L; \r. empty_fail (R r) \ \ empty_fail (L R)" + by (clarsimp simp: handleE_def handleE'_def empty_fail_term empty_fail_cond split: sum.splits) + +lemma empty_fail_handle'[empty_fail_cond]: + "\empty_fail f; \e. empty_fail (handler e)\ \ empty_fail (f handler)" + unfolding handleE'_def + by (fastforce simp: empty_fail_term empty_fail_cond split: sum.splits) + +lemma empty_fail_sequence[empty_fail_cond]: + "(\m. m \ set ms \ empty_fail m) \ empty_fail (sequence ms)" + unfolding sequence_def + by (induct ms; simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_sequence_x[empty_fail_cond]: + "(\m. m \ set ms \ empty_fail m) \ empty_fail (sequence_x ms)" + unfolding sequence_x_def + by (induct ms; simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_sequenceE[empty_fail_cond]: + "(\m. m \ set ms \ empty_fail m) \ empty_fail (sequenceE ms)" + unfolding sequenceE_def + by (induct ms; simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_sequenceE_x[empty_fail_cond]: + "(\m. m \ set ms \ empty_fail m) \ empty_fail (sequenceE_x ms)" + unfolding sequenceE_x_def + by (induct ms; simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_mapM_x[empty_fail_cond]: + "(\m. m \ f ` set ms \ empty_fail m) \ empty_fail (mapM_x f ms)" + unfolding mapM_x_def + by (fastforce intro: empty_fail_term empty_fail_cond) + +lemma empty_fail_mapME[empty_fail_cond]: + "(\m. m \ f ` set xs \ empty_fail m) \ empty_fail (mapME f xs)" + unfolding mapME_def + by (fastforce intro: empty_fail_term empty_fail_cond) + +lemma empty_fail_mapME_x[empty_fail_cond]: + "(\m'. m' \ f ` set xs \ empty_fail m') \ empty_fail (mapME_x f xs)" + unfolding mapME_x_def + by (fastforce intro: empty_fail_term empty_fail_cond) + +lemma empty_fail_filterM[empty_fail_cond]: + "(\m. m \ set ms \ empty_fail (P m)) \ empty_fail (filterM P ms)" + by (induct ms; simp add: empty_fail_term empty_fail_cond) + +lemma empty_fail_zipWithM_x[empty_fail_cond]: + "(\x y. empty_fail (f x y)) \ empty_fail (zipWithM_x f xs ys)" + unfolding zipWithM_x_def zipWith_def + by (fastforce intro: empty_fail_term empty_fail_cond) + +lemma empty_fail_zipWithM[empty_fail_cond]: + "(\x y. empty_fail (f x y)) \ empty_fail (zipWithM f xs ys)" + unfolding zipWithM_def zipWith_def + by (fastforce intro: empty_fail_term empty_fail_cond) + +lemma empty_fail_maybeM[empty_fail_cond]: + "\x. empty_fail (f x) \ empty_fail (maybeM f t)" + unfolding maybeM_def + by (fastforce intro: empty_fail_term split: option.splits) + +lemma empty_fail_ifM[empty_fail_cond]: + "\ empty_fail P; empty_fail a; empty_fail b \ \ empty_fail (ifM P a b)" + by (simp add: ifM_def empty_fail_cond) + +lemma empty_fail_ifME[empty_fail_cond]: + "\ empty_fail P; empty_fail a; empty_fail b \ \ empty_fail (ifME P a b)" + by (simp add: ifME_def empty_fail_cond) + +lemma empty_fail_whenM[empty_fail_cond]: + "\ empty_fail P; empty_fail f \ \ empty_fail (whenM P f)" + by (simp add: whenM_def empty_fail_term empty_fail_cond) + +lemma empty_fail_andM[empty_fail_cond]: + "\ empty_fail A; empty_fail B \ \ empty_fail (andM A B)" + by (simp add: andM_def empty_fail_term empty_fail_cond) + +lemma empty_fail_orM[empty_fail_cond]: + "\ empty_fail A; empty_fail B \ \ empty_fail (orM A B)" + by (simp add: orM_def empty_fail_term empty_fail_cond) + +lemma empty_fail_notM[empty_fail_cond]: + "empty_fail A \ empty_fail (notM A)" + by (simp add: notM_def empty_fail_term empty_fail_cond) + +(* not everything [simp] by default, because side conditions can slow down simp a lot *) +lemmas empty_fail[wp, intro!] = empty_fail_term empty_fail_cond +lemmas [simp] = empty_fail_term + + +subsection \Equations and legacy names\ + +lemma empty_fail_select_eq[simp]: + "empty_fail (select V) = (V \ {})" + by (clarsimp simp: select_def empty_fail_def) + +lemma empty_fail_liftM_eq[simp]: + "empty_fail (liftM f m) = empty_fail m" + unfolding liftM_def + by (fastforce dest: empty_fail_bindD1) + +lemma empty_fail_liftE_eq[simp]: + "empty_fail (liftE f) = empty_fail f" + by (fastforce simp: liftE_def empty_fail_def bind_def) + +lemma liftME_empty_fail_eq[simp]: + "empty_fail (liftME f m) = empty_fail m" + unfolding liftME_def + by (fastforce dest: empty_fail_bindD1 simp: bindE_def) + +(* legacy name binding *) +lemmas empty_fail_error_bits = empty_fail_returnOk empty_fail_throwError empty_fail_liftE_eq + +end diff --git a/lib/Monads/trace/Trace_In_Monad.thy b/lib/Monads/trace/Trace_In_Monad.thy index 073d4096b..24769154c 100644 --- a/lib/Monads/trace/Trace_In_Monad.thy +++ b/lib/Monads/trace/Trace_In_Monad.thy @@ -54,6 +54,10 @@ lemma inl_whenE: "((Inl x, s') \ mres (whenE P f s)) = (P \ (Inl x, s') \ mres (f s))" by (auto simp add: in_whenE) +lemma inr_in_unlessE_throwError[termination_simp]: + "(Inr (), s') \ fst (unlessE P (throwError E) s) = (P \ s'=s)" + by (simp add: unlessE_def returnOk_def throwError_def return_def) + lemma in_fail: "r \ mres (fail s) = False" by (simp add: fail_def mres_def) diff --git a/lib/Monads/trace/Trace_Lemmas.thy b/lib/Monads/trace/Trace_Lemmas.thy index 9ce793eea..f6e7706f3 100644 --- a/lib/Monads/trace/Trace_Lemmas.thy +++ b/lib/Monads/trace/Trace_Lemmas.thy @@ -30,17 +30,169 @@ lemma bind_apply_cong': lemmas bind_apply_cong = bind_apply_cong'[rule_format, fundef_cong] +lemma bind_cong[fundef_cong]: + "\ f = f'; \v s s'. (v, s') \ fst (f' s) \ g v s' = g' v s' \ \ f >>= g = f' >>= g'" + by (auto simp: bind_def Let_def split_def intro: rev_image_eqI) + +lemma bindE_cong[fundef_cong]: + "\ M = M' ; \v s s'. (Inr v, s') \ fst (M' s) \ N v s' = N' v s' \ \ bindE M N = bindE M' N'" + by (auto simp: bindE_def lift_def split: sum.splits intro!: bind_cong) + +lemma bindE_apply_cong[fundef_cong]: + "\ f s = f' s'; \rv st. (Inr rv, st) \ fst (f' s') \ g rv st = g' rv st \ + \ (f >>=E g) s = (f' >>=E g') s'" + by (auto simp: bindE_def lift_def split: sum.splits intro!: bind_apply_cong) + +lemma K_bind_apply_cong[fundef_cong]: + "\ f st = f' st' \ \ K_bind f arg st = K_bind f' arg' st'" + by simp + +lemma when_apply_cong[fundef_cong]: + "\ C = C'; s = s'; C' \ m s' = m' s' \ \ when C m s = when C' m' s'" + by (simp add: when_def) + +lemma unless_apply_cong[fundef_cong]: + "\ C = C'; s = s'; \ C' \ m s' = m' s' \ \ unless C m s = unless C' m' s'" + by (simp add: when_def unless_def) + +lemma whenE_apply_cong[fundef_cong]: + "\ C = C'; s = s'; C' \ m s' = m' s' \ \ whenE C m s = whenE C' m' s'" + by (simp add: whenE_def) + +lemma unlessE_apply_cong[fundef_cong]: + "\ C = C'; s = s'; \ C' \ m s' = m' s' \ \ unlessE C m s = unlessE C' m' s'" + by (simp add: unlessE_def) subsection \Simplifying Monads\ +lemma nested_bind[simp]: + "do x <- do y <- f; return (g y) od; h x od = do y <- f; h (g y) od" + by (clarsimp simp: bind_def Let_def split_def return_def) + +lemma bind_dummy_ret_val: + "do y \ a; b od = do a; b od" + by simp + lemma fail_update[iff]: "fail (f s) = fail s" by (simp add: fail_def) +lemma fail_bind[simp]: + "fail >>= f = fail" + by (simp add: bind_def fail_def) + +lemma fail_bindE[simp]: + "fail >>=E f = fail" + by (simp add: bindE_def bind_def fail_def) + +lemma assert_A_False[simp]: + "assert False = fail" + by (simp add: assert_def) + lemma assert_A_True[simp]: "assert True = return ()" by (simp add: assert_def) +lemma assert_False[simp]: + "assert False >>= f = fail" + by simp + +lemma assert_True[simp]: + "assert True >>= f = f ()" + by simp + +lemma assertE_False[simp]: + "assertE False >>=E f = fail" + by (simp add: assertE_def) + +lemma assertE_True[simp]: + "assertE True >>=E f = f ()" + by (simp add: assertE_def) + +lemma when_False_bind[simp]: + "when False g >>= f = f ()" + by (rule ext) (simp add: when_def bind_def return_def) + +lemma when_True_bind[simp]: + "when True g >>= f = g >>= f" + by (simp add: when_def bind_def return_def) + +lemma whenE_False_bind[simp]: + "whenE False g >>=E f = f ()" + by (simp add: whenE_def bindE_def returnOk_def lift_def) + +lemma whenE_True_bind[simp]: + "whenE True g >>=E f = g >>=E f" + by (simp add: whenE_def bindE_def returnOk_def lift_def) + +lemma when_True[simp]: + "when True X = X" + by (clarsimp simp: when_def) + +lemma when_False[simp]: + "when False X = return ()" + by (clarsimp simp: when_def) + +lemma unless_False[simp]: + "unless False X = X" + by (clarsimp simp: unless_def) + +lemma unlessE_False[simp]: + "unlessE False f = f" + unfolding unlessE_def by fastforce + +lemma unless_True[simp]: + "unless True X = return ()" + by (clarsimp simp: unless_def) + +lemma unlessE_True[simp]: + "unlessE True f = returnOk ()" + unfolding unlessE_def by fastforce + +lemma unlessE_whenE: + "unlessE P = whenE (\P)" + by (rule ext) (simp add: unlessE_def whenE_def) + +lemma unless_when: + "unless P = when (\P)" + by (rule ext) (simp add: unless_def when_def) + +lemma gets_to_return[simp]: + "gets (\s. v) = return v" + by (clarsimp simp: gets_def put_def get_def bind_def return_def) + +lemma assert_opt_Some: + "assert_opt (Some x) = return x" + by (simp add: assert_opt_def) + +lemma assertE_liftE: + "assertE P = liftE (assert P)" + by (simp add: assertE_def assert_def liftE_def returnOk_def) + +lemma liftE_handleE'[simp]: + "(liftE a b) = liftE a" + by (clarsimp simp: liftE_def handleE'_def) + +lemma liftE_handleE[simp]: + "(liftE a b) = liftE a" + unfolding handleE_def by simp + +lemma alternative_bind: + "((a \ b) >>= c) = ((a >>= c) \ (b >>= c))" + by (fastforce simp add: alternative_def bind_def split_def) + +lemma alternative_refl: + "(a \ a) = a" + by (simp add: alternative_def) + +lemma alternative_com: + "(f \ g) = (g \ f)" + by (auto simp: alternative_def) + +lemma liftE_alternative: + "liftE (a \ b) = (liftE a \ liftE b)" + by (simp add: liftE_def alternative_bind) + subsection \Lifting and Alternative Basic Definitions\ @@ -65,7 +217,7 @@ lemma liftM_id[simp]: by (auto simp: liftM_def) lemma liftM_bind: - "liftM t f >>= g = (f >>= (\x. g (t x)))" + "liftM t f >>= g = f >>= (\x. g (t x))" by (simp add: liftM_def bind_assoc) lemma gets_bind_ign: @@ -86,4 +238,33 @@ lemma bind_eqI: "\ f = f'; \x. g x = g' x \ \ f >>= g = f' >>= g'" by (auto simp: bind_def split_def) +lemma condition_split: + "P (condition C a b s) \ (C s \ P (a s)) \ (\C s \ P (b s))" + by (clarsimp simp: condition_def) + +lemma condition_split_asm: + "P (condition C a b s) \ (\(C s \ \ P (a s) \ \C s \ \P (b s)))" + by (clarsimp simp: condition_def) + +lemmas condition_splits = condition_split condition_split_asm + +lemma condition_true_triv[simp]: + "condition (\_. True) A B = A" + by (fastforce split: condition_splits) + +lemma condition_false_triv[simp]: + "condition (\_. False) A B = B" + by (fastforce split: condition_splits) + +lemma condition_true: + "P s \ condition P A B s = A s" + by (clarsimp simp: condition_def) + +lemma condition_false: + "\ P s \ condition P A B s = B s" + by (clarsimp simp: condition_def) + +lemmas arg_cong_bind = arg_cong2[where f=bind] +lemmas arg_cong_bind1 = arg_cong_bind[OF refl ext] + end diff --git a/lib/Monads/trace/Trace_Monad.thy b/lib/Monads/trace/Trace_Monad.thy index 8ddf802f4..37f7a70ea 100644 --- a/lib/Monads/trace/Trace_Monad.thy +++ b/lib/Monads/trace/Trace_Monad.thy @@ -46,6 +46,23 @@ text \ pair of result and state when the computation did not fail.\ type_synonym ('s, 'a) tmonad = "'s \ ((tmid \ 's) list \ ('s, 'a) tmres) set" +text \ + Print the type @{typ "('s,'a) nondet_monad"} instead of its unwieldy expansion. + Needs an AST translation in code, because it needs to check that the state variable + @{typ 's} occurs twice. This comparison is not guaranteed to always work as expected + (AST instances might have different decoration), but it does seem to work here.\ +print_ast_translation \ + let + fun monad_tr _ [t1, Ast.Appl [Ast.Constant @{type_syntax prod}, + Ast.Appl [Ast.Constant @{type_syntax set}, + Ast.Appl [Ast.Constant @{type_syntax prod}, t2, t3]], + Ast.Constant @{type_syntax bool}]] = + if t3 = t1 + then Ast.Appl [Ast.Constant @{type_syntax "nondet_monad"}, t1, t2] + else raise Match + in [(@{type_syntax "fun"}, monad_tr)] end +\ + text \Returns monad results, ignoring failures and traces.\ definition mres :: "((tmid \ 's) list \ ('s, 'a) tmres) set \ ('a \ 's) set" where "mres r = Result -` (snd ` r)" @@ -124,6 +141,12 @@ text \ FIXME: The @{text select_f} function was left out here until we figure out what variant we actually need.\ +text \@{text select_state} takes a relationship between + states, and outputs nondeterministically a state + related to the input state.\ +definition state_select :: "('s \ 's) set \ ('s, unit) nondet_monad" where + "state_select r \ \s. ((\x. ((), x)) ` {s'. (s, s') \ r}, \ (\s'. (s, s') \ r))" + subsection "Failure" text \ @@ -185,6 +208,13 @@ text \ definition gets_the :: "('s \ 'a option) \ ('s, 'a) tmonad" where "gets_the f \ gets f >>= assert_opt" +text \ + Get a map (such as a heap) from the current state and apply an argument to the map. + Fail if the map returns @{const None}, otherwise return the value.\ +definition + gets_map :: "('s \ 'a \ 'b option) \ 'a \ ('s, 'b) nondet_monad" where + "gets_map f p \ gets f >>= (\m. assert_opt (m p))" + subsection \The Monad Laws\ @@ -490,6 +520,10 @@ text \The same for the exception monad:\ definition liftME :: "('a \ 'b) \ ('s,'e+'a) tmonad \ ('s,'e+'b) tmonad" where "liftME f m \ doE x \ m; returnOk (f x) odE" +text \ Execute @{term f} for @{term "Some x"}, otherwise do nothing. \ +definition maybeM :: "('a \ ('s, unit) nondet_monad) \ 'a option \ ('s, unit) nondet_monad" where + "maybeM f y \ case y of Some x \ f x | None \ return ()" + text \Run a sequence of monads from left to right, ignoring return values.\ definition sequence_x :: "('s, 'a) tmonad list \ ('s, unit) tmonad" where "sequence_x xs \ foldr (\x y. x >>= (\_. y)) xs (return ())" @@ -678,6 +712,48 @@ definition whileLoopE :: notation (output) whileLoopE ("(whileLoopE (_)// (_))" [1000, 1000] 1000) + +section "Combinators that have conditions with side effects" + +definition notM :: "('s, bool) nondet_monad \ ('s, bool) nondet_monad" where + "notM m = do c \ m; return (\ c) od" + +definition + whileM :: "('s, bool) nondet_monad \ ('s, 'a) nondet_monad \ ('s, unit) nondet_monad" where + "whileM C B \ do + c \ C; + whileLoop (\c s. c) (\_. do B; C od) c; + return () + od" + +definition + ifM :: "('s, bool) nondet_monad \ ('s, 'a) nondet_monad \ ('s, 'a) nondet_monad \ + ('s, 'a) nondet_monad" where + "ifM test t f = do + c \ test; + if c then t else f + od" + +definition ifME :: + "('a, 'b + bool) nondet_monad \ ('a, 'b + 'c) nondet_monad \ ('a, 'b + 'c) nondet_monad + \ ('a, 'b + 'c) nondet_monad" where + "ifME test t f = doE + c \ test; + if c then t else f + odE" + +definition + whenM :: "('s, bool) nondet_monad \ ('s, unit) nondet_monad \ ('s, unit) nondet_monad" where + "whenM t m = ifM t m (return ())" + +definition + orM :: "('s, bool) nondet_monad \ ('s, bool) nondet_monad \ ('s, bool) nondet_monad" where + "orM a b = ifM a (return True) b" + +definition + andM :: "('s, bool) nondet_monad \ ('s, bool) nondet_monad \ ('s, bool) nondet_monad" where + "andM a b = ifM a b (return False)" + subsection "Await command" text \@{term "Await c f"} blocks the execution until @{term "c"} is true, diff --git a/lib/Monads/trace/Trace_Monad_Equations.thy b/lib/Monads/trace/Trace_Monad_Equations.thy index ee0730def..265b8c73c 100644 --- a/lib/Monads/trace/Trace_Monad_Equations.thy +++ b/lib/Monads/trace/Trace_Monad_Equations.thy @@ -10,11 +10,350 @@ theory Trace_Monad_Equations imports - Trace_Lemmas + Trace_No_Fail begin +lemmas assertE_assert = assertE_liftE + +lemma assert_def2: + "assert v = assert_opt (if v then Some () else None)" + by (cases v; simp add: assert_def assert_opt_def) + +lemma return_returnOk: + "return (Inr x) = returnOk x" + unfolding returnOk_def by simp + +lemma exec_modify: + "(modify f >>= g) s = g () (f s)" + by (simp add: bind_def simpler_modify_def) + +lemma bind_return_eq: + "(a >>= return) = (b >>= return) \ a = b" + apply (clarsimp simp:bind_def) + apply (rule ext) + apply (drule_tac x= x in fun_cong) + apply (auto simp:return_def split_def) + done + lemmas bind_then_eq = arg_cong2[where f=bind, OF _ refl] +lemma bindE_bind_linearise: + "((f >>=E g) >>= h) = + (f >>= case_sum (h o Inl) (\rv. g rv >>= h))" + apply (simp add: bindE_def bind_assoc) + apply (rule ext, rule bind_apply_cong, rule refl) + apply (simp add: lift_def throwError_def split: sum.split) + done + +lemma throwError_bind: + "(throwError e >>= f) = (f (Inl e))" + by (simp add: throwError_def) + +lemma bind_bindE_assoc: + "((f >>= g) >>=E h) + = f >>= (\rv. g rv >>=E h)" + by (simp add: bindE_def bind_assoc) + +lemma returnOk_bind: + "returnOk v >>= f = (f (Inr v))" + by (simp add: returnOk_def) + +lemma liftE_bind: + "(liftE m >>= m') = (m >>= (\rv. m' (Inr rv)))" + by (simp add: liftE_def) + +lemma catch_throwError: "catch (throwError ft) g = g ft" + by (simp add: catch_def throwError_bind) + +lemma cart_singleton_image: + "S \ {s} = (\v. (v, s)) ` S" + by auto + +lemma liftE_bindE_handle: + "((liftE f >>=E (\x. g x)) h) + = f >>= (\x. g x h)" + by (simp add: liftE_bindE handleE_def handleE'_def + bind_assoc) + +lemma catch_liftE: + "catch (liftE g) h = g" + by (simp add: catch_def liftE_def) + +lemma catch_liftE_bindE: + "catch (liftE g >>=E (\x. f x)) h = g >>= (\x. catch (f x) h)" + by (simp add: liftE_bindE catch_def bind_assoc) + +lemma returnOk_catch_bind: + "catch (returnOk v) h >>= g = g v" + by (simp add: returnOk_liftE catch_liftE) + +lemma liftE_bindE_assoc: + "(liftE f >>=E g) >>= h = f >>= (\x. g x >>= h)" + by (simp add: liftE_bindE bind_assoc) + +lemma unlessE_throw_catch_If: + "catch (unlessE P (throwError e) >>=E f) g + = (if P then catch (f ()) g else g e)" + by (simp add: unlessE_def catch_throwError split: if_split) + +lemma whenE_bindE_throwError_to_if: + "whenE P (throwError e) >>=E (\_. b) = (if P then (throwError e) else b)" + unfolding whenE_def bindE_def + by (auto simp: Nondet_Monad.lift_def throwError_def returnOk_def) + +lemma alternative_liftE_returnOk: + "(liftE m \ returnOk v) = liftE (m \ return v)" + by (simp add: liftE_def alternative_def returnOk_def bind_def return_def) + +lemma gets_the_return: + "(return x = gets_the f) = (\s. f s = Some x)" + apply (subst fun_eq_iff) + apply (simp add: return_def gets_the_def exec_gets + assert_opt_def fail_def + split: option.split) + apply auto + done + +lemma gets_the_returns: + "(return x = gets_the f) = (\s. f s = Some x)" + "(returnOk x = gets_the g) = (\s. g s = Some (Inr x))" + "(throwError x = gets_the h) = (\s. h s = Some (Inl x))" + by (simp_all add: returnOk_def throwError_def + gets_the_return) + +lemma all_rv_choice_fn_eq_pred: + "\ \rv. P rv \ \fn. f rv = g fn \ \ \fn. \rv. P rv \ f rv = g (fn rv)" + apply (rule_tac x="\rv. SOME h. f rv = g h" in exI) + apply (clarsimp split: if_split) + by (meson someI_ex) + +lemma all_rv_choice_fn_eq: + "\ \rv. \fn. f rv = g fn \ + \ \fn. f = (\rv. g (fn rv))" + using all_rv_choice_fn_eq_pred[where f=f and g=g and P=\] + by (simp add: fun_eq_iff) + +lemma gets_the_eq_bind: + "\ \fn. f = gets_the (fn o fn'); \rv. \fn. g rv = gets_the (fn o fn') \ + \ \fn. (f >>= g) = gets_the (fn o fn')" + apply (clarsimp dest!: all_rv_choice_fn_eq) + apply (rule_tac x="\s. case (fn s) of None \ None | Some v \ fna v s" in exI) + apply (simp add: gets_the_def bind_assoc exec_gets + assert_opt_def fun_eq_iff + split: option.split) + done + +lemma gets_the_eq_bindE: + "\ \fn. f = gets_the (fn o fn'); \rv. \fn. g rv = gets_the (fn o fn') \ + \ \fn. (f >>=E g) = gets_the (fn o fn')" + apply (simp add: bindE_def) + apply (erule gets_the_eq_bind) + apply (simp add: lift_def gets_the_returns split: sum.split) + apply fastforce + done + +lemma gets_the_fail: + "(fail = gets_the f) = (\s. f s = None)" + by (simp add: gets_the_def exec_gets assert_opt_def + fail_def return_def fun_eq_iff + split: option.split) + +lemma gets_the_asserts: + "(fail = gets_the f) = (\s. f s = None)" + "(assert P = gets_the g) = (\s. g s = (if P then Some () else None))" + "(assertE P = gets_the h) = (\s. h s = (if P then Some (Inr ()) else None))" + by (simp add: assert_def assertE_def gets_the_fail gets_the_returns + split: if_split)+ + +lemma ex_const_function: + "\f. \s. f (f' s) = v" + by force + +lemma gets_the_condsE: + "(\fn. whenE P f = gets_the (fn o fn')) + = (P \ (\fn. f = gets_the (fn o fn')))" + "(\fn. unlessE P g = gets_the (fn o fn')) + = (\ P \ (\fn. g = gets_the (fn o fn')))" + by (simp add: whenE_def unlessE_def gets_the_returns ex_const_function + split: if_split)+ + +lemma let_into_return: + "(let f = x in m f) = (do f \ return x; m f od)" + by simp + +lemma liftME_return: + "liftME f (returnOk v) = returnOk (f v)" + by (simp add: liftME_def) + +lemma fold_bindE_into_list_case: + "(doE v \ f; case_list (g v) (h v) x odE) + = (case_list (doE v \ f; g v odE) (\x xs. doE v \ f; h v x xs odE) x)" + by (simp split: list.split) + +lemma whenE_liftE: + "whenE P (liftE f) = liftE (when P f)" + by (simp add: whenE_def when_def returnOk_liftE) + +lemma whenE_whenE_body: + "whenE P (throwError f) >>=E (\_. whenE Q (throwError f) >>=E r) = whenE (P \ Q) (throwError f) >>=E r" + apply (cases P) + apply (simp add: whenE_def) + apply simp + done + +lemma whenE_whenE_same: + "whenE P (throwError f) >>=E (\_. whenE P (throwError g) >>=E r) = whenE P (throwError f) >>=E r" + apply (cases P) + apply (simp add: whenE_def) + apply simp + done + +lemma maybe_fail_bind_fail: + "unless P fail >>= (\_. fail) = fail" + "when P fail >>= (\_. fail) = fail" + by (clarsimp simp: bind_def fail_def return_def + unless_def when_def)+ + +lemma select_singleton[simp]: + "select {x} = return x" + by (fastforce simp add: fun_eq_iff select_def return_def) + +lemma return_modify: + "return () = modify id" + by (simp add: return_def simpler_modify_def) + +lemma liftE_liftM_liftME: + "liftE (liftM f m) = liftME f (liftE m)" + by (simp add: liftE_liftM liftME_liftM liftM_def) + +lemma bind_return_unit: + "f = (f >>= (\x. return ()))" + by simp + +lemma modify_id_return: + "modify id = return ()" + by (simp add: simpler_modify_def return_def) + + +lemma liftE_bind_return_bindE_returnOk: + "liftE (v >>= (\rv. return (f rv))) + = (liftE v >>=E (\rv. returnOk (f rv)))" + by (simp add: liftE_bindE, simp add: liftE_def returnOk_def) + +lemma bind_eqI: + "g = g' \ f >>= g = f >>= g'" by simp + +lemma unlessE_throwError_returnOk: + "(if P then returnOk v else throwError x) + = (unlessE P (throwError x) >>=E (\_. returnOk v))" + by (cases P, simp_all add: unlessE_def) + +lemma gets_the_bind_eq: + "\ f s = Some x; g x s = h s \ + \ (gets_the f >>= g) s = h s" + by (simp add: gets_the_def bind_assoc exec_gets assert_opt_def) + +lemma zipWithM_x_modify: + "zipWithM_x (\a b. modify (f a b)) as bs + = modify (\s. foldl (\s (a, b). f a b s) s (zip as bs))" + apply (simp add: zipWithM_x_def zipWith_def sequence_x_def) + apply (induct ("zip as bs")) + apply (simp add: simpler_modify_def return_def) + apply (rule ext) + apply (simp add: simpler_modify_def bind_def split_def) + done + +lemma assert2: + "(do v1 \ assert P; v2 \ assert Q; c od) + = (do v \ assert (P \ Q); c od)" + by (simp add: assert_def split: if_split) + +lemma assert_opt_def2: + "assert_opt v = (do assert (v \ None); return (the v) od)" + by (simp add: assert_opt_def split: option.split) + +lemma gets_assert: + "(do v1 \ assert v; v2 \ gets f; c v1 v2 od) + = (do v2 \ gets f; v1 \ assert v; c v1 v2 od)" + by (simp add: simpler_gets_def return_def assert_def fail_def bind_def + split: if_split) + +lemma modify_assert: + "(do v2 \ modify f; v1 \ assert v; c v1 od) + = (do v1 \ assert v; v2 \ modify f; c v1 od)" + by (simp add: simpler_modify_def return_def assert_def fail_def bind_def + split: if_split) + +lemma gets_fold_into_modify: + "do x \ gets f; modify (g x) od = modify (\s. g (f s) s)" + "do x \ gets f; _ \ modify (g x); h od + = do modify (\s. g (f s) s); h od" + by (simp_all add: fun_eq_iff modify_def bind_assoc exec_gets + exec_get exec_put) + +lemma bind_assoc2: + "(do x \ a; _ \ b; c x od) = (do x \ (do x' \ a; _ \ b; return x' od); c x od)" + by (simp add: bind_assoc) + +lemma bind_assoc_return_reverse: + "do x \ f; + _ \ g x; + h x + od = + do x \ do x \ f; + _ \ g x; + return x + od; + h x + od" + by (simp only: bind_assoc return_bind) + +lemma if_bind: + "(if P then (a >>= (\_. b)) else return ()) = + (if P then a else return ()) >>= (\_. if P then b else return ())" + by (cases P; simp) + +lemma bind_liftE_distrib: "(liftE (A >>= (\x. B x))) = (liftE A >>=E (\x. liftE (\s. B x s)))" + by (clarsimp simp: liftE_def bindE_def lift_def bind_assoc) + +lemma if_catch_distrib: + "((if P then f else g) h) = (if P then f h else g h)" + by (simp split: if_split) + +lemma will_throw_and_catch: + "f = throwError e \ (f (\_. g)) = g" + by (simp add: catch_def throwError_def) + +lemma catch_is_if: + "(doE x <- f; g x odE h) = + do + rv <- f; + if sum.isl rv then h (projl rv) else g (projr rv) h + od" + apply (simp add: bindE_def catch_def bind_assoc cong: if_cong) + apply (rule bind_cong, rule refl) + apply (clarsimp simp: Nondet_Monad.lift_def throwError_def split: sum.splits) + done + +lemma liftE_K_bind: "liftE ((K_bind (\s. A s)) x) = K_bind (liftE (\s. A s)) x" + by clarsimp + +lemma monad_eq_split_tail: + "\f = g; a s = b s\ \ (a >>= f) s = ((b >>= g) s)" + by (simp add:bind_def) + +lemma assert_opt_If: + "assert_opt v = If (v = None) fail (return (the v))" + by (simp add: assert_opt_def split: option.split) + +lemma if_to_top_of_bind: + "(bind (If P x y) z) = If P (bind x z) (bind y z)" + by (simp split: if_split) + +lemma if_to_top_of_bindE: + "(bindE (If P x y) z) = If P (bindE x z) (bindE y z)" + by (simp split: if_split) + lemma modify_id: "modify id = return ()" by (simp add: modify_def get_def bind_def put_def return_def) diff --git a/lib/Monads/trace/Trace_More_VCG.thy b/lib/Monads/trace/Trace_More_VCG.thy index 9a12ec1ee..27b5d9d34 100644 --- a/lib/Monads/trace/Trace_More_VCG.thy +++ b/lib/Monads/trace/Trace_More_VCG.thy @@ -13,8 +13,815 @@ theory Trace_More_VCG Trace_VCG begin +lemma hoare_take_disjunct: + "\P\ f \\rv s. P' rv s \ (False \ P'' rv s)\ + \ \P\ f \P''\" + by (erule hoare_strengthen_post, simp) + +lemma hoare_post_add: + "\P\ S \\r s. R r s \ Q r s\ \ \P\ S \Q\" + by (erule hoare_strengthen_post, simp) + +lemma hoare_post_addE: + "\P\ f \\_ s. R s \ Q s\, \T\ \ \P\ f \\_ s. Q s\, \T\" + by (erule hoare_post_impErr'; simp) + +lemma hoare_pre_add: + "(\s. P s \ R s) \ (\P\ f \Q\ \ \P and R\ f \Q\)" + apply (subst iff_conv_conj_imp) + by(intro conjI impI; rule hoare_weaken_pre, assumption, clarsimp) + +lemma hoare_pre_addE: + "(\s. P s \ R s) \ (\P\ f \Q\, \S\ \ \P and R\ f \Q\, \S\)" + apply (subst iff_conv_conj_imp) + by(intro conjI impI; rule hoare_weaken_preE, assumption, clarsimp) + +lemma hoare_name_pre_state: + "\ \s. P s \ \(=) s\ f \Q\ \ \ \P\ f \Q\" + by (clarsimp simp: valid_def) + +lemma hoare_name_pre_stateE: + "\\s. P s \ \(=) s\ f \Q\, \E\\ \ \P\ f \Q\, \E\" + by (clarsimp simp: validE_def2) + +lemma valid_prove_more: (* FIXME: duplicate *) + "\P\ f \\rv s. Q rv s \ Q' rv s\ \ \P\ f \Q'\" + by (rule hoare_post_add) + +lemma hoare_vcg_if_lift: + "\R\ f \\rv s. (P \ X rv s) \ (\P \ Y rv s)\ \ + \R\ f \\rv s. if P then X rv s else Y rv s\" + + "\R\ f \\rv s. (P \ X rv s) \ (\P \ Y rv s)\ \ + \R\ f \\rv. if P then X rv else Y rv\" + by (auto simp: valid_def split_def) + +lemma hoare_vcg_if_lift_strong: + "\ \P'\ f \P\; \\s. \ P' s\ f \\rv s. \ P rv s\; \Q'\ f \Q\; \R'\ f \R\ \ \ + \\s. if P' s then Q' s else R' s\ f \\rv s. if P rv s then Q rv s else R rv s\" + + "\ \P'\ f \P\; \\s. \ P' s\ f \\rv s. \ P rv s\; \Q'\ f \ Q\; \R'\ f \R\ \ \ + \\s. if P' s then Q' s else R' s\ f \\rv s. (if P rv s then Q rv else R rv) s\" + by (wpsimp wp: hoare_vcg_imp_lift' | assumption | fastforce)+ + +lemma hoare_vcg_imp_lift_pre_add: + "\ \P and Q\ f \\rv s. R rv s\; f \\s. \ Q s\ \ \ \P\ f \\rv s. Q s \ R rv s\" + apply (rule hoare_weaken_pre) + apply (rule hoare_vcg_imp_lift') + apply fastforce + apply fastforce + apply (clarsimp simp: pred_conj_def valid_def) + done + lemma hoare_pre_tautI: "\ \A and P\ a \B\; \A and not P\ a \B\ \ \ \A\ a \B\" by (fastforce simp: valid_def split_def pred_conj_def pred_neg_def) +lemma hoare_lift_Pf_pre_conj: + assumes P: "\x. \\s. Q x s\ m \P x\" + assumes f: "\P. \\s. P (g s) \ R s\ m \\_ s. P (f s)\" + shows "\\s. Q (g s) s \ R s\ m \\rv s. P (f s) rv s\" + apply (clarsimp simp: valid_def) + apply (rule use_valid [OF _ P], simp) + apply (rule use_valid [OF _ f], simp, simp) + done + +lemmas hoare_lift_Pf4 = hoare_lift_Pf_pre_conj[where R=\, simplified] +lemmas hoare_lift_Pf3 = hoare_lift_Pf4[where f=f and g=f for f] +lemmas hoare_lift_Pf2 = hoare_lift_Pf3[where P="\f _. P f" for P] +lemmas hoare_lift_Pf = hoare_lift_Pf2[where Q=P and P=P for P] + +lemmas hoare_lift_Pf3_pre_conj = hoare_lift_Pf_pre_conj[where f=f and g=f for f] +lemmas hoare_lift_Pf2_pre_conj = hoare_lift_Pf3_pre_conj[where P="\f _. P f" for P] +lemmas hoare_lift_Pf_pre_conj' = hoare_lift_Pf2_pre_conj[where Q=P and P=P for P] + +lemma hoare_if_r_and: + "\P\ f \\r. if R r then Q r else Q' r\ + = \P\ f \\r s. (R r \ Q r s) \ (\R r \ Q' r s)\" + by (fastforce simp: valid_def) + +lemma hoare_convert_imp: + "\ \\s. \ P s\ f \\rv s. \ Q s\; \R\ f \S\ \ \ + \\s. P s \ R s\ f \\rv s. Q s \ S rv s\" + apply (simp only: imp_conv_disj) + apply (erule(1) hoare_vcg_disj_lift) + done + +lemma hoare_vcg_ex_lift_R: + "\ \v. \P v\ f \Q v\,- \ \ \\s. \v. P v s\ f \\rv s. \v. Q v rv s\,-" + apply (simp add: validE_R_def validE_def) + apply (rule hoare_strengthen_post, erule hoare_vcg_ex_lift) + apply (auto split: sum.split) + done + +lemma hoare_case_option_wpR: + "\\P\ f None \Q\,-; \x. \P' x\ f (Some x) \Q' x\,-\ \ + \case_option P P' v\ f v \\rv. case v of None \ Q rv | Some x \ Q' x rv\,-" + by (cases v) auto + +lemma hoare_vcg_conj_liftE_R: + "\ \P\ f \P'\,-; \Q\ f \Q'\,- \ \ \P and Q\ f \\rv s. P' rv s \ Q' rv s\, -" + apply (simp add: validE_R_def validE_def valid_def split: sum.splits) + apply blast + done + +lemma K_valid[wp]: + "\K P\ f \\_. K P\" + by (simp add: valid_def) + +lemma hoare_vcg_exI: + "\P\ f \Q x\ \ \P\ f \\rv s. \x. Q x rv s\" + apply (simp add: valid_def split_def) + apply blast + done + +lemma hoare_exI_tuple: + "\P\ f \\(rv,rv') s. Q x rv rv' s\ \ \P\ f \\(rv,rv') s. \x. Q x rv rv' s\" + by (fastforce simp: valid_def) + +lemma hoare_ex_all: + "(\x. \P x\ f \Q\) = \\s. \x. P x s\ f \Q\" + apply (rule iffI) + apply (fastforce simp: valid_def)+ + done + +lemma hoare_imp_eq_substR: + "\P\ f \Q\,- \ \P\ f \\rv s. rv = x \ Q x s\,-" + by (fastforce simp add: valid_def validE_R_def validE_def split: sum.splits) + +lemma hoare_split_bind_case_sum: + assumes x: "\rv. \R rv\ g rv \Q\" + "\rv. \S rv\ h rv \Q\" + assumes y: "\P\ f \S\,\R\" + shows "\P\ f >>= case_sum g h \Q\" + apply (rule hoare_seq_ext [OF _ y[unfolded validE_def]]) + apply (case_tac x, simp_all add: x) + done + +lemma hoare_split_bind_case_sumE: + assumes x: "\rv. \R rv\ g rv \Q\,\E\" + "\rv. \S rv\ h rv \Q\,\E\" + assumes y: "\P\ f \S\,\R\" + shows "\P\ f >>= case_sum g h \Q\,\E\" + apply (unfold validE_def) + apply (rule hoare_seq_ext [OF _ y[unfolded validE_def]]) + apply (case_tac x, simp_all add: x [unfolded validE_def]) + done + +lemma assertE_sp: + "\P\ assertE Q \\rv s. Q \ P s\,\E\" + by (clarsimp simp: assertE_def) wp + +lemma throwErrorE_E [wp]: + "\Q e\ throwError e -, \Q\" + by (simp add: validE_E_def) wp + +lemma gets_inv [simp]: + "\ P \ gets f \ \r. P \" + by (simp add: gets_def, wp) + +lemma select_inv: + "\ P \ select S \ \r. P \" + by (simp add: select_def valid_def) + +lemmas return_inv = hoare_return_drop_var + +lemma assert_inv: "\P\ assert Q \\r. P\" + unfolding assert_def + by (cases Q) simp+ + +lemma assert_opt_inv: "\P\ assert_opt Q \\r. P\" + unfolding assert_opt_def + by (cases Q) simp+ + +lemma case_options_weak_wp: + "\ \P\ f \Q\; \x. \P'\ g x \Q\ \ \ \P and P'\ case opt of None \ f | Some x \ g x \Q\" + apply (cases opt) + apply (clarsimp elim!: hoare_weaken_pre) + apply (rule hoare_weaken_pre [where Q=P']) + apply simp+ + done + +lemma case_option_wp_None_return: + assumes [wp]: "\x. \P' x\ f x \\_. Q\" + shows "\\x s. (Q and P x) s \ P' x s \ + \ \Q and (\s. opt \ None \ P (the opt) s)\ + (case opt of None \ return () | Some x \ f x) + \\_. Q\" + by (cases opt; wpsimp) + +lemma case_option_wp_None_returnOk: + assumes [wp]: "\x. \P' x\ f x \\_. Q\,\E\" + shows "\\x s. (Q and P x) s \ P' x s \ + \ \Q and (\s. opt \ None \ P (the opt) s)\ + (case opt of None \ returnOk () | Some x \ f x) + \\_. Q\,\E\" + by (cases opt; wpsimp) + +lemma list_cases_weak_wp: + assumes "\P_A\ a \Q\" + assumes "\x xs. \P_B\ b x xs \Q\" + shows + "\P_A and P_B\ + case ts of + [] \ a + | x#xs \ b x xs \Q\" + apply (cases ts) + apply (simp, rule hoare_weaken_pre, rule assms, simp)+ + done + +lemmas hoare_FalseE_R = hoare_FalseE[where E="\\", folded validE_R_def] + +lemma hoare_vcg_if_lift2: + "\R\ f \\rv s. (P rv s \ X rv s) \ (\ P rv s \ Y rv s)\ \ + \R\ f \\rv s. if P rv s then X rv s else Y rv s\" + + "\R\ f \\rv s. (P' rv \ X rv s) \ (\ P' rv \ Y rv s)\ \ + \R\ f \\rv. if P' rv then X rv else Y rv\" + by (auto simp: valid_def split_def) + +lemma hoare_vcg_if_lift_ER: (* Required because of lack of rv in lifting rules *) + "\R\ f \\rv s. (P rv s \ X rv s) \ (\ P rv s \ Y rv s)\, - \ + \R\ f \\rv s. if P rv s then X rv s else Y rv s\, -" + + "\R\ f \\rv s. (P' rv \ X rv s) \ (\ P' rv \ Y rv s)\, - \ + \R\ f \\rv. if P' rv then X rv else Y rv\, -" + by (auto simp: valid_def validE_R_def validE_def split_def) + +lemma hoare_vcg_imp_liftE: + "\ \P'\ f \\rv s. \ P rv s\, \A\; \Q'\ f \Q\, \A\ \ + \ \\s. \ P' s \ Q' s\ f \\rv s. P rv s \ Q rv s\, \A\" + apply (simp only: imp_conv_disj) + apply (clarsimp simp: validE_def valid_def split_def sum.case_eq_if) + done + +lemma hoare_list_all_lift: + "(\r. r \ set xs \ \Q r\ f \\rv. Q r\) + \ \\s. list_all (\r. Q r s) xs\ f \\rv s. list_all (\r. Q r s) xs\" + apply (induct xs; simp) + apply wpsimp + apply (rule hoare_vcg_conj_lift; simp) + done + +lemma undefined_valid: "\\\ undefined \Q\" + by (rule hoare_pre_cont) + +lemma assertE_wp: + "\\s. F \ Q () s\ assertE F \Q\,\E\" + apply (rule hoare_pre) + apply (unfold assertE_def) + apply wp + apply simp + done + +lemma doesn't_grow_proof: + assumes y: "\s. finite (S s)" + assumes x: "\x. \\s. x \ S s \ P s\ f \\rv s. x \ S s\" + shows "\\s. card (S s) < n \ P s\ f \\rv s. card (S s) < n\" + apply (clarsimp simp: valid_def) + apply (subgoal_tac "S b \ S s") + apply (drule card_mono [OF y], simp) + apply clarsimp + apply (rule ccontr) + apply (subgoal_tac "x \ S b", simp) + apply (erule use_valid [OF _ x]) + apply simp + done + +lemma hoare_vcg_propE_R: + "\\s. P\ f \\rv s. P\, -" + by (simp add: validE_R_def validE_def valid_def split_def split: sum.split) + +lemma set_preserved_proof: + assumes y: "\x. \\s. Q s \ x \ S s\ f \\rv s. x \ S s\" + assumes x: "\x. \\s. Q s \ x \ S s\ f \\rv s. x \ S s\" + shows "\\s. Q s \ P (S s)\ f \\rv s. P (S s)\" + apply (clarsimp simp: valid_def) + by (metis (mono_tags, lifting) equalityI post_by_hoare subsetI x y) + +lemma set_shrink_proof: + assumes x: "\x. \\s. x \ S s\ f \\rv s. x \ S s\" + shows + "\\s. \S'. S' \ S s \ P S'\ + f + \\rv s. P (S s)\" + apply (clarsimp simp: valid_def) + apply (drule spec, erule mp) + apply (clarsimp simp: subset_iff) + apply (rule ccontr) + apply (drule(1) use_valid [OF _ x]) + apply simp + done + +lemma shrinks_proof: + assumes y: "\s. finite (S s)" + assumes x: "\x. \\s. x \ S s \ P s\ f \\rv s. x \ S s\" + assumes z: "\P\ f \\rv s. x \ S s\" + assumes w: "\s. P s \ x \ S s" + shows "\\s. card (S s) \ n \ P s\ f \\rv s. card (S s) < n\" + apply (clarsimp simp: valid_def) + apply (subgoal_tac "S b \ S s") + apply (drule psubset_card_mono [OF y], simp) + apply (rule psubsetI) + apply clarsimp + apply (rule ccontr) + apply (subgoal_tac "x \ S b", simp) + apply (erule use_valid [OF _ x]) + apply simp + by (metis use_valid w z) + +lemma use_validE_R: + "\ (Inr r, s') \ fst (f s); \P\ f \Q\,-; P s \ \ Q r s'" + unfolding validE_R_def validE_def + by (frule(2) use_valid, simp) + +lemma valid_preservation_ex: + assumes x: "\x P. \\s. P (f s x :: 'b)\ m \\rv s. P (f s x)\" + shows "\\s. P (f s :: 'a \ 'b)\ m \\rv s. P (f s)\" + apply (clarsimp simp: valid_def) + apply (erule subst[rotated, where P=P]) + apply (rule ext) + apply (erule use_valid [OF _ x]) + apply simp + done + +lemmas valid_prove_more' = valid_prove_more[where Q="\rv. Q" for Q] + +lemma whenE_inv: + assumes a: "\P\ f \\_. P\" + shows "\P\ whenE Q f \\_. P\" + by (wpsimp wp: a) + +lemma whenE_throwError_wp: + "\\s. \ P \ Q s\ whenE P (throwError e) \\_. Q\, \\\\" + by wpsimp + +lemma ifM_throwError_returnOk: + "\Q\ test \\c s. \ c \ P s\ \ \Q\ ifM test (throwError e) (returnOk ()) \\_. P\, -" + by (fastforce simp: ifM_def returnOk_def throwError_def return_def validE_R_def valid_def + validE_def bind_def + split: if_splits) + +lemma ifME_liftE: + "ifME (liftE test) a b = ifM test a b" + by (simp add: ifME_def ifM_def liftE_bindE) + +lemma gets_the_inv: "\P\ gets_the V \\rv. P\" by wpsimp + +lemma select_f_inv: + "\P\ select_f S \\_. P\" + by (simp add: select_f_def valid_def) + +lemmas state_unchanged = in_inv_by_hoareD [THEN sym] + +lemma validI: + assumes rl: "\s r s'. \ P s; (r, s') \ fst (S s) \ \ Q r s'" + shows "\P\ S \Q\" + unfolding valid_def using rl by safe + +lemma opt_return_pres_lift: + assumes x: "\v. \P\ f v \\rv. P\" + shows "\P\ case x of None \ return () | Some v \ f v \\rv. P\" + by (wpsimp wp: x) + +lemma valid_return_unit: + "\P\ f >>= (\_. return ()) \\r. Q\ \ \P\ f \\r. Q\" + apply (rule validI) + apply (fastforce simp: valid_def return_def bind_def split_def) + done + +lemma static_imp_wp: + "\Q\ m \R\ \ \\s. P \ Q s\ m \\rv s. P \ R rv s\" + by (cases P, simp_all add: valid_def) + +lemma static_imp_wpE : + "\Q\ m \R\,- \ \\s. P \ Q s\ m \\rv s. P \ R rv s\,-" + by (cases P, simp_all) + +lemma static_imp_conj_wp: + "\ \Q\ m \Q'\; \R\ m \R'\ \ + \ \\s. (P \ Q s) \ R s\ m \\rv s. (P \ Q' rv s) \ R' rv s\" + apply (rule hoare_vcg_conj_lift) + apply (rule static_imp_wp) + apply assumption+ + done + +lemma hoare_eq_P: + assumes "\P. \P\ f \\_. P\" + shows "\(=) s\ f \\_. (=) s\" + by (rule assms) + +lemma hoare_validE_R_conj: + "\\P\ f \Q\, -; \P\ f \R\, -\ \ \P\ f \Q and R\, -" + by (simp add: valid_def validE_def validE_R_def Let_def split_def split: sum.splits) + +lemma hoare_vcg_const_imp_lift_R: + "\P\ f \Q\,- \ \\s. F \ P s\ f \\rv s. F \ Q rv s\,-" + by (cases F, simp_all) + +lemma hoare_vcg_disj_lift_R: + assumes x: "\P\ f \Q\,-" + assumes y: "\P'\ f \Q'\,-" + shows "\\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\,-" + using assms + by (fastforce simp: validE_R_def validE_def valid_def split: sum.splits) + +lemmas throwError_validE_R = throwError_wp [where E="\\", folded validE_R_def] + +lemma valid_case_option_post_wp: + "(\x. \P x\ f \\rv. Q x\) \ + \\s. case ep of Some x \ P x s | _ \ True\ + f \\rv s. case ep of Some x \ Q x s | _ \ True\" + by (cases ep, simp_all add: hoare_vcg_prop) + +lemma P_bool_lift: + assumes t: "\Q\ f \\r. Q\" + assumes f: "\\s. \Q s\ f \\r s. \Q s\" + shows "\\s. P (Q s)\ f \\r s. P (Q s)\" + apply (clarsimp simp: valid_def) + apply (subgoal_tac "Q b = Q s") + apply simp + apply (rule iffI) + apply (rule classical) + apply (drule (1) use_valid [OF _ f]) + apply simp + apply (erule (1) use_valid [OF _ t]) + done + +lemmas fail_inv = hoare_fail_any[where Q="\_. P" and P=P for P] + +lemma gets_sp: "\P\ gets f \\rv. P and (\s. f s = rv)\" + by (wp, simp) + +lemma post_by_hoare2: + "\ \P\ f \Q\; (r, s') \ fst (f s); P s \ \ Q r s'" + by (rule post_by_hoare, assumption+) + +lemma hoare_Ball_helper: + assumes x: "\x. \P x\ f \Q x\" + assumes y: "\P. \\s. P (S s)\ f \\rv s. P (S s)\" + shows "\\s. \x \ S s. P x s\ f \\rv s. \x \ S s. Q x rv s\" + apply (clarsimp simp: valid_def) + apply (subgoal_tac "S b = S s") + apply (erule post_by_hoare2 [OF x]) + apply (clarsimp simp: Ball_def) + apply (erule_tac P1="\x. x = S s" in post_by_hoare2 [OF y]) + apply (rule refl) + done + +lemma handy_prop_divs: + assumes x: "\P. \\s. P (Q s) \ S s\ f \\rv s. P (Q' rv s)\" + "\P. \\s. P (R s) \ S s\ f \\rv s. P (R' rv s)\" + shows "\\s. P (Q s \ R s) \ S s\ f \\rv s. P (Q' rv s \ R' rv s)\" + "\\s. P (Q s \ R s) \ S s\ f \\rv s. P (Q' rv s \ R' rv s)\" + apply (clarsimp simp: valid_def + elim!: subst[rotated, where P=P]) + apply (rule use_valid [OF _ x(1)], assumption) + apply (rule use_valid [OF _ x(2)], assumption) + apply simp + apply (clarsimp simp: valid_def + elim!: subst[rotated, where P=P]) + apply (rule use_valid [OF _ x(1)], assumption) + apply (rule use_valid [OF _ x(2)], assumption) + apply simp + done + +lemma hoare_as_subst: + "\ \P. \\s. P (fn s)\ f \\rv s. P (fn s)\; + \v :: 'a. \P v\ f \Q v\ \ \ + \\s. P (fn s) s\ f \\rv s. Q (fn s) rv s\" + by (rule hoare_lift_Pf3) + +lemmas hoare_vcg_ball_lift = hoare_vcg_const_Ball_lift + +lemma hoare_set_preserved: + assumes x: "\x. \fn' x\ m \\rv. fn x\" + shows "\\s. set xs \ {x. fn' x s}\ m \\rv s. set xs \ {x. fn x s}\" + apply (induct xs) + apply simp + apply wp + apply simp + apply (rule hoare_vcg_conj_lift) + apply (rule x) + apply assumption + done + +lemma hoare_ex_pre: (* safe, unlike hoare_vcg_ex_lift *) + "(\x. \P x\ f \Q\) \ \\s. \x. P x s\ f \Q\" + by (fastforce simp: valid_def) + +lemma hoare_ex_pre_conj: + "(\x. \\s. P x s \ P' s\ f \Q\) + \ \\s. (\x. P x s) \ P' s\ f \Q\" + by (fastforce simp: valid_def) + +lemma hoare_conj_lift_inv: + "\\P\ f \Q\; \\s. P' s \ I s\ f \\rv. I\; + \s. P s \ P' s\ + \ \\s. P s \ I s\ f \\rv s. Q rv s \ I s\" + by (fastforce simp: valid_def) + +lemma hoare_in_monad_post : + assumes x: "\P. \P\ f \\x. P\" + shows "\\\ f \\rv s. (rv, s) \ fst (f s)\" + apply (clarsimp simp: valid_def) + apply (subgoal_tac "s = b", simp) + apply (simp add: state_unchanged [OF x]) + done + +lemma list_case_throw_validE_R: + "\ \y ys. xs = y # ys \ \P\ f y ys \Q\,- \ \ + \P\ case xs of [] \ throwError e | x # xs \ f x xs \Q\,-" + apply (case_tac xs, simp_all) + apply wp + done + +lemma validE_R_sp: + assumes x: "\P\ f \Q\,-" + assumes y: "\x. \Q x\ g x \R\,-" + shows "\P\ f >>=E (\x. g x) \R\,-" + by (rule hoare_pre, wp x y, simp) + +lemma valid_set_take_helper: + "\P\ f \\rv s. \x \ set (xs rv s). Q x rv s\ + \ \P\ f \\rv s. \x \ set (take (n rv s) (xs rv s)). Q x rv s\" + apply (erule hoare_strengthen_post) + apply (clarsimp dest!: in_set_takeD) + done + +lemma whenE_throwError_sp: + "\P\ whenE Q (throwError e) \\rv s. \ Q \ P s\, -" + apply (simp add: whenE_def validE_R_def) + apply (intro conjI impI; wp) + done + +lemma weaker_hoare_ifE: + assumes x: "\P \ a \Q\,\E\" + assumes y: "\P'\ b \Q\,\E\" + shows "\P and P'\ if test then a else b \Q\,\E\" + apply (rule hoare_vcg_precond_impE) + apply (wp x y) + apply simp + done + +lemma wp_split_const_if: + assumes x: "\P\ f \Q\" + assumes y: "\P'\ f \Q'\" + shows "\\s. (G \ P s) \ (\ G \ P' s)\ f \\rv s. (G \ Q rv s) \ (\ G \ Q' rv s)\" + by (case_tac G, simp_all add: x y) + +lemma wp_split_const_if_R: + assumes x: "\P\ f \Q\,-" + assumes y: "\P'\ f \Q'\,-" + shows "\\s. (G \ P s) \ (\ G \ P' s)\ f \\rv s. (G \ Q rv s) \ (\ G \ Q' rv s)\,-" + by (case_tac G, simp_all add: x y) + +lemma wp_throw_const_imp: + assumes x: "\P\ f \Q\" + shows "\\s. G \ P s\ f \\rv s. G \ Q rv s\" + by (case_tac G, simp_all add: x hoare_vcg_prop) + +lemma wp_throw_const_impE: + assumes x: "\P\ f \Q\,\E\" + shows "\\s. G \ P s\ f \\rv s. G \ Q rv s\,\\rv s. G \ E rv s\" + apply (case_tac G, simp_all add: x) + apply wp + done + +lemma hoare_const_imp_R: + "\Q\ f \R\,- \ \\s. P \ Q s\ f \\rv s. P \ R rv s\,-" + by (cases P, simp_all) + +lemma hoare_vcg_imp_lift_R: + "\ \P'\ f \\rv s. \ P rv s\, -; \Q'\ f \Q\, - \ \ \\s. P' s \ Q' s\ f \\rv s. P rv s \ Q rv s\, -" + by (auto simp add: valid_def validE_R_def validE_def split_def split: sum.splits) + +lemma hoare_disj_division: + "\ P \ Q; P \ \R\ f \S\; Q \ \T\ f \S\ \ + \ \\s. (P \ R s) \ (Q \ T s)\ f \S\" + apply safe + apply (rule hoare_pre_imp) + prefer 2 + apply simp + apply simp + apply (rule hoare_pre_imp) + prefer 2 + apply simp + apply simp + done + +lemma hoare_grab_asm: + "\ G \ \P\ f \Q\ \ \ \\s. G \ P s\ f \Q\" + by (cases G, simp+) + +lemma hoare_grab_asm2: + "(P' \ \\s. P s \ R s\ f \Q\) + \ \\s. P s \ P' \ R s\ f \Q\" + by (fastforce simp: valid_def) + +lemma hoare_grab_exs: + assumes x: "\x. P x \ \P'\ f \Q\" + shows "\\s. \x. P x \ P' s\ f \Q\" + apply (clarsimp simp: valid_def) + apply (erule(2) use_valid [OF _ x]) + done + +lemma hoare_prop_E: "\\rv. P\ f -,\\rv s. P\" + unfolding validE_E_def + by (rule hoare_pre, wp, simp) + +lemma hoare_vcg_conj_lift_R: + "\ \P\ f \Q\,-; \R\ f \S\,- \ \ + \\s. P s \ R s\ f \\rv s. Q rv s \ S rv s\,-" + apply (simp add: validE_R_def validE_def) + apply (drule(1) hoare_vcg_conj_lift) + apply (erule hoare_strengthen_post) + apply (clarsimp split: sum.splits) + done + +lemma hoare_walk_assmsE: + assumes x: "\P\ f \\rv. P\" and y: "\s. P s \ Q s" and z: "\P\ g \\rv. Q\" + shows "\P\ doE x \ f; g odE \\rv. Q\" + apply (wp z) + apply (simp add: validE_def) + apply (rule hoare_strengthen_post [OF x]) + apply (auto simp: y split: sum.splits) + done + +lemma univ_wp: + "\\s. \(rv, s') \ fst (f s). Q rv s'\ f \Q\" + by (simp add: valid_def) + +lemma univ_get_wp: + assumes x: "\P. \P\ f \\rv. P\" + shows "\\s. \(rv, s') \ fst (f s). s = s' \ Q rv s'\ f \Q\" + apply (rule hoare_pre_imp [OF _ univ_wp]) + apply clarsimp + apply (drule bspec, assumption, simp) + apply (subgoal_tac "s = b", simp) + apply (simp add: state_unchanged [OF x]) + done + +lemma result_in_set_wp : + assumes x: "\P. \P\ fn \\rv. P\" + shows "\\s. True\ fn \\v s'. (v, s') \ fst (fn s')\" + by (rule hoare_pre_imp [OF _ univ_get_wp], simp_all add: x split_def) clarsimp + +lemma other_result_in_set_wp: + assumes x: "\P. \P\ fn \\rv. P\" + shows "\\s. \(v, s) \ fst (fn s). F v = v\ fn \\v s'. (F v, s') \ fst (fn s')\" + proof - + have P: "\v s. (F v = v) \ (v, s) \ fst (fn s) \ (F v, s) \ fst (fn s)" + by simp + show ?thesis + apply (rule hoare_post_imp [OF P], assumption) + apply (rule hoare_pre_imp) + defer + apply (rule hoare_vcg_conj_lift) + apply (rule univ_get_wp [OF x]) + apply (rule result_in_set_wp [OF x]) + apply clarsimp + apply (drule bspec, assumption, simp) + done + qed + +lemma weak_if_wp: + "\ \P\ f \Q\; \P'\ f \Q'\ \ \ + \P and P'\ f \\r. if C r then Q r else Q' r\" + by (auto simp add: valid_def split_def) + +lemma weak_if_wp': + "\P\ f \\r. Q r and Q' r\ \ + \P\ f \\r. if C r then Q r else Q' r\" + by (auto simp add: valid_def split_def) + +lemma bindE_split_recursive_asm: + assumes x: "\x s'. \ (Inr x, s') \ fst (f s) \ \ \\s. B x s \ s = s'\ g x \C\, \E\" + shows "\A\ f \B\, \E\ \ \\st. A st \ st = s\ f >>=E g \C\, \E\" + apply (clarsimp simp: validE_def valid_def bindE_def bind_def lift_def) + apply (erule allE, erule(1) impE) + apply (drule(1) bspec, simp) + apply (case_tac a, simp_all add: throwError_def return_def) + apply (drule x) + apply (clarsimp simp: validE_def valid_def) + apply (drule(1) bspec, simp) + done + +lemma validE_R_abstract_rv: + "\P\ f \\rv s. \rv'. Q rv' s\,- \ \P\ f \Q\,-" + by (erule hoare_post_imp_R, simp) + +lemma validE_cases_valid: + "\P\ f \\rv s. Q (Inr rv) s\,\\rv s. Q (Inl rv) s\ + \ \P\ f \Q\" + apply (simp add: validE_def) + apply (erule hoare_strengthen_post) + apply (simp split: sum.split_asm) + done + +lemma liftM_pre: + assumes rl: "\\s. \ P s \ a \ \_ _. False \" + shows "\\s. \ P s \ liftM f a \ \_ _. False \" + unfolding liftM_def + apply (rule seq) + apply (rule rl) + apply wp + apply simp + done + +lemma hoare_gen_asm': + "(P \ \P'\ f \Q\) \ \P' and (\_. P)\ f \Q\" + apply (auto intro: hoare_assume_pre) + done + +lemma hoare_gen_asm_conj: + "(P \ \P'\ f \Q\) \ \\s. P' s \ P\ f \Q\" + by (fastforce simp: valid_def) + + +lemma hoare_add_K: + "\P\ f \Q\ \ \\s. P s \ I\ f \\rv s. Q rv s \ I\" + by (fastforce simp: valid_def) + + +lemma valid_rv_lift: + "\P'\ f \\rv s. rv \ Q rv s\ \ \\s. P \ P' s\ f \\rv s. rv \ P \ Q rv s\" + by (fastforce simp: valid_def) + +lemma valid_imp_ex: + "\P\ f \\rv s. \x. rv \ Q rv s x\ \ \P\ f \\rv s. rv \ (\x. Q rv s x)\" + by (fastforce simp: valid_def) + +lemma valid_rv_split: + "\\P\ f \\rv s. rv \ Q s\; \P\ f \\rv s. \rv \ Q' s\\ + \ + \P\ f \\rv s. if rv then Q s else Q' s\" + by (fastforce simp: valid_def) + +lemma hoare_rv_split: + "\\P\ f \\rv s. rv \ (Q rv s)\; \P\ f \\rv s. (\rv) \ (Q rv s)\\ + \ \P\ f \Q\" + apply (clarsimp simp: valid_def) + apply (case_tac a, fastforce+) + done + +lemma combine_validE: "\ \ P \ x \ Q \,\ E \; + \ P' \ x \ Q' \,\ E' \ \ \ + \ P and P' \ x \ \r. (Q r) and (Q' r) \,\\r. (E r) and (E' r) \" + apply (clarsimp simp: validE_def valid_def split: sum.splits) + apply (erule allE, erule (1) impE)+ + apply (drule (1) bspec)+ + apply clarsimp + done + +lemma valid_case_prod: + "\ \x y. valid (P x y) (f x y) Q \ \ valid (case_prod P v) (case_prod (\x y. f x y) v) Q" + by (simp add: split_def) + +lemma validE_case_prod: + "\ \x y. validE (P x y) (f x y) Q E \ \ validE (case_prod P v) (case_prod (\x y. f x y) v) Q E" + by (simp add: split_def) + +lemma valid_pre_satisfies_post: + "\ \s r' s'. P s \ Q r' s' \ \ \ P \ m \ Q \" + by (clarsimp simp: valid_def) + +lemma validE_pre_satisfies_post: + "\ \s r' s'. P s \ Q r' s'; \s r' s'. P s \ R r' s' \ \ \ P \ m \ Q \,\ R \" + by (clarsimp simp: validE_def2 split: sum.splits) + +lemma hoare_validE_R_conjI: + "\ \P\ f \Q\, - ; \P\ f \Q'\, - \ \ \P\ f \\rv s. Q rv s \ Q' rv s\, -" + apply (clarsimp simp: Ball_def validE_R_def validE_def valid_def) + by (case_tac a; fastforce) + +lemma hoare_validE_E_conjI: + "\ \P\ f -, \Q\ ; \P\ f -, \Q'\ \ \ \P\ f -, \\rv s. Q rv s \ Q' rv s\" + apply (clarsimp simp: Ball_def validE_E_def validE_def valid_def) + by (case_tac a; fastforce) + +lemma validE_R_post_conjD1: + "\P\ f \\r s. Q r s \ R r s\,- \ \P\ f \Q\,-" + apply (clarsimp simp: validE_R_def validE_def valid_def) + by (case_tac a; fastforce) + +lemma validE_R_post_conjD2: + "\P\ f \\r s. Q r s \ R r s\,- \ \P\ f \R\,-" + apply (clarsimp simp: validE_R_def validE_def valid_def) + by (case_tac a; fastforce) + +lemma throw_opt_wp[wp]: + "\if v = None then E ex else Q (the v)\ throw_opt ex v \Q\,\E\" + unfolding throw_opt_def by wpsimp auto + +lemma hoare_name_pre_state2: + "(\s. \P and ((=) s)\ f \Q\) \ \P\ f \Q\" + by (auto simp: valid_def intro: hoare_name_pre_state) + +lemma returnOk_E': "\P\ returnOk r -,\E\" + by (clarsimp simp: returnOk_def validE_E_def validE_def valid_def return_def) + +lemma throwError_R': "\P\ throwError e \Q\,-" + by (clarsimp simp:throwError_def validE_R_def validE_def valid_def return_def) + end \ No newline at end of file diff --git a/lib/Monads/trace/Trace_No_Fail.thy b/lib/Monads/trace/Trace_No_Fail.thy index dd47142aa..4265dd079 100644 --- a/lib/Monads/trace/Trace_No_Fail.thy +++ b/lib/Monads/trace/Trace_No_Fail.thy @@ -42,6 +42,11 @@ subsection \Bundles\ bundle no_pre = hoare_pre [wp_pre del] no_fail_pre [wp_pre del] +bundle classic_wp_pre = + hoare_pre [wp_pre del] + all_classic_wp_combs[wp_comb del] + all_classic_wp_combs[wp_comb] + subsection \Lemmas\ @@ -135,8 +140,91 @@ lemma no_fail_bind[wp]: apply (fastforce simp: image_def) done +lemma no_fail_assume_pre: + "(\s. P s \ no_fail P f) \ no_fail P f" + by (simp add: no_fail_def) + +lemma no_fail_liftM_eq[simp]: + "no_fail P (liftM f m) = no_fail P m" + by (auto simp: liftM_def no_fail_def bind_def return_def) + +lemma no_fail_liftM[wp]: + "no_fail P m \ no_fail P (liftM f m)" + by simp + +lemma no_fail_pre_and: + "no_fail P f \ no_fail (P and Q) f" + by (erule no_fail_pre) simp + +lemma no_fail_spec: + "\ \s. no_fail (((=) s) and P) f \ \ no_fail P f" + by (simp add: no_fail_def) + +lemma no_fail_assertE[wp]: + "no_fail (\_. P) (assertE P)" + by (simp add: assertE_def split: if_split) + +lemma no_fail_spec_pre: + "\ no_fail (((=) s) and P') f; \s. P s \ P' s \ \ no_fail (((=) s) and P) f" + by (erule no_fail_pre, simp) + +lemma no_fail_whenE[wp]: + "\ G \ no_fail P f \ \ no_fail (\s. G \ P s) (whenE G f)" + by (simp add: whenE_def split: if_split) + +lemma no_fail_unlessE[wp]: + "\ \ G \ no_fail P f \ \ no_fail (\s. \ G \ P s) (unlessE G f)" + by (simp add: unlessE_def split: if_split) + +lemma no_fail_throwError[wp]: + "no_fail \ (throwError e)" + by (simp add: throwError_def) + +lemma no_fail_liftE[wp]: + "no_fail P f \ no_fail P (liftE f)" + unfolding liftE_def by wpsimp + +lemma no_fail_gets_the[wp]: + "no_fail (\s. f s \ None) (gets_the f)" + unfolding gets_the_def + by wpsimp + +lemma no_fail_lift: + "(\y. x = Inr y \ no_fail P (f y)) \ no_fail (\s. \isl x \ P s) (lift f x)" + unfolding lift_def + by (wpsimp wp: no_fail_throwError split: sum.splits | assumption)+ + +lemma validE_R_valid_eq: + "\Q\ f \R\, - = \Q\ f \\rv s. \ isl rv \ R (projr rv) s\" + unfolding validE_R_def validE_def valid_def + by (fastforce split: sum.splits prod.split) + +lemma no_fail_bindE[wp]: + "\ no_fail P f; \rv. no_fail (R rv) (g rv); \Q\ f \R\,- \ + \ no_fail (P and Q) (f >>=E g)" + unfolding bindE_def + by (wpsimp wp: no_fail_lift simp: validE_R_valid_eq | assumption)+ + +lemma no_fail_False[simp]: + "no_fail (\_. False) X" + by (clarsimp simp: no_fail_def) + +lemma no_fail_gets_map[wp]: + "no_fail (\s. f s p \ None) (gets_map f p)" + unfolding gets_map_def by wpsimp + lemma no_fail_or: "\no_fail P a; no_fail Q a\ \ no_fail (P or Q) a" by (clarsimp simp: no_fail_def) +lemma no_fail_state_assert[wp]: + "no_fail P (state_assert P)" + unfolding state_assert_def + by wpsimp + +lemma no_fail_condition: + "\no_fail Q A; no_fail R B\ \ no_fail (\s. (C s \ Q s) \ (\ C s \ R s)) (condition C A B)" + unfolding condition_def no_fail_def + by clarsimp + end diff --git a/lib/Monads/trace/Trace_No_Throw.thy b/lib/Monads/trace/Trace_No_Throw.thy index c98ac9659..90d667b71 100644 --- a/lib/Monads/trace/Trace_No_Throw.thy +++ b/lib/Monads/trace/Trace_No_Throw.thy @@ -25,4 +25,74 @@ definition no_throw :: "('s \ bool) \ ('s, 'e + 'a) tmon definition no_return :: "('a \ bool) \ ('a, 'b + 'c) tmonad \ bool" where "no_return P A \ \ P \ A \\_ _. False\,\\_ _. True \" +(* Alternative definition of no_throw; easier to work with than unfolding validE. *) +lemma no_throw_def': + "no_throw P A = (\s. P s \ (\(r, t) \ fst (A s). (\x. r = Inr x)))" + by (clarsimp simp: no_throw_def validE_def2 split_def split: sum.splits) + + +lemma no_throw_returnOk[simp]: + "no_throw P (returnOk a)" + unfolding no_throw_def + by wp + +lemma no_throw_liftE[simp]: + "no_throw P (liftE x)" + by (wpsimp simp: liftE_def no_throw_def validE_def) + +lemma no_throw_bindE: + "\ no_throw A X; \a. no_throw B (Y a); \ A \ X \ \_. B \,\ \_ _. True \ \ + \ no_throw A (X >>=E Y)" + unfolding no_throw_def + using hoare_validE_cases seqE by blast + +lemma no_throw_bindE_simple: + "\ no_throw \ L; \x. no_throw \ (R x) \ \ no_throw \ (L >>=E R)" + using hoareE_TrueI no_throw_bindE by blast + +lemma no_throw_handleE_simple: + "\ \x. no_throw \ L \ no_throw \ (R x) \ \ no_throw \ (L R)" + by (fastforce simp: no_throw_def' handleE_def handleE'_def validE_def valid_def bind_def return_def + split: sum.splits) + +lemma no_throw_handle2: + "\ \a. no_throw Y (B a); \ X \ A \ \_ _. True \,\ \_. Y \ \ \ no_throw X (A B)" + by (fastforce simp: no_throw_def' handleE'_def validE_def valid_def bind_def return_def + split: sum.splits) + +lemma no_throw_handle: + "\ \a. no_throw Y (B a); \ X \ A \ \_ _. True \,\ \_. Y \ \ \ no_throw X (A B)" + unfolding handleE_def + by (rule no_throw_handle2) + +lemma no_throw_fail[simp]: + "no_throw P fail" + by (clarsimp simp: no_throw_def) + +lemma handleE'_nothrow_lhs: + "no_throw \ L \ no_throw \ (L R)" + unfolding no_throw_def + using handleE'_wp[rotated] by fastforce + +lemma handleE'_nothrow_rhs: + "\ \x. no_throw \ (R x) \ \ no_throw \ (L R)" + unfolding no_throw_def + by (metis hoareE_TrueI no_throw_def no_throw_handle2) + +lemma handleE_nothrow_lhs: + "no_throw \ L \ no_throw \ (L R)" + by (metis handleE'_nothrow_lhs handleE_def) + +lemma handleE_nothrow_rhs: + "\ \x. no_throw \ (R x) \ \ no_throw \ (L R)" + by (metis no_throw_handleE_simple) + +lemma condition_nothrow: + "\ no_throw \ L; no_throw \ R \ \ no_throw \ (condition C L R)" + by (clarsimp simp: condition_def no_throw_def validE_def2) + +lemma no_throw_Inr: + "\ x \ fst (A s); no_throw P A; P s \ \ \y. fst x = Inr y" + by (fastforce simp: no_throw_def' split: sum.splits) + end \ No newline at end of file diff --git a/lib/Monads/trace/Trace_Sat.thy b/lib/Monads/trace/Trace_Sat.thy index 2f40ec7b7..20d68e584 100644 --- a/lib/Monads/trace/Trace_Sat.thy +++ b/lib/Monads/trace/Trace_Sat.thy @@ -26,6 +26,30 @@ definition ex_exs_validE :: ("\_\ _ \\_\, \_\") where "\P\ f \\Q\, \E\ \ \P\ f \\\rv. case rv of Inl e \ E e | Inr v \ Q v\" +text \ + Seen as predicate transformer, @{const exs_valid} is the so-called conjugate wp in the literature, + i.e. with + @{term "wp f Q \ \s. fst (f s) \ {(rv,s). Q rv s}"} and + @{term "cwp f Q \ not (wp f (not Q))"}, we get + @{prop "valid P f Q = (\s. P s \ wp f Q s)"} and + @{prop "exs_valid P f Q = (\s. P s \ cwp f Q s)"}. + + See also "Predicate Calculus and Program Semantics" by E. W. Dijkstra and C. S. Scholten.\ +experiment +begin + +definition + "wp f Q \ \s. fst (f s) \ {(rv,s). Q rv s}" + +definition + "cwp f Q \ not (wp f (not Q))" + +lemma + "exs_valid P f Q = (\s. P s \ cwp f Q s)" + unfolding exs_valid_def cwp_def wp_def by auto + +end + subsection \Set up for @{method wp}\ @@ -76,9 +100,11 @@ lemma exs_valid_return[wp]: lemma exs_valid_select[wp]: "\\s. \r \ S. Q r s\ select S \\Q\" - apply (clarsimp simp: exs_valid_def select_def mres_def) - apply (auto simp add: image_def) - done + by (clarsimp simp: exs_valid_def select_def) + +lemma exs_valid_alt[wp]: + "\ \P\ f \\Q\; \P'\ g \\Q\ \ \ \P or P'\ f \ g \\Q\" + by (fastforce simp: exs_valid_def alternative_def) lemma exs_valid_get[wp]: "\\s. Q s s\ get \\ Q \" @@ -97,10 +123,15 @@ lemma exs_valid_fail[wp]: unfolding fail_def exs_valid_def by simp +lemma exs_valid_assert[wp]: + "\\s. Q () s \ G\ assert G \\Q\" + unfolding assert_def + by (wpsimp | rule conjI)+ + lemma exs_valid_state_assert[wp]: "\ \s. Q () s \ G s \ state_assert G \\ Q \" - by (clarsimp simp: state_assert_def exs_valid_def get_def - assert_def bind_def2 return_def mres_def) + unfolding state_assert_def + by wp lemmas exs_valid_guard = exs_valid_state_assert @@ -108,4 +139,16 @@ lemma exs_valid_condition[wp]: "\ \P\ l \\Q\; \P'\ r \\Q\ \ \ \\s. (C s \ P s) \ (\ C s \ P' s)\ condition C l r \\Q\" by (clarsimp simp: condition_def exs_valid_def split: sum.splits) +lemma gets_exs_valid: + "\(=) s\ gets f \\\r. (=) s\" + by (rule exs_valid_gets) + +lemma exs_valid_assert_opt[wp]: + "\\s. \x. G = Some x \ Q x s\ assert_opt G \\Q\" + by (clarsimp simp: assert_opt_def exs_valid_def get_def assert_def bind_def' return_def) + +lemma gets_the_exs_valid[wp]: + "\\s. \x. h s = Some x \ Q x s\ gets_the h \\Q\" + by (wpsimp simp: gets_the_def) + end \ No newline at end of file diff --git a/lib/Monads/trace/Trace_Strengthen_Setup.thy b/lib/Monads/trace/Trace_Strengthen_Setup.thy index 024d370f8..c4b652fa0 100644 --- a/lib/Monads/trace/Trace_Strengthen_Setup.thy +++ b/lib/Monads/trace/Trace_Strengthen_Setup.thy @@ -41,6 +41,46 @@ lemma strengthen_validI[strg]: \ st F (\) (\P\,\G\ f \R\,\Q\) (\P\,\G\ f \R\,\Q'\)" by (cases F, auto elim: validI_strengthen_post) +lemma wpfix_strengthen_hoare: + "(\s. st (\ F) (\) (P s) (P' s)) + \ (\r s. st F (\) (Q r s) (Q' r s)) + \ st F (\) (\P\ f \Q\) (\P'\ f \Q'\)" + by (cases F, auto elim: hoare_chain) + +lemma wpfix_strengthen_validE_R_cong: + "(\s. st (\ F) (\) (P s) (P' s)) + \ (\r s. st F (\) (Q r s) (Q' r s)) + \ st F (\) (\P\ f \Q\, -) (\P'\ f \Q'\, -)" + by (cases F, auto elim: hoare_chainE simp: validE_R_def) + +lemma wpfix_strengthen_validE_cong: + "(\s. st (\ F) (\) (P s) (P' s)) + \ (\r s. st F (\) (Q r s) (R r s)) + \ (\r s. st F (\) (S r s) (T r s)) + \ st F (\) (\P\ f \Q\, \S\) (\P'\ f \R\, \T\)" + by (cases F, auto elim: hoare_chainE) + +lemma wpfix_strengthen_validE_E_cong: + "(\s. st (\ F) (\) (P s) (P' s)) + \ (\r s. st F (\) (S r s) (T r s)) + \ st F (\) (\P\ f -, \S\) (\P'\ f -, \T\)" + by (cases F, auto elim: hoare_chainE simp: validE_E_def) + +lemma wpfix_no_fail_cong: + "(\s. st (\ F) (\) (P s) (P' s)) + \ st F (\) (no_fail P f) (no_fail P' f)" + by (cases F, auto elim: no_fail_pre) + +lemmas nondet_wpfix_strgs = + wpfix_strengthen_validE_R_cong + wpfix_strengthen_validE_E_cong + wpfix_strengthen_validE_cong + wpfix_strengthen_hoare + wpfix_no_fail_cong + end +lemmas nondet_wpfix_strgs[wp_fix_strgs] + = strengthen_implementation.nondet_wpfix_strgs + end \ No newline at end of file diff --git a/lib/Monads/trace/Trace_Total.thy b/lib/Monads/trace/Trace_Total.thy index 97c804807..7c89f267f 100644 --- a/lib/Monads/trace/Trace_Total.thy +++ b/lib/Monads/trace/Trace_Total.thy @@ -49,17 +49,40 @@ wpc_setup "\m. \P\ m \Q\!" wpc_helper_va subsection \Basic @{const validNF} theorems\ +lemma validNF_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \!) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') \ f \ Q' \!" + by (auto simp add: valid_def validNF_def no_fail_def split: prod.splits) + +lemma validE_NF_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \, \ \rv s. E s0 rv s \!) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') + \ (\rv s'. E s0 rv s' \ E' rv s') \ f \ Q' \, \ E' \!" + by (auto simp add: validE_NF_def validE_def valid_def no_fail_def split: prod.splits sum.splits) + +lemma validNF_conjD1: + "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q \!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF_conjD2: + "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q' \!" + by (fastforce simp: validNF_def valid_def no_fail_def) + lemma validNF[intro?]: (* FIXME lib: should be validNFI *) "\ \ P \ f \ Q \; no_fail P f \ \ \ P \ f \ Q \!" by (clarsimp simp: validNF_def) +lemma validNFE: + "\ \ P \ f \ Q \!; \ \ P \ f \ Q \; no_fail P f \ \ R \ \ R" + by (clarsimp simp: validNF_def) + lemma validNF_valid: "\ \ P \ f \ Q \! \ \ \ P \ f \ Q \" - by (clarsimp simp: validNF_def) + by (erule validNFE) lemma validNF_no_fail: "\ \ P \ f \ Q \! \ \ no_fail P f" - by (clarsimp simp: validNF_def) + by (erule validNFE) lemma snd_validNF: "\ \ P \ f \ Q \!; P s \ \ Failed \ snd ` (f s)" @@ -163,60 +186,45 @@ subsection "validNF compound rules" lemma validNF_state_assert[wp]: "\ \s. P () s \ G s \ state_assert G \ P \!" - apply (rule validNF) - apply wpsimp - apply (clarsimp simp: no_fail_def state_assert_def - bind_def2 assert_def return_def get_def) - done + by (rule validNF; wpsimp) lemma validNF_modify[wp]: "\ \s. P () (f s) \ modify f \ P \!" - apply (clarsimp simp: modify_def) - apply wp - done + by (rule validNF; wpsimp) lemma validNF_gets[wp]: "\\s. P (f s) s\ gets f \P\!" - apply (clarsimp simp: gets_def) - apply wp - done + by (rule validNF; wpsimp) lemma validNF_condition[wp]: "\ \ Q \ A \P\!; \ R \ B \P\!\ \ \\s. if C s then Q s else R s\ condition C A B \P\!" - apply rule - apply (drule validNF_valid)+ - apply (erule (1) condition_wp) - apply (drule validNF_no_fail)+ - apply (clarsimp simp: no_fail_def condition_def) - done + by (erule validNFE)+ + (rule validNF; wpsimp wp: no_fail_condition) lemma validNF_assert[wp]: - "\ (\s. P) and (R ()) \ assert P \ R \!" - apply (rule validNF) - apply (clarsimp simp: valid_def in_return) - apply (clarsimp simp: no_fail_def return_def) - done + "\ (\s. P) and (R ()) \ assert P \ R \!" + by (rule validNF; wpsimp) lemma validNF_false_pre: "\ \_. False \ P \ Q \!" - by (clarsimp simp: validNF_def no_fail_def) + by (rule validNF; wpsimp) lemma validNF_chain: "\\P'\ a \R'\!; \s. P s \ P' s; \r s. R' r s \ R r s\ \ \P\ a \R\!" by (fastforce simp: validNF_def valid_def no_fail_def Ball_def) lemma validNF_case_prod[wp]: - "\ \x y. validNF (P x y) (B x y) Q \ \ validNF (case_prod P v) (case_prod (\x y. B x y) v) Q" + "\\x y. \P x y\ B x y \Q\!\ \ \case v of (x, y) \ P x y\ case v of (x, y) \ B x y \Q\!" by (metis prod.exhaust split_conv) lemma validE_NF_case_prod[wp]: - "\ \a b. \P a b\ f a b \Q\, \E\! \ \ - \case x of (a, b) \ P a b\ case x of (a, b) \ f a b \Q\, \E\!" - apply (clarsimp simp: validE_NF_alt_def) - apply (erule validNF_case_prod) - done + "\ \a b. \P a b\ f a b \Q\, \E\! \ \ + \case x of (a, b) \ P a b\ case x of (a, b) \ f a b \Q\, \E\!" + unfolding validE_NF_alt_def + by (erule validNF_case_prod) -lemma no_fail_is_validNF_True: "no_fail P s = (\ P \ s \ \_ _. True \!)" +lemma no_fail_is_validNF_True: + "no_fail P s = (\ P \ s \ \_ _. True \!)" by (clarsimp simp: no_fail_def validNF_def valid_def) @@ -226,13 +234,17 @@ lemma validE_NF[intro?]: "\ \ P \ f \ Q \,\ E \; no_fail P f \ \ \ P \ f \ Q \,\ E \!" by (clarsimp simp: validE_NF_def) +lemma validE_NFE: + "\ \ P \ f \ Q \,\ E \!; \ \ P \ f \ Q \,\ E \; no_fail P f \ \ R \ \ R" + by (clarsimp simp: validE_NF_def) + lemma validE_NF_valid: "\ \ P \ f \ Q \,\ E \! \ \ \ P \ f \ Q \,\ E \" - by (clarsimp simp: validE_NF_def) + by (rule validE_NFE) lemma validE_NF_no_fail: "\ \ P \ f \ Q \,\ E \! \ \ no_fail P f" - by (clarsimp simp: validE_NF_def) + by (rule validE_NFE) lemma validE_NF_weaken_pre[wp_pre]: "\\Q\ a \R\,\E\!; \s. P s \ Q s\ \ \P\ a \R\,\E\!" @@ -263,21 +275,13 @@ lemma validE_NF_chain: lemma validE_NF_bind_wp[wp]: "\\x. \B x\ g x \C\, \E\!; \A\ f \B\, \E\!\ \ \A\ f >>=E (\x. g x) \C\, \E\!" - apply (unfold validE_NF_alt_def bindE_def) - apply (rule validNF_bind [rotated]) - apply assumption - apply (clarsimp simp: lift_def throwError_def split: sum.splits) - apply wpsimp - done + by (blast intro: validE_NF hoare_vcg_seqE no_fail_pre no_fail_bindE validE_validE_R validE_weaken + elim!: validE_NFE) lemma validNF_catch[wp]: "\\x. \E x\ handler x \Q\!; \P\ f \Q\, \E\!\ \ \P\ f (\x. handler x) \Q\!" - apply (unfold validE_NF_alt_def catch_def) - apply (rule validNF_bind [rotated]) - apply assumption - apply (clarsimp simp: lift_def throwError_def split: sum.splits) - apply wp - done + unfolding validE_NF_alt_def catch_def lift_def throwError_def + by (clarsimp simp: validNF_return split: sum.splits elim!: validNF_bind[rotated]) lemma validNF_throwError[wp]: "\E e\ throwError e \P\, \E\!" @@ -285,15 +289,15 @@ lemma validNF_throwError[wp]: lemma validNF_returnOk[wp]: "\P e\ returnOk e \P\, \E\!" - by (clarsimp simp: validE_NF_alt_def returnOk_def) wpsimp + by (clarsimp simp: validE_NF_alt_def returnOk_def) wpsimp lemma validNF_whenE[wp]: "(P \ \Q\ f \R\, \E\!) \ \if P then Q else R ()\ whenE P f \R\, \E\!" - unfolding whenE_def by clarsimp wp + unfolding whenE_def by wpsimp lemma validNF_nobindE[wp]: "\ \B\ g \C\,\E\!; \A\ f \\r s. B s\,\E\! \ \ \A\ doE f; g odE \C\,\E\!" - by clarsimp wp + by wpsimp text \ Set up triple rules for @{term validE_NF} so that we can use @{method wp} combinator rules.\ @@ -336,11 +340,10 @@ lemma validE_NF_handleE[wp]: lemma validE_NF_condition[wp]: "\ \ Q \ A \P\,\ E \!; \ R \ B \P\,\ E \!\ \ \\s. if C s then Q s else R s\ condition C A B \P\,\ E \!" - apply rule - apply (drule validE_NF_valid)+ - apply wp - apply (drule validE_NF_no_fail)+ - apply (clarsimp simp: no_fail_def condition_def) - done + by (erule validE_NFE)+ (wpsimp wp: no_fail_condition validE_NF) + +lemma hoare_assume_preNF: + "(\s. P s \ \P\ f \Q\!) \ \P\ f \Q\!" + by (metis validNF_alt_def) end \ No newline at end of file diff --git a/lib/Monads/trace/Trace_VCG.thy b/lib/Monads/trace/Trace_VCG.thy index c20265ebd..f950d1b6e 100644 --- a/lib/Monads/trace/Trace_VCG.thy +++ b/lib/Monads/trace/Trace_VCG.thy @@ -71,6 +71,18 @@ definition validE_E :: (* FIXME lib: this should be an abbreviation *) "('s \ bool) \ ('s, 'e + 'a) tmonad \ ('e \ 's \ bool) \ bool" ("\_\/ _ /-, \_\") where "\P\ f -,\Q\ \ validE P f (\x y. True) Q" +(* These lemmas are useful to apply to rules to convert valid rules into a format suitable for wp. *) +lemma valid_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') \ f \ Q' \" + by (auto simp add: valid_def split: prod.splits) + +lemma validE_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \, \ \rv s. E s0 rv s \) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') + \ (\rv s'. E s0 rv s' \ E' rv s') \ f \ Q' \, \ E' \" + by (auto simp add: validE_def valid_def split: prod.splits sum.splits) + section \Lemmas\ @@ -166,7 +178,9 @@ lemma seq_ext': \A\ do x \ f; g x od \C\" by (metis bind_wp) -lemmas seq_ext = bind_wp[rotated] +lemma seq_ext: + "\ \A\ f \B\; \x. \B x\ g x \C\ \ \ \A\ do x \ f; g x od \C\" + by (fastforce simp: valid_def bind_def) lemma seqE': "\ \A\ f \B\,\E\; \x. \B x\ g x \C\,\E\ \ \ @@ -305,6 +319,12 @@ lemma use_valid: lemmas post_by_hoare = use_valid[rotated] +lemma use_valid_inv: + assumes step: "(r, s') \ fst (f s)" + assumes pres: "\N. \\s. N (P s) \ E s\ f \\rv s. N (P s)\" + shows "E s \ P s = P s'" + using use_valid[where f=f, OF step pres[where N="\p. p = P s"]] by simp + lemma use_validE_norm: "\ (Inr r', s') \ mres (B s); \P\ B \Q\,\ E \; P s \ \ Q r' s'" unfolding validE_def valid_def by force @@ -328,6 +348,22 @@ lemma hoare_gen_asm: "(P \ \P'\ f \Q\) \ \P' and K P\ f \Q\" by (fastforce simp add: valid_def) +lemmas hoare_gen_asm_single = hoare_gen_asm[where P'="\", simplified pred_conj_def simp_thms] + +lemma hoare_gen_asm_lk: + "(P \ \P'\ f \Q\) \ \K P and P'\ f \Q\" + by (fastforce simp add: valid_def) + +\ \Useful for forward reasoning, when P is known. + The first version allows weakening the precondition.\ +lemma hoare_gen_asm_spec': + "\ \s. P s \ S \ R s; S \ \R\ f \Q\ \ \ \P\ f \Q\" + by (fastforce simp: valid_def) + +lemma hoare_gen_asm_spec: + "\ \s. P s \ S; S \ \P\ f \Q\ \ \ \P\ f \Q\" + by (rule hoare_gen_asm_spec'[where S=S and R=P]) simp + lemma hoare_conjI: "\ \P\ f \Q\; \P\ f \R\ \ \ \P\ f \\r s. Q r s \ R r s\" unfolding valid_def by blast @@ -374,10 +410,24 @@ lemma hoare_case_option_wp: \ \case_option P P' v\ f v \\rv. case v of None \ Q rv | Some x \ Q' x rv\" by (cases v) auto +lemma hoare_case_option_wp2: + "\ \P\ f None \Q\; \x. \P' x\ f (Some x) \Q' x\ \ + \ \case_option P P' v\ f v \\rv s. case v of None \ Q rv s | Some x \ Q' x rv s\" + by (cases v) auto + +(* Might be useful for forward reasoning, when P is known. *) +lemma hoare_when_cases: + "\\s. \\B; P s\ \ Q s; B \ \P\ f \\_. Q\\ \ \P\ when B f \\_. Q\" + by (cases B; simp add: valid_def return_def) + lemma hoare_vcg_prop: "\\s. P\ f \\rv s. P\" by (simp add: valid_def) +lemma validE_eq_valid: + "\P\ f \\rv. Q\,\\rv. Q\ = \P\ f \\rv. Q\" + by (simp add: validE_def) + subsection \@{const valid} and @{const validE}, @{const validE_R}, @{const validE_E}\ @@ -489,7 +539,7 @@ lemma hoare_seq_ext_nobindE: "\ \B\ g \C\, \E\; \A\ f \\_. B\, \E\ \ \ \A\ doE f; g odE \C\, \E\" by (erule seqE) (clarsimp simp: validE_def) -lemmas hoare_seq_ext_skip' = hoare_seq_ext[where Q'=Q and Q=Q for Q] +lemmas hoare_seq_ext_skip' = hoare_seq_ext[where B=C and C=C for C] lemma hoare_chain: "\ \P\ f \Q\; \s. R s \ P s; \rv s. Q rv s \ S rv s \ \ \R\ f \S\" @@ -507,6 +557,9 @@ lemma hoare_vcg_conj_lift: unfolding valid_def by fastforce +\ \A variant which works nicely with subgoals that do not contain schematics\ +lemmas hoare_vcg_conj_lift_pre_fix = hoare_vcg_conj_lift[where P=R and P'=R for R, simplified] + lemma hoare_vcg_conj_liftE1: "\ \P\ f \Q\,-; \P'\ f \Q'\,\E\ \ \ \P and P'\ f \\rv s. Q rv s \ Q' rv s\,\E\" unfolding valid_def validE_R_def validE_def @@ -535,6 +588,30 @@ lemma hoare_vcg_all_lift_R: "(\x. \P x\ f \Q x\, -) \ \\s. \x. P x s\ f \\rv s. \x. Q x rv s\, -" by (rule hoare_vcg_const_Ball_lift_R[where S=UNIV, simplified]) +lemma hoare_vcg_imp_lift: + "\ \P'\ f \\rv s. \ P rv s\; \Q'\ f \Q\ \ \ \\s. P' s \ Q' s\ f \\rv s. P rv s \ Q rv s\" + by (simp only: imp_conv_disj) (rule hoare_vcg_disj_lift) + +lemma hoare_vcg_imp_lift': + "\ \P'\ f \\rv s. \ P rv s\; \Q'\ f \Q\ \ \ \\s. \ P' s \ Q' s\ f \\rv s. P rv s \ Q rv s\" + by (wpsimp wp: hoare_vcg_imp_lift) + +lemma hoare_vcg_imp_conj_lift[wp_comb]: + "\ \P\ f \\rv s. Q rv s \ Q' rv s\; \P'\ f \\rv s. (Q rv s \ Q'' rv s) \ Q''' rv s\ \ \ + \P and P'\ f \\rv s. (Q rv s \ Q' rv s \ Q'' rv s) \ Q''' rv s\" + by (auto simp: valid_def) + +lemmas hoare_vcg_imp_conj_lift'[wp_unsafe] = hoare_vcg_imp_conj_lift[where Q'''="\\", simplified] + +lemma hoare_absorb_imp: + "\ P \ f \\rv s. Q rv s \ R rv s\ \ \ P \ f \\rv s. Q rv s \ R rv s\" + by (erule hoare_post_imp[rotated], blast) + +lemma hoare_weaken_imp: + "\ \rv s. Q rv s \ Q' rv s ; \P\ f \\rv s. Q' rv s \ R rv s\ \ + \ \P\ f \\rv s. Q rv s \ R rv s\" + by (clarsimp simp: valid_def split_def) + lemma hoare_vcg_const_imp_lift: "\ P \ \Q\ m \R\ \ \ \\s. P \ Q s\ m \\rv s. P \ R rv s\" by (cases P, simp_all add: hoare_vcg_prop) @@ -547,6 +624,8 @@ lemma hoare_weak_lift_imp: "\P'\ f \Q\ \ \\s. P \ P' s\ f \\rv s. P \ Q rv s\" by (auto simp add: valid_def split_def) +lemmas hoare_vcg_weaken_imp = hoare_weaken_imp (* FIXME lib: eliminate *) + lemma hoare_vcg_ex_lift: "\ \x. \P x\ f \Q x\ \ \ \\s. \x. P x s\ f \\rv s. \x. Q x rv s\" by (clarsimp simp: valid_def, blast) @@ -555,6 +634,17 @@ lemma hoare_vcg_ex_lift_R1: "(\x. \P x\ f \Q\, -) \ \\s. \x. P x s\ f \Q\, -" by (fastforce simp: valid_def validE_R_def validE_def split: sum.splits) +lemma hoare_liftP_ext: + assumes "\P x. m \\s. P (f s x)\" + shows "m \\s. P (f s)\" + unfolding valid_def + apply clarsimp + apply (erule subst[rotated, where P=P]) + apply (rule ext) + apply (drule use_valid, rule assms, rule refl) + apply simp + done + (* for instantiations *) lemma hoare_triv: "\P\f\Q\ \ \P\f\Q\" . lemma hoare_trivE: "\P\ f \Q\,\E\ \ \P\ f \Q\,\E\" . @@ -575,11 +665,48 @@ lemma hoare_vcg_R_conj: unfolding validE_R_def validE_def by (rule hoare_post_imp[OF _ hoare_vcg_conj_lift]; simp split: sum.splits) +lemma hoare_lift_Pf_E_R: + "\ \x. \P x\ m \\_. P x\, -; \P. \\s. P (f s)\ m \\_ s. P (f s)\, - \ \ + \\s. P (f s) s\ m \\_ s. P (f s) s\, -" + by (fastforce simp: validE_R_def validE_def valid_def split: sum.splits) + +lemma hoare_lift_Pf_E_E: + "\ \x. \P x\ m -, \\_. P x\; \P. \\s. P (f s)\ m -, \\_ s. P (f s)\ \ \ + \\s. P (f s) s\ m -, \\_ s. P (f s) s\" + by (fastforce simp: validE_E_def validE_def valid_def split: sum.splits) + +lemma hoare_vcg_const_Ball_lift_E_E: + "(\x. x \ S \ \P x\ f -,\Q x\) \ \\s. \x \ S. P x s\ f -,\\rv s. \x \ S. Q x rv s\" + unfolding validE_E_def validE_def valid_def + by (fastforce split: sum.splits) + +lemma hoare_vcg_all_liftE_E: + "(\x. \P x\ f -, \Q x\) \ \\s. \x. P x s\ f -,\\rv s. \x. Q x rv s\" + by (rule hoare_vcg_const_Ball_lift_E_E[where S=UNIV, simplified]) + +lemma hoare_vcg_imp_liftE_E: + "\\P'\ f -, \\rv s. \ P rv s\; \Q'\ f -, \Q\\ \ + \\s. \ P' s \ Q' s\ f -, \\rv s. P rv s \ Q rv s\" + by (auto simp add: valid_def validE_E_def validE_def split_def split: sum.splits) + +lemma hoare_vcg_ex_liftE: + "\ \x. \P x\ f \Q x\,\E\ \ \ \\s. \x. P x s\ f \\rv s. \x. Q x rv s\,\E\" + by (fastforce simp: validE_def valid_def split: sum.splits) + +lemma hoare_vcg_ex_liftE_E: + "\ \x. \P x\ f -,\E x\ \ \ \\s. \x. P x s\ f -,\\rv s. \x. E x rv s\" + by (fastforce simp: validE_E_def validE_def valid_def split: sum.splits) + lemma hoare_post_imp_R: "\ \P\ f \Q'\,-; \rv s. Q' rv s \ Q rv s \ \ \P\ f \Q\,-" unfolding validE_R_def by (erule hoare_post_impErr) +lemma hoare_post_imp_E: + "\ \P\ f -,\Q'\; \rv s. Q' rv s \ Q rv s \ \ \P\ f -,\Q\" + unfolding validE_E_def + by (rule hoare_post_impErr) + lemma hoare_post_comb_imp_conj: "\ \P'\ f \Q\; \P\ f \Q'\; \s. P s \ P' s \ \ \P\ f \\rv s. Q rv s \ Q' rv s\" by (wpsimp wp: hoare_vcg_conj_lift) @@ -596,8 +723,8 @@ lemma return_wp: by(simp add: valid_def return_def mres_def) lemma get_wp: - "\\s. Q s s\ get \Q\" - by (simp add: get_def valid_def mres_def) + "\\s. P s s\ get \P\" + by(simp add: valid_def split_def get_def) lemma gets_wp: "\\s. P (f s) s\ gets f \P\" @@ -740,10 +867,53 @@ lemma whenE_wp: "(P \ \Q\ f \R\, \E\) \ \if P then Q else R ()\ whenE P f \R\, \E\" unfolding whenE_def by clarsimp (wp returnOk_wp) +lemma unlessE_wp: + "(\ P \ \Q\ f \R\, \E\) \ \if P then R () else Q\ unlessE P f \R\, \E\" + unfolding unlessE_def + by (wpsimp wp: returnOk_wp) + +lemma maybeM_wp: + "(\x. y = Some x \ \P x\ m x \Q\) \ + \\s. (\x. y = Some x \ P x s) \ (y = None \ Q () s)\ maybeM m y \Q\" + unfolding maybeM_def by (cases y; simp add: bind_def return_def valid_def) + +lemma notM_wp: + "\P\ m \\c. Q (\ c)\ \ \P\ notM m \Q\" + unfolding notM_def by (fastforce simp: bind_def return_def valid_def) + +lemma ifM_wp: + assumes [wp]: "\Q\ f \S\" "\R\ g \S\" + assumes [wp]: "\A\ P \\c s. c \ Q s\" "\B\ P \\c s. \c \ R s\" + shows "\A and B\ ifM P f g \S\" + unfolding ifM_def using assms + by (fastforce simp: bind_def valid_def split: if_splits) + +lemma andM_wp: + assumes [wp]: "\Q'\ B \Q\" + assumes [wp]: "\P\ A \\c s. c \ Q' s\" "\P'\ A \\c s. \ c \ Q False s\" + shows "\P and P'\ andM A B \Q\" + unfolding andM_def by (wp ifM_wp return_wp) + +lemma orM_wp: + assumes [wp]: "\Q'\ B \Q\" + assumes [wp]: "\P\ A \\c s. c \ Q True s\" "\P'\ A \\c s. \ c \ Q' s\" + shows "\P and P'\ orM A B \Q\" + unfolding orM_def by (wp ifM_wp return_wp) + +lemma whenM_wp: + assumes [wp]: "\Q\ f \S\" + assumes [wp]: "\A\ P \\c s. c \ Q s\" "\B\ P \\c s. \c \ S () s\" + shows "\A and B\ whenM P f \S\" + unfolding whenM_def by (wp ifM_wp return_wp) + lemma hoare_K_bind[wp_split]: "\P\ f \Q\ \ \P\ K_bind f x \Q\" by simp +lemma validE_K_bind[wp_split]: + "\ P \ x \ Q \, \ E \ \ \ P \ K_bind x f \ Q \, \ E \" + by simp + lemma hoare_fun_app_wp: "\P\ f' x \Q'\ \ \P\ f' $ x \Q'\" "\P\ f x \Q\,\E\ \ \P\ f $ x \Q\,\E\" @@ -771,6 +941,31 @@ lemma case_option_wpE: lemmas liftME_E_E_wp[wp_split] = validE_validE_E [OF liftME_wp, simplified, OF validE_E_validE] +lemma assert_opt_wp: + "\\s. x \ None \ Q (the x) s\ assert_opt x \Q\" + unfolding assert_opt_def + by (case_tac x; wpsimp wp: fail_wp return_wp) + +lemma gets_the_wp: + "\\s. (f s \ None) \ Q (the (f s)) s\ gets_the f \Q\" + unfolding gets_the_def + by (wp seq_ext gets_wp assert_opt_wp) + +lemma gets_the_wp': (* FIXME: should prefer this one in [wp] *) + "\\s. \rv. f s = Some rv \ Q rv s\ gets_the f \Q\" + unfolding gets_the_def + by (wpsimp wp: seq_ext gets_wp assert_opt_wp) + +lemma gets_map_wp: + "\\s. f s p \ None \ Q (the (f s p)) s\ gets_map f p \Q\" + unfolding gets_map_def + by (wpsimp wp: seq_ext gets_wp assert_opt_wp) + +lemma gets_map_wp': + "\\s. \rv. f s p = Some rv \ Q rv s\ gets_map f p \Q\" + unfolding gets_map_def + by (wpsimp wp: seq_ext gets_wp assert_opt_wp) + (* FIXME: make wp *) lemma whenE_throwError_wp: "\\s. \Q \ P s\ whenE Q (throwError e) \\rv. P\, -" @@ -864,6 +1059,9 @@ lemmas [wp] = hoare_vcg_prop failE_wp assert_wp state_assert_wp + assert_opt_wp + gets_the_wp + gets_map_wp' liftE_wp alternative_wp alternativeE_R_wp @@ -873,6 +1071,7 @@ lemmas [wp] = hoare_vcg_prop state_select_wp condition_wp conditionE_wp + maybeM_wp notM_wp ifM_wp andM_wp orM_wp whenM_wp lemmas [wp_trip] = valid_is_triple validE_is_triple validE_E_is_triple validE_R_is_triple @@ -965,8 +1164,20 @@ lemmas hoare_wp_pred_conj_elims = hoare_elim_pred_conjE2 hoare_elim_pred_conjE_R +subsection \Bundles\ + +bundle no_pre = hoare_pre [wp_pre del] + +bundle classic_wp_pre = hoare_pre [wp_pre del] + all_classic_wp_combs[wp_comb del] all_classic_wp_combs[wp_comb] + + text \Miscellaneous lemmas on hoare triples\ +lemma hoare_pre_cases: + "\ \\s. R s \ P s\ f \Q\; \\s. \R s \ P' s\ f \Q\ \ \ \P and P'\ f \Q\" + unfolding valid_def by fastforce + lemma hoare_vcg_mp: "\ \P\ f \Q\; \P\ f \\r s. Q r s \ Q' r s\ \ \ \P\ f \Q'\" by (auto simp: valid_def split_def) @@ -988,6 +1199,12 @@ lemma hoare_list_case: \case xs of [] \ P1 | y#ys \ P2 y ys\ f (case xs of [] \ f1 | y#ys \ f2 y ys) \Q\" by (cases xs; simp) +lemmas whenE_wps[wp_split] = + whenE_wp whenE_wp[THEN validE_validE_R] whenE_wp[THEN validE_validE_E] + +lemmas unlessE_wps[wp_split] = + unlessE_wp unlessE_wp[THEN validE_validE_R] unlessE_wp[THEN validE_validE_E] + lemma hoare_use_eq: assumes "\P. \\s. P (f s)\ m \\_ s. P (f s)\" assumes "\f. \\s. P f s\ m \\_ s. Q f s\" @@ -1043,12 +1260,58 @@ lemma hoare_drop_impE_E: lemmas hoare_drop_imps = hoare_drop_imp hoare_drop_impE_R hoare_drop_impE_E +(*This is unsafe, but can be very useful when supplied as a comb rule.*) +lemma hoare_drop_imp_conj[wp_unsafe]: + "\ \P\ f \Q'\; \P'\ f \\rv s. (Q rv s \ Q'' rv s) \ Q''' rv s\ \ \ + \P and P'\ f \\rv s. (Q rv s \ Q' rv s \ Q'' rv s) \ Q''' rv s\" + by (auto simp: valid_def) + +lemmas hoare_drop_imp_conj'[wp_unsafe] = hoare_drop_imp_conj[where Q'''="\\", simplified] + lemmas bindE_E_wp[wp_split] = validE_validE_E[OF hoare_vcg_seqE [OF validE_E_validE]] lemma True_E_E[wp]: "\\\ f -,\\\\" by (auto simp: validE_E_def validE_def valid_def split: sum.splits) +lemma hoare_vcg_set_pred_lift: + assumes "\P x. m \ \s. P (f x s) \" + shows "m \ \s. P {x. f x s} \" + using assms[where P="\x . x"] assms[where P=Not] use_valid + by (fastforce simp: valid_def elim!: subst[rotated, where P=P]) + +lemma hoare_vcg_set_pred_lift_mono: + assumes f: "\x. m \ f x \" + assumes mono: "\A B. A \ B \ P A \ P B" + shows "m \ \s. P {x. f x s} \" + by (fastforce simp: valid_def elim!: mono[rotated] dest: use_valid[OF _ f]) + +text \If a function contains an @{term assert}, or equivalent, then it might be + possible to strengthen the precondition of an already-proven hoare triple + @{text pos}, by additionally proving a side condition @{text neg}, that + violating some condition causes failure. The stronger hoare triple produced + by this theorem allows the precondition to assume that the condition is + satisfied.\ +lemma hoare_strengthen_pre_via_assert_forward: + assumes pos: "\ P \ f \ Q \" + assumes rel: "\s. S s \ P s \ N s" + assumes neg: "\ N \ f \ \\ \" + shows "\ S \ f \ Q \" + apply (rule hoare_weaken_pre) + apply (rule hoare_strengthen_post) + apply (rule hoare_vcg_disj_lift[OF pos neg]) + by (auto simp: rel) + +text \Like @{thm hoare_strengthen_pre_via_assert_forward}, strengthen a precondition + by proving a side condition that the negation of that condition would cause + failure. This version is intended for backward reasoning. Apply it to a goal to + obtain a stronger precondition after proving the side condition.\ +lemma hoare_strengthen_pre_via_assert_backward: + assumes neg: "\ Not \ E \ f \ \\ \" + assumes pos: "\ P and E \ f \ Q \" + shows "\ P \ f \ Q \" + by (rule hoare_strengthen_pre_via_assert_forward[OF pos _ neg], simp) + subsection \Strongest postcondition rules\ @@ -1080,4 +1343,44 @@ lemma hoare_returnOk_sp: "\P\ returnOk x \\rv s. rv = x \ P s\, \Q\" by (simp add: valid_def validE_def returnOk_def return_def mres_def) +\ \For forward reasoning in Hoare proofs, these lemmas allow us to step over the + left-hand-side of monadic bind, while keeping the same precondition.\ + +named_theorems forward_inv_step_rules + +lemmas hoare_forward_inv_step_nobind[forward_inv_step_rules] = + hoare_seq_ext_nobind[where B=A and A=A for A, rotated] + +lemmas hoare_seq_ext_skip[forward_inv_step_rules] = + hoare_seq_ext[where B="\_. A" and A=A for A, rotated] + +lemmas hoare_forward_inv_step_nobindE_valid[forward_inv_step_rules] = + hoare_seq_ext_nobindE[where B=A and A=A and E="\_. C" and C="\_. C" for A C, + simplified validE_eq_valid, rotated] + +lemmas hoare_forward_inv_step_valid[forward_inv_step_rules] = + hoare_vcg_seqE[where B="\_. A" and A=A and E="\_. C" and C="\_. C" for A C, + simplified validE_eq_valid, rotated] + +lemmas hoare_forward_inv_step_nobindE[forward_inv_step_rules] = + hoare_seq_ext_nobindE[where B=A and A=A for A, rotated] + +lemmas hoare_seq_ext_skipE[forward_inv_step_rules] = + hoare_vcg_seqE[where B="\_. A" and A=A for A, rotated] + +lemmas hoare_forward_inv_step_nobindE_validE_E[forward_inv_step_rules] = + hoare_forward_inv_step_nobindE[where C="\\", simplified validE_E_def[symmetric]] + +lemmas hoare_forward_inv_step_validE_E[forward_inv_step_rules] = + hoare_seq_ext_skipE[where C="\\", simplified validE_E_def[symmetric]] + +lemmas hoare_forward_inv_step_nobindE_validE_R[forward_inv_step_rules] = + hoare_forward_inv_step_nobindE[where E="\\", simplified validE_R_def[symmetric]] + +lemmas hoare_forward_inv_step_validE_R[forward_inv_step_rules] = + hoare_seq_ext_skipE[where E="\\", simplified validE_R_def[symmetric]] + +method forward_inv_step uses wp simp = + rule forward_inv_step_rules, solves \wpsimp wp: wp simp: simp\ + end