Theory Refine_Basic

theory Refine_Basic
imports Monad_Syntax RefineG_Recursion RefineG_Assert
header {* \isaheader{Basic Concepts} *}
theory Refine_Basic
imports Main 
  "~~/src/HOL/Library/Monad_Syntax" 
  "Refine_Misc"
  "Generic/RefineG_Recursion"
  "Generic/RefineG_Assert"
begin

subsection {* Setup *}
  ML {*
    structure Refine = struct

    structure vcg = Named_Thms
      ( val name = @{binding refine_vcg}
        val description = "Refinement Framework: " ^ 
          "Verification condition generation rules (intro)" )

    structure refine0 = Named_Thms
      ( val name = @{binding refine0}
        val description = "Refinement Framework: " ^
          "Refinement rules applied first (intro)" )

    structure refine = Named_Thms
      ( val name = @{binding refine}
        val description = "Refinement Framework: Refinement rules (intro)" )

    structure refine2 = Named_Thms
      ( val name = @{binding refine2}
        val description = "Refinement Framework: " ^
          "Refinement rules 2nd stage (intro)" )

    structure refine_pw_simps = Named_Thms
      ( val name = @{binding refine_pw_simps}
        val description = "Refinement Framework: " ^
          "Simplifier rules for pointwise reasoning" )

    (* Replaced by respective solvers in tagged solver
    structure refine_post = Named_Thms
      ( val name = @{binding refine_post}
        val description = "Refinement Framework: " ^
          "Postprocessing rules" )
    *)



    (* If set to true, the product splitter of refine_rcg is disabled. *)
    val no_prod_split = 
      Attrib.setup_config_bool @{binding refine_no_prod_split} (K false);

    fun rcg_tac add_thms ctxt = 
      let 
        val ref_thms = (refine0.get ctxt 
          @ add_thms @ refine.get ctxt @ refine2.get ctxt);
        val prod_ss = (Splitter.add_split @{thm prod.split} 
          (put_simpset HOL_basic_ss ctxt));
        val prod_simp_tac = 
          if Config.get ctxt no_prod_split then 
            K no_tac
          else
            (simp_tac prod_ss THEN' 
              REPEAT_ALL_NEW (resolve_tac @{thms impI allI}));
      in
        REPEAT_ALL_NEW (DETERM o (resolve_tac ref_thms ORELSE' prod_simp_tac))
      end;

      fun post_tac ctxt = let
        (*val thms = refine_post.get ctxt;*)
      in
        REPEAT_ALL_NEW (FIRST' [
          eq_assume_tac,
          (*match_tac thms,*)
          SOLVED' (Tagged_Solver.solve_tac ctxt)]) 
           (* TODO: Get rid of refine_post! Use tagged_solver instead *)
      end;

    end;
  *}
  setup {* Refine.vcg.setup *}
  setup {* Refine.refine0.setup *}
  setup {* Refine.refine.setup *}
  setup {* Refine.refine2.setup *}
  (*setup {* Refine.refine_post.setup *}*)
  setup {* Refine.refine_pw_simps.setup *}

  method_setup refine_rcg = 
    {* Attrib.thms >> (fn add_thms => fn ctxt => SIMPLE_METHOD' (
      Refine.rcg_tac add_thms ctxt THEN_ALL_NEW (TRY o Refine.post_tac ctxt)
    )) *} 
    "Refinement framework: Generate refinement conditions"

  (* Use tagged-solver instead!
  method_setup refine_post = 
    {* Scan.succeed (fn ctxt => SIMPLE_METHOD' (
      Refine.post_tac ctxt
    )) *} 
    "Refinement framework: Postprocessing of refinement goals"
    *)


subsection {* Nondeterministic Result Lattice and Monad *}
text {*
  In this section we introduce a complete lattice of result sets with an
  additional top element that represents failure. On this lattice, we define
  a monad: The return operator models a result that consists of a single value,
  and the bind operator models applying a function to all results.
  Binding a failure yields always a failure.
  
  In addition to the return operator, we also introduce the operator 
  @{text RES}, that embeds a set of results into our lattice. Its synonym for
  a predicate is @{text SPEC}.

  Program correctness is expressed by refinement, i.e., the expression
  @{text "M ≤ SPEC Φ"} means that @{text M} is correct w.r.t.\ 
  specification @{text Φ}. This suggests the following view on the program 
  lattice: The top-element is the result that is never correct. We call this
  result @{text FAIL}. The bottom element is the program that is always correct.
  It is called @{text SUCCEED}. An assertion can be encoded by failing if the
  asserted predicate is not true. Symmetrically, an assumption is encoded by
  succeeding if the predicate is not true. 
*}


datatype 'a nres = FAILi | RES "'a set"
text {*
  @{text FAILi} is only an internal notation, that should not be exposed to 
  the user.
  Instead, @{text FAIL} should be used, that is defined later as abbreviation 
  for the top element of the lattice.
*}
instantiation nres :: (type) complete_lattice
begin
fun less_eq_nres where
  "_ ≤ FAILi <-> True" |
  "(RES a) ≤ (RES b) <-> a⊆b" |
  "FAILi ≤ (RES _) <-> False"

fun less_nres where
  "FAILi < _ <-> False" |
  "(RES _) < FAILi <-> True" |
  "(RES a) < (RES b) <-> a⊂b"

fun sup_nres where
  "sup _ FAILi = FAILi" |
  "sup FAILi _ = FAILi" |
  "sup (RES a) (RES b) = RES (a∪b)"

fun inf_nres where 
  "inf x FAILi = x" |
  "inf FAILi x = x" |
  "inf (RES a) (RES b) = RES (a∩b)"

definition "Sup X ≡ if FAILi∈X then FAILi else RES (\<Union>{x . RES x ∈ X})"
definition "Inf X ≡ if ∃x. RES x∈X then RES (\<Inter>{x . RES x ∈ X}) else FAILi"

definition "bot ≡ RES {}"
definition "top ≡ FAILi"

instance
  apply (intro_classes)
  unfolding Sup_nres_def Inf_nres_def bot_nres_def top_nres_def
  apply (case_tac x, case_tac [!] y, auto) []
  apply (case_tac x, auto) []
  apply (case_tac x, case_tac [!] y, case_tac [!] z, auto) []
  apply (case_tac x, (case_tac [!] y)?, auto) []
  apply (case_tac x, (case_tac [!] y)?, simp_all) []
  apply (case_tac x, (case_tac [!] y)?, auto) []
  apply (case_tac x, case_tac [!] y, case_tac [!] z, auto) []
  apply (case_tac x, (case_tac [!] y)?, auto) []
  apply (case_tac x, (case_tac [!] y)?, auto) []
  apply (case_tac x, case_tac [!] y, case_tac [!] z, auto) []
  apply (case_tac x, auto) []
  apply (case_tac z, fastforce+) []
  apply (case_tac x, auto) []
  apply (case_tac z, fastforce+) []
  apply auto []
  apply auto []
  done
  
end

abbreviation "FAIL ≡ top::'a nres"
abbreviation "SUCCEED ≡ bot::'a nres"
abbreviation "SPEC Φ ≡ RES (Collect Φ)"
definition "RETURN x ≡ RES {x}"

text {* We try to hide the original @{text "FAILi"}-element as well as possible. 
*}
lemma nres_cases[case_names FAIL RES, cases type]:
  obtains "M=FAIL" | X where "M=RES X"
  apply (cases M, fold top_nres_def) by auto

lemma nres_simp_internals: 
  "RES {} = SUCCEED"
  "FAILi = FAIL" 
  unfolding top_nres_def bot_nres_def by simp_all

lemma nres_inequalities[simp]: 
  "FAIL ≠ RES X"
  "FAIL ≠ SUCCEED" 
  "FAIL ≠ RETURN x"
  "SUCCEED ≠ FAIL"
  "SUCCEED ≠ RETURN x"
  "RES X ≠ FAIL"
  "RETURN x ≠ FAIL"
  "RETURN x ≠ SUCCEED"
  unfolding top_nres_def bot_nres_def RETURN_def
  by auto

lemma nres_more_simps[simp]:
  "SUCCEED = RES X <-> X={}"
  "RES X = SUCCEED <-> X={}"
  "RES X = RETURN x <-> X={x}"
  "RES X = RES Y <-> X=Y"
  "RETURN x = RES X <-> {x}=X"
  "RETURN x = RETURN y <-> x=y"
  unfolding top_nres_def bot_nres_def RETURN_def by auto

lemma nres_order_simps[simp]:
  "!!M. SUCCEED ≤ M"
  "!!M. M ≤ SUCCEED <-> M=SUCCEED"
  "!!M. M ≤ FAIL"
  "!!M. FAIL ≤ M <-> M=FAIL"
  "!!X Y. RES X ≤ RES Y <-> X≤Y"
  "!!X. Sup X = FAIL <-> FAIL∈X"
  "!!X f. SUPREMUM X f = FAIL <-> FAIL ∈ f ` X"
  "!!X. FAIL = Sup X <-> FAIL∈X"
  "!!X f. FAIL = SUPREMUM X f <-> FAIL ∈ f ` X"
  "!!X. FAIL∈X ==> Sup X = FAIL"
  "!!X. FAIL∈f ` X ==> SUPREMUM X f = FAIL"
  "!!A. Sup (RES ` A) = RES (Sup A)"
  "!!A. SUPREMUM A RES = RES (Sup A)"
  "!!A. A≠{} ==> Inf (RES`A) = RES (Inf A)"
  "!!A. A≠{} ==> INFIMUM A RES = RES (Inf A)"
  "Inf {} = FAIL"
  "Inf UNIV = SUCCEED"
  "Sup {} = SUCCEED"
  "Sup UNIV = FAIL"
  "!!x y. RETURN x ≤ RETURN y <-> x=y"
  "!!x Y. RETURN x ≤ RES Y <-> x∈Y"
  "!!X y. RES X ≤ RETURN y <-> X ⊆ {y}"
  unfolding Sup_nres_def Inf_nres_def SUP_def INF_def RETURN_def
  by (auto simp add: bot_unique top_unique nres_simp_internals)

lemma Sup_eq_RESE:
  assumes "Sup A = RES B"
  obtains C where "A=RES`C" and "B=Sup C"
proof -
  show ?thesis
    using assms unfolding Sup_nres_def
    apply (simp split: split_if_asm)
    apply (rule_tac C="{X. RES X ∈ A}" in that)
    apply auto []
    apply (case_tac x, auto simp: nres_simp_internals) []
    apply (auto simp: nres_simp_internals) []
    done
qed

declare nres_simp_internals[simp]

subsubsection {* Pointwise Reasoning *}

definition "nofail S ≡ S≠FAIL"
definition "inres S x ≡ RETURN x ≤ S"

lemma nofail_simps[simp, refine_pw_simps]:
  "nofail FAIL <-> False"
  "nofail (RES X) <-> True"
  "nofail (RETURN x) <-> True"
  "nofail SUCCEED <-> True"
  unfolding nofail_def
  by (simp_all add: RETURN_def)

lemma inres_simps[simp, refine_pw_simps]:
  "inres FAIL = (λ_. True)"
  "inres (RES X) = (λx. x∈X)"
  "inres (RETURN x) = (λy. x=y)"
  "inres SUCCEED = (λ_. False)"
  unfolding inres_def [abs_def]
  by (auto simp add: RETURN_def)

lemma not_nofail_iff: 
  "¬nofail S <-> S=FAIL" by (cases S) auto

lemma not_nofail_inres[simp, refine_pw_simps]: 
  "¬nofail S ==> inres S x" 
  apply (cases S) by auto

lemma intro_nofail[refine_pw_simps]: 
  "S≠FAIL <-> nofail S"
  "FAIL≠S <-> nofail S"
  by (cases S, simp_all)+

text {* The following two lemmas will introduce pointwise reasoning for
  orderings and equalities. *}
lemma pw_le_iff: 
  "S ≤ S' <-> (nofail S'--> (nofail S ∧ (∀x. inres S x --> inres S' x)))"
  apply (cases S, simp_all)
  apply (case_tac [!] S', auto)
  done

lemma pw_eq_iff:
  "S=S' <-> (nofail S = nofail S' ∧ (∀x. inres S x <-> inres S' x))"
  apply (rule iffI)
  apply simp
  apply (rule antisym)
  apply (simp_all add: pw_le_iff)
  done

lemma pw_flat_le_iff: "flat_le S S' <-> 
  (∃x. inres S x) --> (nofail S <-> nofail S') ∧ (∀x. inres S x <-> inres S' x)"
  by (auto simp : flat_ord_def pw_eq_iff)
  
lemma pw_flat_ge_iff: "flat_ge S S' <-> 
  (nofail S) --> nofail S' ∧ (∀x. inres S x <-> inres S' x)"
  apply (simp add: flat_ord_def pw_eq_iff) apply safe
  apply simp
  apply simp
  apply simp
  apply (rule ccontr)
  apply simp
  done

lemmas pw_ords_iff = pw_le_iff pw_flat_le_iff pw_flat_ge_iff

lemma pw_leI: 
  "(nofail S'--> (nofail S ∧ (∀x. inres S x --> inres S' x))) ==> S ≤ S'"
  by (simp add: pw_le_iff)

lemma pw_leI': 
  assumes "nofail S' ==> nofail S"
  assumes "!!x. [|nofail S'; inres S x|] ==> inres S' x"
  shows "S ≤ S'"
  using assms
  by (simp add: pw_le_iff)

lemma pw_eqI: 
  assumes "nofail S = nofail S'" 
  assumes "!!x. inres S x <-> inres S' x" 
  shows "S=S'"
  using assms by (simp add: pw_eq_iff)

lemma pwD1:
  assumes "S≤S'" "nofail S'" 
  shows "nofail S"
  using assms by (simp add: pw_le_iff)

lemma pwD2:
  assumes "S≤S'" "inres S x"
  shows "inres S' x"
  using assms 
  by (auto simp add: pw_le_iff)

lemmas pwD = pwD1 pwD2

text {*
  When proving refinement, we may assume that the refined program does not 
  fail. *}
lemma le_nofailI: "[| nofail M' ==> M ≤ M' |] ==> M ≤ M'"
  by (cases M') auto

text {* The following lemmas push pointwise reasoning over operators,
  thus converting an expression over lattice operators into a logical
  formula. *}

lemma pw_sup_nofail[refine_pw_simps]:
  "nofail (sup a b) <-> nofail a ∧ nofail b"
  apply (cases a, simp)
  apply (cases b, simp_all)
  done

lemma pw_sup_inres[refine_pw_simps]:
  "inres (sup a b) x <-> inres a x ∨ inres b x"
  apply (cases a, simp)
  apply (cases b, simp)
  apply (simp)
  done

lemma pw_Sup_inres[refine_pw_simps]: "inres (Sup X) r <-> (∃M∈X. inres M r)"
  apply (cases "Sup X")
  apply (simp)
  apply (erule bexI[rotated])
  apply simp
  apply (erule Sup_eq_RESE)
  apply (simp)
  done

lemma pw_SUP_inres [refine_pw_simps]: "inres (SUPREMUM X f) r <-> (∃M∈X. inres (f M) r)"
  using pw_Sup_inres [of "f ` X"] by simp

lemma pw_Sup_nofail[refine_pw_simps]: "nofail (Sup X) <-> (∀x∈X. nofail x)"
  apply (cases "Sup X")
  apply force
  apply simp
  apply (erule Sup_eq_RESE)
  apply auto
  done

lemma pw_SUP_nofail [refine_pw_simps]: "nofail (SUPREMUM X f) <-> (∀x∈X. nofail (f x))"
  using pw_Sup_nofail [of "f ` X"] by simp

lemma pw_inf_nofail[refine_pw_simps]:
  "nofail (inf a b) <-> nofail a ∨ nofail b"
  apply (cases a, simp)
  apply (cases b, simp_all)
  done

lemma pw_inf_inres[refine_pw_simps]:
  "inres (inf a b) x <-> inres a x ∧ inres b x"
  apply (cases a, simp)
  apply (cases b, simp)
  apply (simp)
  done

lemma pw_Inf_nofail[refine_pw_simps]: "nofail (Inf C) <-> (∃x∈C. nofail x)"
  apply (cases "C={}")
  apply simp
  apply (cases "Inf C")
  apply (subgoal_tac "C={FAIL}")
  apply simp
  apply auto []
  apply (subgoal_tac "C≠{FAIL}")
  apply (auto simp: not_nofail_iff) []
  apply auto []
  done

lemma pw_INF_nofail [refine_pw_simps]: "nofail (INFIMUM C f) <-> (∃x∈C. nofail (f x))"
  using pw_Inf_nofail [of "f ` C"] by simp

lemma pw_Inf_inres[refine_pw_simps]: "inres (Inf C) r <-> (∀M∈C. inres M r)"
  apply (unfold Inf_nres_def)
  apply auto
  apply (case_tac M)
  apply force
  apply force
  apply (case_tac M)
  apply force
  apply force
  done

lemma pw_INF_inres [refine_pw_simps]: "inres (INFIMUM C f) r <-> (∀M∈C. inres (f M) r)"
  using pw_Inf_inres [of "f ` C"] by simp


subsubsection {* Monad Operators *}
definition bind where "bind M f ≡ case M of 
  FAILi => FAIL |
  RES X => Sup (f`X)"

lemma bind_FAIL[simp]: "bind FAIL f = FAIL"
  unfolding bind_def by (auto split: nres.split)

lemma bind_SUCCEED[simp]: "bind SUCCEED f = SUCCEED"
  unfolding bind_def by (auto split: nres.split)

lemma bind_RES: "bind (RES X) f = Sup (f`X)" unfolding bind_def 
  by (auto)

adhoc_overloading
  Monad_Syntax.bind Refine_Basic.bind

lemma pw_bind_nofail[refine_pw_simps]:
  "nofail (bind M f) <-> (nofail M ∧ (∀x. inres M x --> nofail (f x)))"
  apply (cases M)
  by (auto simp: bind_RES refine_pw_simps)
  
lemma pw_bind_inres[refine_pw_simps]:
  "inres (bind M f) = (λx. nofail M --> (∃y. (inres M y ∧ inres (f y) x)))"
  apply (rule ext)
  apply (cases M)
  apply (auto simp add: bind_RES refine_pw_simps)
  done

lemma pw_bind_le_iff:
  "bind M f ≤ S <-> (nofail S --> nofail M) ∧ 
    (∀x. nofail M ∧ inres M x --> f x ≤ S)"
  by (auto simp: pw_le_iff refine_pw_simps)

lemma pw_bind_leI: "[| 
  nofail S ==> nofail M; !!x. [|nofail M; inres M x|] ==> f x ≤ S|] 
  ==> bind M f ≤ S"
  by (simp add: pw_bind_le_iff)

text {* \paragraph{Monad Laws} *}
lemma nres_monad1[simp]: "bind (RETURN x) f = f x"
  by (rule pw_eqI) (auto simp: refine_pw_simps)
lemma nres_monad2[simp]: "bind M RETURN = M"
  by (rule pw_eqI) (auto simp: refine_pw_simps)
lemma nres_monad3[simp]: "bind (bind M f) g = bind M (λx. bind (f x) g)"
  by (rule pw_eqI) (auto simp: refine_pw_simps)
lemmas nres_monad_laws = nres_monad1 nres_monad2 nres_monad3


text {* \paragraph{Monotonicity and Related Properties} *}
lemma bind_mono[refine_mono]:
  "[| M ≤ M'; !!x. RETURN x ≤ M ==> f x ≤ f' x |] ==> bind M f ≤ bind M' f'"
  (*"[| flat_le M M'; !!x. flat_le (f x) (f' x) |] ==> flat_le (bind M f) (bind M' f')"*)
  "[| flat_ge M M'; !!x. flat_ge (f x) (f' x) |] ==> flat_ge (bind M f) (bind M' f')"
  apply (auto simp: refine_pw_simps pw_ords_iff) []
  apply (auto simp: refine_pw_simps pw_ords_iff) []
  done

thm partial_function_mono

lemma bind_mono1[simp, intro!]: "mono (λM. bind M f)"
  apply (rule monoI)
  apply (rule bind_mono)
  by auto

lemma bind_mono1'[simp, intro!]: "mono bind"
  apply (rule monoI)
  apply (rule le_funI)
  apply (rule bind_mono)
  by auto

lemma bind_mono2'[simp, intro!]: "mono (bind M)"
  apply (rule monoI)
  apply (rule bind_mono)
  by (auto dest: le_funD)


lemma bind_distrib_sup: "bind (sup M N) f = sup (bind M f) (bind N f)"
  by (auto simp add: pw_eq_iff refine_pw_simps)

lemma RES_Sup_RETURN: "Sup (RETURN`X) = RES X"
  by (rule pw_eqI) (auto simp add: refine_pw_simps)

subsection {* Data Refinement *}
text {*
  In this section we establish a notion of pointwise data refinement, by
  lifting a relation @{text "R"} between concrete and abstract values to 
  our result lattice.

  Given a relation @{text R}, we define a {\em concretization function}
  @{text "\<Down>R"} that takes an abstract result, and returns a concrete result.
  The concrete result contains all values that are mapped by @{text R} to
  a value in the abstract result.

  Note that our concretization function forms no Galois connection, i.e.,
  in general there is no @{text α} such that 
  @{text "m ≤\<Down> R m'"} is equivalent to @{text "α m ≤ m'"}.
  However, we get a Galois connection for the special case of 
  single-valued relations.
 
  Regarding data refinement as Galois connections is inspired by \cite{mmo97},
  that also uses the adjuncts of
  a Galois connection to express data refinement by program refinement.
*}

definition conc_fun ("\<Down>") where
  "conc_fun R m ≡ case m of FAILi => FAIL | RES X => RES (R¯``X)"

definition abs_fun ("\<Up>") where
  "abs_fun R m ≡ case m of FAILi => FAIL 
    | RES X => if X⊆Domain R then RES (R``X) else FAIL"

lemma 
  conc_fun_FAIL[simp]: "\<Down>R FAIL = FAIL" and
  conc_fun_RES: "\<Down>R (RES X) = RES (R¯``X)"
  unfolding conc_fun_def by (auto split: nres.split)

lemma abs_fun_simps[simp]: 
  "\<Up>R FAIL = FAIL"
  "X⊆Domain R ==> \<Up>R (RES X) = RES (R``X)"
  "¬(X⊆Domain R) ==> \<Up>R (RES X) = FAIL"
  unfolding abs_fun_def by (auto split: nres.split)
  
context fixes R assumes SV: "single_valued R" begin
lemma conc_abs_swap: "m' ≤ \<Down>R m <-> \<Up>R m' ≤ m"
  unfolding conc_fun_def abs_fun_def using SV
  by (auto split: nres.split)
    (metis ImageE converseD single_valuedD subsetD)

lemma ac_galois: "galois_connection (\<Up>R) (\<Down>R)"
  apply (unfold_locales)
  by (rule conc_abs_swap)

end

lemma pw_abs_nofail[refine_pw_simps]: 
  "nofail (\<Up>R M) <-> (nofail M ∧ (∀x. inres M x --> x∈Domain R))"
  apply (cases M)
  apply simp
  apply (auto simp: abs_fun_simps abs_fun_def)
  done

lemma pw_abs_inres[refine_pw_simps]: 
  "inres (\<Up>R M) a <-> (nofail (\<Up>R M) --> (∃c. inres M c ∧ (c,a)∈R))"
  apply (cases M)
  apply simp
  apply (auto simp: abs_fun_def)
  done

lemma pw_conc_nofail[refine_pw_simps]: 
  "nofail (\<Down>R S) = nofail S"
  by (cases S) (auto simp: conc_fun_RES)

lemma pw_conc_inres[refine_pw_simps]:
  "inres (\<Down>R S') = (λs. nofail S' 
  --> (∃s'. (s,s')∈R ∧ inres S' s'))"
  apply (rule ext)
  apply (cases S')
  apply (auto simp: conc_fun_RES)
  done

lemma abs_fun_strict[simp]:
  "\<Up> R SUCCEED = SUCCEED"
  unfolding abs_fun_def by (auto split: nres.split)

lemma conc_fun_strict[simp]:
  "\<Down> R SUCCEED = SUCCEED"
  unfolding conc_fun_def by (auto split: nres.split)

lemma conc_fun_mono[simp, intro!]: "mono (\<Down>R)"
  by rule (auto simp: pw_le_iff refine_pw_simps)

lemma abs_fun_mono[simp, intro!]: "mono (\<Up>R)"
  by rule (auto simp: pw_le_iff refine_pw_simps)

lemma conc_fun_chain: "\<Down>R (\<Down>S M) = \<Down>(R O S) M"
  unfolding conc_fun_def
  by (auto split: nres.split)

lemma conc_Id[simp]: "\<Down>Id = id"
  unfolding conc_fun_def [abs_def] by (auto split: nres.split)

lemma abs_Id[simp]: "\<Up>Id = id"
  unfolding abs_fun_def [abs_def] by (auto split: nres.split)

lemma conc_fun_fail_iff[simp]: 
  "\<Down>R S = FAIL <-> S=FAIL"
  "FAIL = \<Down>R S <-> S=FAIL"
  by (auto simp add: pw_eq_iff refine_pw_simps)

lemma conc_trans[trans]:
  assumes A: "C ≤ \<Down>R B" and B: "B ≤ \<Down>R' A" 
  shows "C ≤ \<Down>R (\<Down>R' A)"
  using assms by (fastforce simp: pw_le_iff refine_pw_simps)

lemma abs_trans[trans]:
  assumes A: "\<Up>R C ≤ B" and B: "\<Up>R' B ≤ A" 
  shows "\<Up>R' (\<Up>R C) ≤ A"
  using assms by (fastforce simp: pw_le_iff refine_pw_simps)

subsubsection {* Transitivity Reasoner Setup *}

text {* WARNING: The order of the single statements is important here! *}
lemma conc_trans_additional[trans]:
  "!!A B C. A≤\<Down>R  B ==> B≤    C ==> A≤\<Down>R  C"
  "!!A B C. A≤\<Down>Id B ==> B≤\<Down>R  C ==> A≤\<Down>R  C"
  "!!A B C. A≤\<Down>R  B ==> B≤\<Down>Id C ==> A≤\<Down>R  C"

  "!!A B C. A≤\<Down>Id B ==> B≤\<Down>Id C ==> A≤    C"
  "!!A B C. A≤\<Down>Id B ==> B≤    C ==> A≤    C"
  "!!A B C. A≤    B ==> B≤\<Down>Id C ==> A≤    C"
  using conc_trans[where R=R and R'=Id]
  by (auto intro: order_trans)

text {* WARNING: The order of the single statements is important here! *}
lemma abs_trans_additional[trans]:
  "!!A B C. [| A ≤ B; \<Up> R B ≤ C|] ==> \<Up> R A ≤ C"
  "!!A B C. [|\<Up> Id A ≤ B; \<Up> R B ≤ C|] ==> \<Up> R A ≤ C"
  "!!A B C. [|\<Up> R A ≤ B; \<Up> Id B ≤ C|] ==> \<Up> R A ≤ C"

  "!!A B C. [|\<Up> Id A ≤ B; \<Up> Id B ≤ C|] ==> A ≤ C"
  "!!A B C. [|\<Up> Id A ≤ B; B ≤ C|] ==> A ≤ C"
  "!!A B C. [|A ≤ B; \<Up> Id B ≤ C|] ==> A ≤ C"

  apply (auto simp: refine_pw_simps pw_le_iff)
  apply fastforce+
  done


subsection {* Derived Program Constructs *}
text {*
  In this section, we introduce some programming constructs that are derived 
  from the basic monad and ordering operations of our nondeterminism monad.
*}
subsubsection {* ASSUME and ASSERT *}

definition ASSERT where "ASSERT ≡ iASSERT RETURN"
definition ASSUME where "ASSUME ≡ iASSUME RETURN"
interpretation assert?: generic_Assert bind RETURN ASSERT ASSUME
  apply unfold_locales
  by (simp_all add: ASSERT_def ASSUME_def)

lemma pw_ASSERT[refine_pw_simps]:
  "nofail (ASSERT Φ) <-> Φ"
  "inres (ASSERT Φ) x"
  by (cases Φ, simp_all)+

lemma pw_ASSUME[refine_pw_simps]:
  "nofail (ASSUME Φ)"
  "inres (ASSUME Φ) x <-> Φ"
  by (cases Φ, simp_all)+

subsubsection {* Recursion *}
lemma pw_REC_nofail: 
  shows "nofail (REC B x) <-> trimono B ∧
  (∃F. (∀x. 
    nofail (F x) --> nofail (B F x) 
    ∧ (∀x'. inres (B F x) x' --> inres (F x) x')
  ) ∧ nofail (F x))"
proof -
  have "nofail (REC B x) <-> trimono B ∧
  (∃F. (∀x. B F x ≤ F x) ∧ nofail (F x))"
    unfolding REC_def lfp_def
    apply (auto simp: refine_pw_simps intro: le_funI dest: le_funD)
    done
  thus ?thesis
    unfolding pw_le_iff .
qed

lemma pw_REC_inres: 
  "inres (REC B x) x' = (trimono B -->
  (∀F. (∀x''. 
    nofail (F x'') --> nofail (B F x'') 
    ∧ (∀x. inres (B F x'') x --> inres (F x'') x)) 
    --> inres (F x) x'))"
proof -
  have "inres (REC B x) x' 
    <-> (trimono B --> (∀F. (∀x''. B F x'' ≤ F x'') --> inres (F x) x'))"
    unfolding REC_def lfp_def
    by (auto simp: refine_pw_simps intro: le_funI dest: le_funD)
  thus ?thesis unfolding pw_le_iff .
qed
  
lemmas pw_REC = pw_REC_inres pw_REC_nofail

lemma pw_RECT_nofail: 
  shows "nofail (RECT B x) <-> trimono B ∧
  (∀F. (∀y. nofail (B F y) -->
             nofail (F y) ∧ (∀x. inres (F y) x --> inres (B F y) x)) -->
        nofail (F x))"
proof -
  have "nofail (RECT B x) <-> (trimono B ∧ (∀F. (∀y. F y ≤ B F y) --> nofail (F x)))"
    unfolding RECT_gfp_def gfp_def
    by (auto simp: refine_pw_simps intro: le_funI dest: le_funD)
  thus ?thesis
    unfolding pw_le_iff .
qed

lemma pw_RECT_inres: 
  shows "inres (RECT B x) x' = (trimono B -->
   (∃M. (∀y. nofail (B M y) -->
             nofail (M y) ∧ (∀x. inres (M y) x --> inres (B M y) x)) ∧
        inres (M x) x'))"
proof -
  have "inres (RECT B x) x' <-> trimono B --> (∃M. (∀y. M y ≤ B M y) ∧ inres (M x) x')"
    unfolding RECT_gfp_def gfp_def
    by (auto simp: refine_pw_simps intro: le_funI dest: le_funD)
  thus ?thesis unfolding pw_le_iff .
qed
  
lemmas pw_RECT = pw_RECT_inres pw_RECT_nofail

subsection {* Proof Rules *}

subsubsection {* Proving Correctness *}
text {*
  In this section, we establish Hoare-like rules to prove that a program
  meets its specification.
*}

lemma RETURN_rule[refine_vcg]: "Φ x ==> RETURN x ≤ SPEC Φ"
  by (auto simp: RETURN_def)
lemma RES_rule[refine_vcg]: "[|!!x. x∈S ==> Φ x|] ==> RES S ≤ SPEC Φ"
  by auto
lemma SUCCEED_rule[refine_vcg]: "SUCCEED ≤ SPEC Φ" by auto
lemma FAIL_rule: "False ==> FAIL ≤ SPEC Φ" by auto
lemma SPEC_rule[refine_vcg]: "[|!!x. Φ x ==> Φ' x|] ==> SPEC Φ ≤ SPEC Φ'" by auto

lemma RETURN_to_SPEC_rule[refine_vcg]: "m≤SPEC (op = v) ==> m≤RETURN v"
  by (simp add: pw_le_iff refine_pw_simps)

lemma Sup_img_rule_complete: 
  "(∀x. x∈S --> f x ≤ SPEC Φ) <-> Sup (f`S) ≤ SPEC Φ"
  apply rule
  apply (rule pw_leI)
  apply (auto simp: pw_le_iff refine_pw_simps) []
  apply (intro allI impI)
  apply (rule pw_leI)
  apply (auto simp: pw_le_iff refine_pw_simps) []
  done

lemma SUP_img_rule_complete: 
  "(∀x. x∈S --> f x ≤ SPEC Φ) <-> SUPREMUM S f ≤ SPEC Φ"
  using Sup_img_rule_complete [of S f] by simp

lemma Sup_img_rule[refine_vcg]: 
  "[| !!x. x∈S ==> f x ≤ SPEC Φ |] ==> Sup(f`S) ≤ SPEC Φ"
  by (auto simp: SUP_img_rule_complete[symmetric])

text {* This lemma is just to demonstrate that our rule is complete. *}
lemma bind_rule_complete: "bind M f ≤ SPEC Φ <-> M ≤ SPEC (λx. f x ≤ SPEC Φ)"
  by (auto simp: pw_le_iff refine_pw_simps)
lemma bind_rule[refine_vcg]: 
  "[| M ≤ SPEC (λx. f x ≤ SPEC Φ) |] ==> bind M (λx. f x) ≤ SPEC Φ"
  -- {* Note: @{text "η"}-expanded version helps Isabelle's unification to keep meaningful 
      variable names from the program *}
  by (auto simp: bind_rule_complete)

lemma ASSUME_rule[refine_vcg]: "[|Φ ==> Ψ ()|] ==> ASSUME Φ ≤ SPEC Ψ"
  by (cases Φ) auto

lemma ASSERT_rule[refine_vcg]: "[|Φ; Φ ==> Ψ ()|] ==> ASSERT Φ ≤ SPEC Ψ" by auto

lemma prod_rule[refine_vcg]: 
  "[|!!a b. p=(a,b) ==> S a b ≤ SPEC Φ|] ==> case_prod S p ≤ SPEC Φ"
  by (auto split: prod.split)

(* TODO: Add a simplifier setup that normalizes nested case-expressions to
  the vcg! *)
lemma prod2_rule[refine_vcg]:
  assumes "!!a b c d. [|ab=(a,b); cd=(c,d)|] ==> f a b c d ≤ SPEC Φ"
  shows "(λ(a,b) (c,d). f a b c d) ab cd ≤ SPEC Φ"
  using assms
  by (auto split: prod.split)

lemma if_rule[refine_vcg]: 
  "[| b ==> S1 ≤ SPEC Φ; ¬b ==> S2 ≤ SPEC Φ|] 
  ==> (if b then S1 else S2) ≤ SPEC Φ"
  by (auto)

lemma option_rule[refine_vcg]: 
  "[| v=None ==> S1 ≤ SPEC Φ; !!x. v=Some x ==> f2 x ≤ SPEC Φ|] 
  ==> case_option S1 f2 v ≤ SPEC Φ"
  by (auto split: option.split)

lemma Let_rule[refine_vcg]:
  "f x ≤ SPEC Φ ==> Let x f ≤ SPEC Φ" by auto

lemma Let_rule':
  assumes "!!x. x=v ==> f x ≤ SPEC Φ"
  shows "Let v (λx. f x) ≤ SPEC Φ"
  using assms by simp


(* Obsolete, use RECT_eq_REC_tproof instead
text {* The following lemma shows that greatest and least fixed point are equal,
  if we can provide a variant. *}
thm RECT_eq_REC
lemma RECT_eq_REC_old:
  assumes WF: "wf V"
  assumes I0: "I x"
  assumes IS: "!!f x. I x ==> 
    body (λx'. do { ASSERT (I x' ∧ (x',x)∈V); f x'}) x ≤ body f x"
  shows "RECT body x = REC body x"
  apply (rule RECT_eq_REC)
  apply (rule WF)
  apply (rule I0)
  apply (rule order_trans[OF _ IS])
  apply (subgoal_tac "(λx'. if I x' ∧ (x', x) ∈ V then f x' else FAIL) = 
    (λx'. ASSERT (I x' ∧ (x', x) ∈ V) »= (λ_. f x'))")
  apply simp
  apply (rule ext)
  apply (rule pw_eqI)
  apply (auto simp add: refine_pw_simps)
  done
*)

(* TODO: Also require RECT_le_rule. Derive RECT_invisible_refine from that. *)
lemma REC_le_rule:
  assumes M: "trimono body"
  assumes I0: "(x,x')∈R"
  assumes IS: "!!f x x'. [| !!x x'. (x,x')∈R ==> f x ≤ M x'; (x,x')∈R |] 
    ==> body f x ≤ M x'"
  shows "REC body x ≤ M x'"
  by (rule REC_rule_arb[OF M, where pre="λx' x. (x,x')∈R", OF I0 IS])

(* TODO: Invariant annotations and vcg-rule
  Possibility 1: Semantically alter the program, such that it fails if the 
    invariant does not hold
  Possibility 2: Only syntactically annotate the invariant, as hint for the VCG.
*)

subsubsection {* Proving Monotonicity *}

lemma nr_mono_bind:
  assumes MA: "mono A" and MB: "!!s. mono (B s)"
  shows "mono (λF s. bind (A F s) (λs'. B s F s'))"
  apply (rule monoI)
  apply (rule le_funI)
  apply (rule bind_mono)
  apply (auto dest: monoD[OF MA, THEN le_funD]) []
  apply (auto dest: monoD[OF MB, THEN le_funD]) []
  done


lemma nr_mono_bind': "mono (λF s. bind (f s) F)"
  apply rule
  apply (rule le_funI)
  apply (rule bind_mono)
  apply (auto dest: le_funD)
  done

lemmas nr_mono = nr_mono_bind nr_mono_bind' mono_const mono_if mono_id

subsubsection {* Proving Refinement *}
text {* In this subsection, we establish rules to prove refinement between 
  structurally similar programs. All rules are formulated including a possible
  data refinement via a refinement relation. If this is not required, the 
  refinement relation can be chosen to be the identity relation.
  *}

text {* If we have two identical programs, this rule solves the refinement goal
  immediately, using the identity refinement relation. *}
lemma Id_refine[refine0]: "S ≤ \<Down>Id S" by auto

lemma RES_refine: 
  "[| !!s. s∈S ==> ∃s'∈S'. (s,s')∈R|] ==> RES S ≤ \<Down>R (RES S')" 
  by (auto simp: conc_fun_RES)

lemma SPEC_refine: 
  assumes "S ≤ SPEC (λx. ∃x'. (x,x')∈R ∧ Φ x')"
  shows "S ≤ \<Down>R (SPEC Φ)"
  using assms
  by (force simp: pw_le_iff refine_pw_simps)

(* TODO/FIXME: This is already part of a type-based heuristics! *)
lemma Id_SPEC_refine[refine]: 
  "S ≤ SPEC Φ ==> S ≤ \<Down>Id (SPEC Φ)" by simp

lemma RETURN_refine[refine]:
  assumes "(x,x')∈R"
  shows "RETURN x ≤ \<Down>R (RETURN x')"
  using assms
  by (auto simp: RETURN_def conc_fun_RES)

lemma RETURN_SPEC_refine:
  assumes "∃x'. (x,x')∈R ∧ Φ x'"
  shows "RETURN x ≤ \<Down>R (SPEC Φ)"
  using assms 
  by (auto simp: pw_le_iff refine_pw_simps)

lemma FAIL_refine[refine]: "X ≤ \<Down>R FAIL" by auto
lemma SUCCEED_refine[refine]: "SUCCEED ≤ \<Down>R X'" by auto

text {* The next two rules are incomplete, but a good approximation for refining
  structurally similar programs. *}
lemma bind_refine':
  fixes R' :: "('a×'b) set" and R::"('c×'d) set"
  assumes R1: "M ≤ \<Down> R' M'"
  assumes R2: "!!x x'. [| (x,x')∈R'; inres M x; inres M' x';
    nofail M; nofail M'
  |] ==> f x ≤ \<Down> R (f' x')"
  shows "bind M (λx. f x) ≤ \<Down> R (bind M' (λx'. f' x'))"
  using assms
  apply (simp add: pw_le_iff refine_pw_simps)
  apply fast
  done

lemma bind_refine[refine]:
  fixes R' :: "('a×'b) set" and R::"('c×'d) set"
  assumes R1: "M ≤ \<Down> R' M'"
  assumes R2: "!!x x'. [| (x,x')∈R' |] 
    ==> f x ≤ \<Down> R (f' x')"
  shows "bind M (λx. f x) ≤ \<Down> R (bind M' (λx'. f' x'))"
  apply (rule bind_refine') using assms by auto

text {* Special cases for refinement of binding to @{text "RES"}
  statements *}
lemma bind_refine_RES:
  "[|RES X ≤ \<Down> R' M';
  !!x x'. [|(x, x') ∈ R'; x ∈ X |] ==> f x ≤ \<Down> R (f' x')|]
  ==> RES X »= (λx. f x) ≤ \<Down> R (M' »= (λx'. f' x'))"

  "[|M ≤ \<Down> R' (RES X');
  !!x x'. [|(x, x') ∈ R'; x' ∈ X' |] ==> f x ≤ \<Down> R (f' x')|]
  ==> M »= (λx. f x) ≤ \<Down> R (RES X' »= (λx'. f' x'))"

  "[|RES X ≤ \<Down> R' (RES X');
  !!x x'. [|(x, x') ∈ R'; x ∈ X; x' ∈ X'|] ==> f x ≤ \<Down> R (f' x')|]
  ==> RES X »= (λx. f x) ≤ \<Down> R (RES X' »= (λx'. f' x'))"
  by (auto intro!: bind_refine')

declare bind_refine_RES(1,2)[refine]
declare bind_refine_RES(3)[refine]


lemma ASSERT_refine[refine]:
  "[| Φ'==>Φ |] ==> ASSERT Φ ≤ \<Down>Id (ASSERT Φ')"
  by (cases Φ') auto

lemma ASSUME_refine[refine]: 
  "[| Φ ==> Φ' |] ==> ASSUME Φ ≤ \<Down>Id (ASSUME Φ')"
  by (cases Φ) auto

text {*
  Assertions and assumptions are treated specially in bindings
*}
lemma ASSERT_refine_right:
  assumes "Φ ==> S ≤\<Down>R S'"
  shows "S ≤\<Down>R (do {ASSERT Φ; S'})"
  using assms by (cases Φ) auto
lemma ASSERT_refine_right_pres:
  assumes "Φ ==> S ≤\<Down>R (do {ASSERT Φ; S'})"
  shows "S ≤\<Down>R (do {ASSERT Φ; S'})"
  using assms by (cases Φ) auto

lemma ASSERT_refine_left:
  assumes "Φ"
  assumes "Φ ==> S ≤ \<Down>R S'"
  shows "do{ASSERT Φ; S} ≤ \<Down>R S'"
  using assms by (cases Φ) auto

lemma ASSUME_refine_right:
  assumes "Φ"
  assumes "Φ ==> S ≤\<Down>R S'"
  shows "S ≤\<Down>R (do {ASSUME Φ; S'})"
  using assms by (cases Φ) auto

lemma ASSUME_refine_left:
  assumes "Φ ==> S ≤ \<Down>R S'"
  shows "do {ASSUME Φ; S} ≤ \<Down>R S'"
  using assms by (cases Φ) auto

lemma ASSUME_refine_left_pres:
  assumes "Φ ==> do {ASSUME Φ; S} ≤ \<Down>R S'"
  shows "do {ASSUME Φ; S} ≤ \<Down>R S'"
  using assms by (cases Φ) auto

text {* Warning: The order of @{text "[refine]"}-declarations is 
  important here, as preconditions should be generated before 
  additional proof obligations. *}
lemmas [refine0] = ASSUME_refine_right
lemmas [refine0] = ASSERT_refine_left
lemmas [refine0] = ASSUME_refine_left
lemmas [refine0] = ASSERT_refine_right

text {* For backward compatibility, as @{text "intro refine"} still
  seems to be used instead of @{text "refine_rcg"}. *}
lemmas [refine] = ASSUME_refine_right
lemmas [refine] = ASSERT_refine_left
lemmas [refine] = ASSUME_refine_left
lemmas [refine] = ASSERT_refine_right


lemma REC_refine[refine]:
  assumes M: "trimono body"
  assumes R0: "(x,x')∈R"
  assumes RS: "!!f f' x x'. [| !!x x'. (x,x')∈R ==> f x ≤\<Down>S (f' x'); (x,x')∈R; 
        REC body' = f' |] 
    ==> body f x ≤\<Down>S (body' f' x')"
  shows "REC (λf x. body f x) x ≤\<Down>S (REC (λf' x'. body' f' x') x')"
  unfolding REC_def
  apply (clarsimp simp add: M)
  apply (rule lfp_induct_pointwise[where pre="λx' x. (x,x')∈R" and B=body])

  apply rule
  apply clarsimp
  apply (blast intro: SUP_least)

  apply simp

  apply (simp add: trimonoD[OF M])

  apply (rule R0)

  apply (subst lfp_unfold, simp add: trimonoD)
  apply (rule RS)
  apply blast
  apply blast
  apply (simp add: REC_def[abs_def])
  done

lemma RECT_refine[refine]:
  assumes M: "trimono body"
  assumes R0: "(x,x')∈R"
  assumes RS: "!!f f' x x'. [| !!x x'. (x,x')∈R ==> f x ≤\<Down>S (f' x'); (x,x')∈R |] 
    ==> body f x ≤\<Down>S (body' f' x')"
  shows "RECT (λf x. body f x) x ≤\<Down>S (RECT (λf' x'. body' f' x') x')"
  unfolding RECT_def
  apply (clarsimp simp add: M)

  apply (rule flatf_fixp_transfer[where 
        fp'="flatf_gfp body" 
    and B'=body 
    and P="λx x'. (x',x)∈R", 
    OF _ _ flatf_ord.fixp_unfold[OF M[THEN trimonoD_flatf_ge]] R0])
  apply simp
  apply (simp add: trimonoD)
  by (rule RS)

lemma if_refine[refine]:
  assumes "b <-> b'"
  assumes "[|b;b'|] ==> S1 ≤ \<Down>R S1'"
  assumes "[|¬b;¬b'|] ==> S2 ≤ \<Down>R S2'"
  shows "(if b then S1 else S2) ≤ \<Down>R (if b' then S1' else S2')"
  using assms by auto

lemma Let_unfold_refine[refine]:
  assumes "f x ≤ \<Down>R (f' x')"
  shows "Let x f ≤ \<Down>R (Let x' f')"
  using assms by auto

text {* The next lemma is sometimes more convenient, as it prevents
  large let-expressions from exploding by being completely unfolded. *}
lemma Let_refine:
  assumes "(m,m')∈R'"
  assumes "!!x x'. (x,x')∈R' ==> f x ≤ \<Down>R (f' x')"
  shows "Let m (λx. f x) ≤\<Down>R (Let m' (λx'. f' x'))"
  using assms by auto

lemma case_option_refine[refine]:
  assumes "(x,x')∈Id"
  assumes "fn ≤ \<Down>R fn'"
  assumes "!!v v'. [|x=Some v; (v,v')∈Id|] ==> fs v ≤ \<Down>R (fs' v')"
  shows "case_option fn (λv. fs v) x ≤ \<Down>R (case_option fn' (λv'. fs' v') x')"
  using assms by (auto split: option.split)

text {* It is safe to split conjunctions in refinement goals.*}
declare conjI[refine]

text {* The following rules try to compensate for some structural changes,
  like inlining lets or converting binds to lets. *}
lemma remove_Let_refine[refine2]:
  assumes "M ≤ \<Down>R (f x)"
  shows "M ≤ \<Down>R (Let x f)" using assms by auto

lemma intro_Let_refine[refine2]:
  assumes "f x ≤ \<Down>R M'"
  shows "Let x f ≤ \<Down>R M'" using assms by auto
  
lemma bind2let_refine[refine2]:
  assumes "RETURN x ≤ \<Down>R' M'"
  assumes "!!x'. (x,x')∈R' ==> f x ≤ \<Down>R (f' x')"
  shows "Let x f ≤ \<Down>R (bind M' (λx'. f' x'))"
  using assms 
  apply (simp add: pw_le_iff refine_pw_simps) 
  apply fast
  done

lemma bind_Let_refine2[refine2]: "[| 
    m' ≤\<Down>R' (RETURN x);
    !!x'. [|inres m' x'; (x',x)∈R'|] ==> f' x' ≤ \<Down>R (f x) 
  |] ==> m'»=(λx'. f' x') ≤ \<Down>R (Let x (λx. f x))"
  apply (simp add: pw_le_iff refine_pw_simps)
  apply blast
  done

lemma bind2letRETURN_refine[refine2]:
  assumes "RETURN x ≤ \<Down>R' M'"
  assumes "!!x'. (x,x')∈R' ==> RETURN (f x) ≤ \<Down>R (f' x')"
  shows "RETURN (Let x f) ≤ \<Down>R (bind M' (λx'. f' x'))"
  using assms
  apply (simp add: pw_le_iff refine_pw_simps)
  apply fast
  done

lemma RETURN_as_SPEC_refine[refine2]:
  assumes "M ≤ SPEC (λc. (c,a)∈R)"
  shows "M ≤ \<Down>R (RETURN a)"
  using assms
  by (simp add: pw_le_iff refine_pw_simps)

lemma RETURN_as_SPEC_refine_old:
  "!!M R. M ≤ \<Down>R (SPEC (λx. x=v)) ==> M ≤\<Down>R (RETURN v)"
  by (simp add: RETURN_def)

lemma if_RETURN_refine [refine2]:
  assumes "b <-> b'"
  assumes "[|b;b'|] ==> RETURN S1 ≤ \<Down>R S1'"
  assumes "[|¬b;¬b'|] ==> RETURN S2 ≤ \<Down>R S2'"
  shows "RETURN (if b then S1 else S2) ≤ \<Down>R (if b' then S1' else S2')"
  (* this is nice to have for small functions, hence keep it in refine2 *)
  using assms
  by (simp add: pw_le_iff refine_pw_simps)

lemma RES_sng_as_SPEC_refine[refine2]:
  assumes "M ≤ SPEC (λc. (c,a)∈R)"
  shows "M ≤ \<Down>R (RES {a})"
  using assms
  by (simp add: pw_le_iff refine_pw_simps)


lemma intro_spec_refine_iff:
  "(bind (RES X) f ≤ \<Down>R M) <-> (∀x∈X. f x ≤ \<Down>R M)"
  apply (simp add: pw_le_iff refine_pw_simps)
  apply blast
  done

lemma intro_spec_refine[refine2]:
  assumes "!!x. x∈X ==> f x ≤ \<Down>R M"
  shows "bind (RES X) (λx. f x) ≤ \<Down>R M"
  using assms
  by (simp add: intro_spec_refine_iff)


text {* The following rules are intended for manual application, to reflect 
  some common structural changes, that, however, are not suited to be applied
  automatically. *}

text {* Replacing a let by a deterministic computation *}
lemma let2bind_refine:
  assumes "m ≤ \<Down>R' (RETURN m')"
  assumes "!!x x'. (x,x')∈R' ==> f x ≤ \<Down>R (f' x')"
  shows "bind m (λx. f x) ≤ \<Down>R (Let m' (λx'. f' x'))"
  using assms
  apply (simp add: pw_le_iff refine_pw_simps)
  apply blast
  done



text {* Introduce a new binding, without a structural match in the abstract 
  program *}
lemma intro_bind_refine:
  assumes "m ≤ \<Down>R' (RETURN m')"
  assumes "!!x. (x,m')∈R' ==> f x ≤ \<Down>R m''"
  shows "bind m (λx. f x) ≤ \<Down>R m''"
  using assms
  apply (simp add: pw_le_iff refine_pw_simps)
  apply blast
  done

lemma intro_bind_refine_id:
  assumes "m ≤ (SPEC (op = m'))"
  assumes "f m' ≤ \<Down>R m''"
  shows "bind m f ≤ \<Down>R m''"
  using assms
  apply (simp add: pw_le_iff refine_pw_simps)
  apply blast
  done

subsection {* Relators *}

definition nres_rel where 
  nres_rel_def_internal: "nres_rel R ≡ {(c,a). c ≤ \<Down>R a}"

lemma nres_rel_def: "⟨R⟩nres_rel ≡ {(c,a). c ≤ \<Down>R a}"
  by (simp add: nres_rel_def_internal relAPP_def)

lemma nres_relD: "(c,a)∈⟨R⟩nres_rel ==> c ≤\<Down>R a" by (simp add: nres_rel_def)
lemma nres_relI: "c ≤\<Down>R a ==> (c,a)∈⟨R⟩nres_rel" by (simp add: nres_rel_def)

lemma param_RES[param]:
  "(RES,RES) ∈ ⟨R⟩set_rel -> ⟨R⟩nres_rel"
  unfolding set_rel_def nres_rel_def
  by (fastforce intro: RES_refine)

lemma param_RETURN[param]: 
  "(RETURN,RETURN) ∈ R -> ⟨R⟩nres_rel"
  by (auto simp: nres_rel_def RETURN_refine)

lemma param_bind[param]:
  "(bind,bind) ∈ ⟨Ra⟩nres_rel -> (Ra->⟨Rb⟩nres_rel) -> ⟨Rb⟩nres_rel"
  by (auto simp: nres_rel_def intro: bind_refine dest: fun_relD)

subsection {* Autoref Setup *}

consts i_nres :: "interface => interface"
lemmas [autoref_rel_intf] = REL_INTFI[of nres_rel i_nres]

(*lemma id_nres[autoref_id_self]: "ID_LIST 
  (l SUCCEED FAIL bind (REC::_ => _ => _ nres,1) (RECT::_ => _ => _ nres,1))"
  by simp_all
*)
(*definition [simp]: "op_RETURN x ≡ RETURN x"
lemma [autoref_op_pat_def]: "RETURN x ≡ op_RETURN x" by simp
*)

definition [simp]: "op_nres_ASSERT_bnd Φ m ≡ do {ASSERT Φ; m}"

context begin interpretation autoref_syn .
lemma id_ASSERT[autoref_op_pat_def]:
  "do {ASSERT Φ; m} ≡ OP (op_nres_ASSERT_bnd Φ)$m"
  by simp

definition [simp]: "op_nres_ASSUME_bnd Φ m ≡ do {ASSUME Φ; m}"
lemma id_ASSUME[autoref_op_pat_def]:
  "do {ASSUME Φ; m} ≡ OP (op_nres_ASSUME_bnd Φ)$m"
  by simp

end

lemma autoref_SUCCEED[autoref_rules]: "(SUCCEED,SUCCEED) ∈ ⟨R⟩nres_rel"
  by (auto simp: nres_rel_def)

lemma autoref_FAIL[autoref_rules]: "(FAIL,FAIL) ∈ ⟨R⟩nres_rel"
  by (auto simp: nres_rel_def)

lemma autoref_RETURN[autoref_rules]: 
  "(RETURN,RETURN) ∈ R -> ⟨R⟩nres_rel"
  by (auto simp: nres_rel_def RETURN_refine)

lemma autoref_bind[autoref_rules]: 
  "(bind,bind) ∈ ⟨R1⟩nres_rel -> (R1->⟨R2⟩nres_rel) -> ⟨R2⟩nres_rel"
  apply (intro fun_relI)
  apply (rule nres_relI)
  apply (rule bind_refine)
  apply (erule nres_relD)
  apply (erule (1) fun_relD[THEN nres_relD])
  done
 
context begin interpretation autoref_syn .
lemma autoref_ASSERT[autoref_rules]:
  assumes "Φ ==> (m',m)∈⟨R⟩nres_rel"
  shows "(
    m',
    (OP (op_nres_ASSERT_bnd Φ) ::: ⟨R⟩nres_rel -> ⟨R⟩nres_rel) $ m)∈⟨R⟩nres_rel"
  using assms unfolding nres_rel_def
  by (simp add: ASSERT_refine_right)

lemma autoref_ASSUME[autoref_rules]:
  assumes "SIDE_PRECOND Φ"
  assumes "Φ ==> (m',m)∈⟨R⟩nres_rel"
  shows "(
    m',
    (OP (op_nres_ASSUME_bnd Φ) ::: ⟨R⟩nres_rel -> ⟨R⟩nres_rel) $ m)∈⟨R⟩nres_rel"
  using assms unfolding nres_rel_def
  by (simp add: ASSUME_refine_right)

lemma autoref_REC[autoref_rules]:
  assumes "(B,B')∈(Ra->⟨Rr⟩nres_rel) -> Ra -> ⟨Rr⟩nres_rel"
  assumes "DEFER trimono B"
  shows "(REC B,
    (OP REC 
      ::: ((Ra->⟨Rr⟩nres_rel) -> Ra -> ⟨Rr⟩nres_rel) -> Ra -> ⟨Rr⟩nres_rel)$B'
    ) ∈ Ra -> ⟨Rr⟩nres_rel"
  apply (intro fun_relI)
  using assms
  apply (auto simp: nres_rel_def intro!: REC_refine)
  apply (simp add: fun_rel_def)
  apply blast
  done

lemma autoref_RECT[autoref_rules]:
  assumes "(B,B') ∈ (Ra->⟨Rr⟩nres_rel) -> Ra->⟨Rr⟩nres_rel"
  assumes "DEFER trimono B"
  shows "(RECT B,
    (OP RECT 
      ::: ((Ra->⟨Rr⟩nres_rel) -> Ra -> ⟨Rr⟩nres_rel) -> Ra -> ⟨Rr⟩nres_rel)$B'
   ) ∈ Ra -> ⟨Rr⟩nres_rel"
  apply (intro fun_relI)
  using assms
  apply (auto simp: nres_rel_def intro!: RECT_refine)
  apply (simp add: fun_rel_def)
  apply blast
  done
end

subsection {* Convenience Rules*}
text {*
  In this section, we define some lemmas that simplify common prover tasks.
*}

lemma ref_two_step: "A≤\<Down>R  B ==> B≤C ==> A≤\<Down>R  C" 
  by (rule conc_trans_additional)

   
lemma pw_ref_iff:
  shows "S ≤ \<Down>R S' 
  <-> (nofail S' 
    --> nofail S ∧ (∀x. inres S x --> (∃s'. (x, s') ∈ R ∧ inres S' s')))"
  by (simp add: pw_le_iff refine_pw_simps)

lemma pw_ref_I:
  assumes "nofail S' 
    --> nofail S ∧ (∀x. inres S x --> (∃s'. (x, s') ∈ R ∧ inres S' s'))"
  shows "S ≤ \<Down>R S'"
  using assms
  by (simp add: pw_ref_iff)

text {* Introduce an abstraction relation. Usage: 
  @{text "rule introR[where R=absRel]"}
*}
lemma introR: "(a,a')∈R ==> (a,a')∈R" .

lemma intro_prgR: "c ≤ \<Down>R a ==> c ≤ \<Down>R a" by auto


lemma le_ASSERTI_pres:
  assumes "Φ ==> S ≤ do {ASSERT Φ; S'}"
  shows "S ≤ do {ASSERT Φ; S'}"
  using assms by (auto intro: le_ASSERTI)

lemma RETURN_ref_SPECD:
  assumes "RETURN c ≤ \<Down>R (SPEC Φ)"
  obtains a where "(c,a)∈R" "Φ a"
  using assms
  by (auto simp: pw_le_iff refine_pw_simps)

lemma RETURN_ref_RETURND:
  assumes "RETURN c ≤ \<Down>R (RETURN a)"
  shows "(c,a)∈R"
  using assms
  apply (auto simp: pw_le_iff refine_pw_simps)
  done

lemma SPEC_eq_is_RETURN:
  "SPEC (op = x) = RETURN x"
  "SPEC (λx. x=y) = RETURN y"
  by (auto simp: RETURN_def)

lemma RETURN_SPEC_conv: "RETURN r = SPEC (λx. x=r)"
  by (simp add: RETURN_def)

lemma build_rel_SPEC: 
  "M ≤ SPEC ( λx. Φ (α x) ∧ I x) ==> M ≤ \<Down>(build_rel α I) (SPEC Φ)"
  by (auto simp: pw_le_iff refine_pw_simps build_rel_def)

lemma refine_IdD: "c ≤ \<Down>Id a ==> c ≤ a" by simp

lemma le_RES_nofailI:
  assumes "a≤RES x"
  shows "nofail a"
  using assms
  by (metis nofail_simps(2) pwD1)

lemma add_invar_refineI:
  assumes "f x ≤\<Down>R (f' x')"
    and "nofail (f x) ==> f x ≤ SPEC I"
  shows "f x ≤ \<Down> {(c, a). (c, a) ∈ R ∧ I c} (f' x')"
  using assms
  by (simp add: pw_le_iff refine_pw_simps sv_add_invar)


lemma bind_RES_RETURN_eq: "bind (RES X) (λx. RETURN (f x)) = 
  RES { f x | x. x∈X }"
  by (simp add: pw_eq_iff refine_pw_simps)
    blast

lemma bind_RES_RETURN2_eq: "bind (RES X) (λ(x,y). RETURN (f x y)) = 
  RES { f x y | x y. (x,y)∈X }"
  apply (simp add: pw_eq_iff refine_pw_simps)
  apply blast
  done

lemma le_SPEC_bindI: 
  assumes "Φ x"
  assumes "m ≤ f x"
  shows "m ≤ SPEC Φ »= f"
  using assms by (auto simp add: pw_le_iff refine_pw_simps)

lemma bind_assert_refine: 
  assumes "m1 ≤ SPEC Φ"
  assumes "!!x. Φ x ==> m2 x ≤ m'"
  shows "do {x\<leftarrow>m1; ASSERT (Φ x); m2 x} ≤ m'"
  using assms
  by (simp add: pw_le_iff refine_pw_simps) blast


text {* The following rules are useful for massaging programs before the 
  refinement takes place *}
lemma let_to_bind_conv: 
  "Let x f = RETURN x»=f"
  by simp

lemmas bind_to_let_conv = let_to_bind_conv[symmetric]

lemma pull_out_let_conv: "RETURN (Let x f) = Let x (λx. RETURN (f x))"
  by simp

lemma push_in_let_conv: 
  "Let x (λx. RETURN (f x)) = RETURN (Let x f)"
  "Let x (RETURN o f) = RETURN (Let x f)"
  by simp_all

lemma pull_out_RETURN_case_option: 
  "case_option (RETURN a) (λv. RETURN (f v)) x = RETURN (case_option a f x)"
  by (auto split: option.splits)

lemma if_bind_cond_refine: 
  assumes "ci ≤ RETURN b"
  assumes "b ==> ti≤\<Down>R t"
  assumes "¬b ==> ei≤\<Down>R e"
  shows "do {b\<leftarrow>ci; if b then ti else ei} ≤ \<Down>R (if b then t else e)"
  using assms
  by (auto simp add: refine_pw_simps pw_le_iff)

lemma intro_RETURN_Let_refine:
  assumes "RETURN (f x) ≤ \<Down>R M'"
  shows "RETURN (Let x f) ≤ \<Down>R M'" 
  (* this should be needed very rarely - so don't add it *)
  using assms by auto

(* Obsolete, use RECT_eq_REC_tproof instead!
text {* 
  This rule establishes equality between RECT and REC, using an additional 
  invariant. Note that the default way is to prove termination in
  first place, instead of re-proving parts of the invariant just for the
  termination proof. *}
lemma RECT_eq_REC':
  assumes WF: "wf V'"
  assumes I0: "I x"
  assumes IS: "!!f x. I x ==> (!!x. f x ≤ REC body x) ==>
    body (λx'. if (I x' ∧ (x',x)∈V') then f x' else top) x ≤ body f x"
  shows "RECT body x = REC body x"
proof (cases "mono body")
  assume "¬mono body"
  thus ?thesis unfolding REC_def RECT_def by simp
next
  assume MONO: "mono body"

  from lfp_le_gfp' MONO have "lfp body x ≤ gfp body x" .
  moreover have "I x --> gfp body x ≤ lfp body x"
    using WF
    apply (induct rule: wf_induct[consumes 1])
    apply (rule impI)
    apply (subst lfp_unfold[OF MONO])
    apply (subst gfp_unfold[OF MONO])
    apply (rule order_trans[OF _ IS])
    apply (rule monoD[OF MONO,THEN le_funD])
    apply (rule le_funI)
    apply simp
    apply simp
    apply (simp add: REC_def)
    done
  ultimately show ?thesis
    unfolding REC_def RECT_def
    apply (rule_tac antisym)
    using I0 MONO by auto
qed
*)

subsubsection {* Boolean Operations on Specifications *}
lemma SPEC_iff:
  assumes "P ≤ SPEC (λs. Q s --> R s)"
  and "P ≤ SPEC (λs. ¬ Q s --> ¬ R s)"
  shows "P ≤ SPEC (λs. Q s <-> R s)"
  using assms[THEN pw_le_iff[THEN iffD1]]
  by (auto intro!: pw_leI)

lemma SPEC_rule_conjI:
  assumes "A ≤ SPEC P" and "A ≤ SPEC Q"
    shows "A ≤ SPEC (λv. P v ∧ Q v)"
proof -
  have "A ≤ inf (SPEC P) (SPEC Q)" using assms by (rule_tac inf_greatest) assumption
  thus ?thesis by (auto simp add:Collect_conj_eq)
qed

lemma SPEC_rule_conjunct1:
  assumes "A ≤ SPEC (λv. P v ∧ Q v)"
    shows "A ≤ SPEC P"
proof -
  note assms
  also have "… ≤ SPEC P" by (rule SPEC_rule) auto
  finally show ?thesis .
qed

lemma SPEC_rule_conjunct2:
  assumes "A ≤ SPEC (λv. P v ∧ Q v)"
    shows "A ≤ SPEC Q"
proof -
  note assms
  also have "… ≤ SPEC Q" by (rule SPEC_rule) auto
  finally show ?thesis .
qed


subsubsection {* Pointwise Reasoning *}
lemma inres_if:
  "[| inres (if P then Q else R) x; [|P; inres Q x|] ==> S; [|¬ P; inres R x|] ==> S |] ==> S"
by (metis (full_types))

lemma inres_SPEC:
  "inres M x ==> M ≤ SPEC Φ ==> Φ x"
by (auto dest: pwD2)

lemma SPEC_nofail:
  "X ≤ SPEC Φ ==> nofail X"
by (auto dest: pwD1)


end