theory WB_More_Refinement
  imports Weidenbach_Book_Base.WB_List_More
    "HOL-Library.Cardinality"
    "HOL-Eisbach.Eisbach"
    "HOL-Library.Rewrite"
    "Isabelle_LLVM.Refine_Monadic_Thin"
    \<comment> \<open>don't import \<open>Refine_Monadic.Monadic\<close>, that imports too much.\<close>
    Isabelle_LLVM.Sepref_Rules
    Isabelle_LLVM.Sepref_Misc
    More_Refinement_Libs.WB_More_Refinement_Loops
    WB_More_Refinement_List
begin

no_notation funcset (infixr "\<rightarrow>" 60)

chapter \<open>More Theorems on Refinement\<close>

text \<open>

We first attempted to keep both the LLVM and the SML version
working. Due to where things are defined, some constants have to be
duplicated (including simple ones like swap or critical ones like
fref -- although none of them should be in IICF).

We finally give up (at least temporary) when we realised that the LLVM
version is twice as fast as the SML version while providing better
tooling than the SML version. However, some things remains in the
theories from this attempt like fref defined below.

Additionaly some of the things developed here moved to Isabelle\_LLVM.
\<close>

hide_const Autoref_Fix_Rel.CONSTRAINT

section \<open>Some Tooling for Refinement\<close>

text \<open>
  The following very simple tactics remove duplicate variables generated by some tactic like
  \<open>refine_rcg\<close>. For example, if the problem contains \<^term>\<open>(i, C) = (xa, xb)\<close>, then only
  \<^term>\<open>i\<close> and \<^term>\<open>C\<close> will remain. It can also prove trivial goals where the goals already
  appears in the assumptions.
  \<close>

method remove_dummy_vars uses simps =
  ((unfold prod.inject)?; (simp only: prod.inject)?; (elim conjE)?;
    hypsubst?; (simp only: triv_forall_equality simps)?)


subsection \<open>From @{text \<rightarrow>} to @{text \<Down>}\<close>


ML \<open>
signature MORE_REFINEMENT = sig
  val down_converse: Proof.context -> thm -> thm
end

structure More_Refinement: MORE_REFINEMENT = struct
  val unfold_refine = (fn context => Local_Defs.unfold (context)
   @{thms refine_rel_defs nres_rel_def in_pair_collect_simp})
  val unfold_Ball = (fn context => Local_Defs.unfold (context)
    @{thms Ball2_split_def all_to_meta})
  val replace_ALL_by_meta = (fn context => fn thm => Object_Logic.rulify context thm)
  val down_converse = (fn context =>
    replace_ALL_by_meta context o (unfold_Ball context) o (unfold_refine context))
end
\<close>

attribute_setup "to_\<Down>" = \<open>
    Scan.succeed (Thm.rule_attribute [] (More_Refinement.down_converse o Context.proof_of))
  \<close> "convert theorem from @{text \<rightarrow>}-form to @{text \<Down>}-form."

method "to_\<Down>" =
   (unfold refine_rel_defs nres_rel_def in_pair_collect_simp;
   unfold Ball2_split_def all_to_meta;
   intro allI impI)

lemma fref_syn_invert:
  \<open>a = a' \<Longrightarrow> b \<subseteq> b' \<Longrightarrow> a \<rightarrow>\<^sub>f b \<subseteq> a' \<rightarrow>\<^sub>f b'\<close>
  unfolding fref_param1[symmetric]
  by (rule fun_rel_syn_invert)

method match_spec =
  (match conclusion in \<open>(f, g) \<in> R\<close> for f g R \<Rightarrow>
    \<open>print_term f; match premises in I[thin]: \<open>(f, g) \<in> R'\<close> for R'
       \<Rightarrow> \<open>print_term R'; rule mem_set_trans[OF _ I]\<close>\<close>)

method match_fun_rel =
  ((match conclusion in
       \<open>fun_rel_syn _  _ \<subseteq> _ \<rightarrow> _\<close> \<Rightarrow> \<open>rule fun_rel_mono\<close>
     \<bar> \<open>_ \<rightarrow>\<^sub>f _ \<subseteq> _ \<rightarrow>\<^sub>f _\<close> \<Rightarrow> \<open>rule fref_syn_invert\<close>
     \<bar> \<open>\<langle>_\<rangle>nres_rel \<subseteq> \<langle>_\<rangle>nres_rel\<close> \<Rightarrow> \<open>rule nres_rel_mono\<close>
     \<bar> \<open>[_]\<^sub>f _ \<rightarrow> _ \<subseteq> [_]\<^sub>f _ \<rightarrow> _\<close> \<Rightarrow> \<open>rule fref_mono\<close>
   )+)


section \<open>More Theorems for Refinement\<close>


lemma fref_weaken_pre_weaken:
  assumes "\<And>x. P x \<longrightarrow> P' x"
  assumes "(f,h) \<in> frefnd P' R S"
  assumes \<open>S \<subseteq> S'\<close>
  shows "(f,h) \<in> frefnd P R S'"
  using assms unfolding fref_def by blast

lemma fref_to_Down:
  \<open>(f, g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x'. P x' \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> f x \<le> \<Down> B (g x'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry_left:
  fixes f:: \<open>'a \<Rightarrow> 'b \<Rightarrow> 'c nres\<close> and
    A::\<open>(('a \<times> 'b) \<times> 'd) set\<close>
  shows
    \<open>(uncurry f, g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
      (\<And>a b x'. P x' \<Longrightarrow> ((a, b), x') \<in> A \<Longrightarrow> f a b \<le> \<Down> B (g x'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry:
  \<open>(uncurry f, uncurry g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> f x y \<le> \<Down> B (g x' y'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_Id_keep:
  assumes \<open>(f, RETURN o g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>Id\<rangle>nres_rel\<close>
  shows \<open>(\<And>x x'. P x' \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> f x \<le> \<Down> {(x, y). x = y \<and> x = g x'} (RETURN (g x')))\<close>
  using assms
  unfolding fref_def uncurry_def nres_rel_def RETURN_def conc_fun_RES
  by auto

text \<open>This has been moved to theory \<open>Isabelle_LLVM.Sepref_Misc\<close>. However, we cannot import it here
  due to \<open>Refine_Imperative_HOL.Sepref_Misc\<close>. Therefore, we mark the abbreviations as input only
  and hope for the best.\<close>


lemma fref_to_Down_curry2:
  \<open>(uncurry2 f, uncurry2 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z'. P ((x', y'), z') \<Longrightarrow> (((x, y), z), ((x', y'), z')) \<in> A\<Longrightarrow>
         f x y z \<le> \<Down> B (g x' y' z'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry2':
  \<open>(uncurry2 f, uncurry2 g) \<in> A \<rightarrow>\<^sub>f \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z'. (((x, y), z), ((x', y'), z')) \<in> A \<Longrightarrow>
         f x y z \<le> \<Down> B (g x' y' z'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry3:
  \<open>(uncurry3 f, uncurry3 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z' a a'. P (((x', y'), z'), a') \<Longrightarrow>
        ((((x, y), z), a), (((x', y'), z'), a')) \<in> A \<Longrightarrow>
         f x y z a \<le> \<Down> B (g x' y' z' a'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry4:
  \<open>(uncurry4 f, uncurry4 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z' a a' b b'. P ((((x', y'), z'), a'), b') \<Longrightarrow>
        (((((x, y), z), a), b), ((((x', y'), z'), a'), b')) \<in> A \<Longrightarrow>
         f x y z a b \<le> \<Down> B (g x' y' z' a' b'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry5:
  \<open>(uncurry5 f, uncurry5 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z' a a' b b' c c'. P (((((x', y'), z'), a'), b'), c') \<Longrightarrow>
        ((((((x, y), z), a), b), c), (((((x', y'), z'), a'), b'), c')) \<in> A \<Longrightarrow>
         f x y z a b c \<le> \<Down> B (g x' y' z' a' b' c'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry6:
  \<open>(uncurry6 f, uncurry6 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z' a a' b b' c c' d d'. P ((((((x', y'), z'), a'), b'), c'), d') \<Longrightarrow>
        (((((((x, y), z), a), b), c), d), ((((((x', y'), z'), a'), b'), c'), d')) \<in> A \<Longrightarrow>
         f x y z a b c d \<le> \<Down> B (g x' y' z' a' b' c' d'))\<close>
  unfolding fref_def uncurry_def nres_rel_def by auto

lemma fref_to_Down_curry7:
  \<open>(uncurry7 f, uncurry7 g) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y' z z' a a' b b' c c' d d' e e'. P (((((((x', y'), z'), a'), b'), c'), d'), e') \<Longrightarrow>
       ((((((((x, y), z), a), b), c), d), e), (((((((x', y'), z'), a'), b'), c'), d'), e')) \<in> A \<Longrightarrow>
         f x y z a b c d e \<le> \<Down> B (g x' y' z' a' b' c' d' e'))\<close>
  unfolding fref_def uncurry_def nres_rel_def by auto

lemma fref_to_Down_explode:
  \<open>(f a, g a) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' b. P x' \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> b = a \<Longrightarrow> f a x \<le> \<Down> B (g b x'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_unRET_uncurry2:
  fixes f :: \<open>'a \<Rightarrow> 'b \<Rightarrow> 'c \<Rightarrow> 'f\<close>
    and g :: \<open>'a2 \<Rightarrow> 'b2 \<Rightarrow> 'c2 \<Rightarrow> 'g\<close>
  shows
    \<open>(uncurry2 (RETURN ooo f), uncurry2 (RETURN ooo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
       (\<And>(x :: 'a) x' y y' (z :: 'c) (z' :: 'c2).
         P ((x', y'), z') \<Longrightarrow> (((x, y), z), ((x', y'), z')) \<in> A \<Longrightarrow>
         (f x y z, g x' y' z') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_unRET_uncurry3:
  shows
    \<open>(uncurry3 (RETURN oooo f), uncurry3 (RETURN oooo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
       (\<And>(x :: 'a) x' y y' (z :: 'c) (z' :: 'c2) a a'.
         P (((x', y'), z'), a') \<Longrightarrow> ((((x, y), z), a), (((x', y'), z'), a')) \<in> A \<Longrightarrow>
         (f x y z a, g x' y' z' a') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_unRET_uncurry4:
  shows
    \<open>(uncurry4 (RETURN ooooo f), uncurry4 (RETURN ooooo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
       (\<And>(x :: 'a) x' y y' (z :: 'c) (z' :: 'c2) a a' b b'.
         P ((((x', y'), z'), a'), b') \<Longrightarrow> (((((x, y), z), a), b), ((((x', y'), z'), a'), b')) \<in> A \<Longrightarrow>
         (f x y z a b, g x' y' z' a' b') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto


lemma fref_to_Down_curry_no_nres_Id:
  \<open>(uncurry (RETURN oo f), uncurry (RETURN oo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>Id\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> f x y = g x' y')\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_no_nres:
  \<open>((RETURN o f), (RETURN o g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x'. P (x') \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> (f x, g x') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_curry_no_nres:
  \<open>(uncurry (RETURN oo f), uncurry (RETURN oo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> (f x y, g x' y') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma RES_RETURN_RES4:
   \<open>SPEC \<Phi> \<bind> (\<lambda>(T, T', T'', T'''). RETURN (f T T' T'' T''')) =
      RES ((\<lambda>(a, b, c, d). f a b c d) ` {T. \<Phi> T})\<close>
  using RES_RETURN_RES[of \<open>Collect \<Phi>\<close> \<open>\<lambda>(a, b, c, d). f a b c d\<close>]
  apply (subst (asm)(2) split_prod_bound)
  apply (subst (asm)(3) split_prod_bound)
  apply (subst (asm)(4) split_prod_bound)
  by auto

declare RETURN_as_SPEC_refine[refine2 del]


lemma fref_to_Down_unRET_uncurry_Id:
  \<open>(uncurry (RETURN oo f), uncurry (RETURN oo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>Id\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> f x y = (g x' y'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto
lemma fref_to_Down_unRET_uncurry:
  \<open>(uncurry (RETURN oo f), uncurry (RETURN oo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> (f x y, g x' y') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_unRET_Id:
  \<open>((RETURN o f), (RETURN o g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>Id\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x'. P x' \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> f x = (g x'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma fref_to_Down_unRET:
  \<open>((RETURN o f), (RETURN o g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>B\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x'. P x' \<Longrightarrow> (x, x') \<in> A \<Longrightarrow> (f x, g x') \<in> B)\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto


thm in_pair_collect_simp


section \<open>More declarations\<close>

(* TODO: only input notation? *)
notation prod_rel_syn (infixl "\<times>\<^sub>f" 70)


section \<open>More Functions, Relations, and Theorems\<close>

definition emptied_list :: \<open>'a list \<Rightarrow> 'a list\<close> where
  \<open>emptied_list l = []\<close>



subsection \<open>Ghost parameters\<close>

text \<open>
  This is a trick to recover from consumption of a variable (\<^term>\<open>\<A>\<^sub>i\<^sub>n\<close>) that is passed as
  argument and destroyed by the initialisation: We copy it as a zero-cost
  (by creating a \<^term>\<open>()\<close>), because we don't need it in the code and only in the specification.

  This is a way to have ghost parameters, without having them: The parameter is replaced by \<^term>\<open>()\<close>
  and we hope that the compiler will do the right thing.
\<close>
definition virtual_copy where
  [simp]: \<open>virtual_copy = id\<close>

definition virtual_copy_rel where
  \<open>virtual_copy_rel = {(c, b). c = ()}\<close>

lemma refine_add_inv:
  fixes f :: \<open>'a \<Rightarrow> 'a nres\<close> and f' :: \<open>'b \<Rightarrow> 'b nres\<close> and h :: \<open>'b \<Rightarrow> 'a\<close>
  assumes
    \<open>(f', f) \<in> {(S, S'). S' = h S \<and> R S} \<rightarrow> \<langle>{(T, T'). T' = h T \<and> P' T}\<rangle> nres_rel\<close>
    (is \<open>_ \<in> ?R \<rightarrow> \<langle>{(T, T'). ?H T T' \<and> P' T}\<rangle> nres_rel\<close>)
  assumes
    \<open>\<And>S. R S \<Longrightarrow> f (h S) \<le> SPEC (\<lambda>T. Q T)\<close>
  shows
    \<open>(f', f) \<in> ?R \<rightarrow> \<langle>{(T, T'). ?H T T' \<and> P' T \<and> Q (h T)}\<rangle> nres_rel\<close>
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by fastforce

lemma refine_add_inv_generalised:
  fixes f :: \<open>'a \<Rightarrow> 'b nres\<close> and f' :: \<open>'c \<Rightarrow> 'd nres\<close>
  assumes
    \<open>(f', f) \<in> A \<rightarrow>\<^sub>f \<langle>B\<rangle> nres_rel\<close>
  assumes
    \<open>\<And>S S'. (S, S') \<in> A \<Longrightarrow> f S' \<le> RES C\<close>
  shows
    \<open>(f', f) \<in> A \<rightarrow>\<^sub>f \<langle>{(T, T'). (T, T') \<in> B \<and> T' \<in> C}\<rangle> nres_rel\<close>
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
   fref_param1[symmetric]
  by fastforce

lemma refine_add_inv_pair:
  fixes f :: \<open>'a \<Rightarrow> ('c \<times> 'a) nres\<close> and f' :: \<open>'b \<Rightarrow> ('c \<times> 'b) nres\<close> and h :: \<open>'b \<Rightarrow> 'a\<close>
  assumes
    \<open>(f', f) \<in> {(S, S'). S' = h S \<and> R S} \<rightarrow> \<langle>{(S, S'). (fst S' = h' (fst S) \<and>
    snd S' = h (snd S)) \<and> P' S}\<rangle> nres_rel\<close>  (is \<open>_ \<in> ?R \<rightarrow> \<langle>{(S, S'). ?H S S' \<and> P' S}\<rangle> nres_rel\<close>)
  assumes
    \<open>\<And>S. R S \<Longrightarrow> f (h S) \<le> SPEC (\<lambda>T. Q (snd T))\<close>
  shows
    \<open>(f', f) \<in> ?R \<rightarrow> \<langle>{(S, S'). ?H S S' \<and> P' S \<and> Q (h (snd S))}\<rangle> nres_rel\<close>
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by fastforce

lemma refine_itself0:
    \<open>(X, X) \<in> Id \<rightarrow>\<^sub>f \<langle>Id\<rangle>nres_rel\<close> and
  refine_itself1:
    \<open>(uncurry X1, uncurry X1) \<in> Id \<times>\<^sub>f Id \<rightarrow>\<^sub>f \<langle>Id\<rangle>nres_rel\<close> and
  refine_itself2:
    \<open>(uncurry2 X2, uncurry2 X2) \<in> Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<rightarrow>\<^sub>f \<langle>Id\<rangle>nres_rel\<close> and
  refine_itself3:
    \<open>(uncurry3 X3, uncurry3 X3) \<in> Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<rightarrow>\<^sub>f \<langle>Id\<rangle>nres_rel\<close>and
  refine_itself4:
    \<open>(uncurry4 X4, uncurry4 X4) \<in> Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<times>\<^sub>f Id \<rightarrow>\<^sub>f \<langle>Id\<rangle>nres_rel\<close>
  by (intro frefI nres_relI; auto; fail)+

lemma fref_to_Down_unRET_Id_uncurry:
  \<open>(uncurry (RETURN oo f), uncurry (RETURN oo g)) \<in> [P]\<^sub>f A \<rightarrow> \<langle>Id\<rangle>nres_rel \<Longrightarrow>
     (\<And>x x' y y'. P (x', y') \<Longrightarrow> ((x, y), (x', y')) \<in> A \<Longrightarrow> f x y = (g x' y'))\<close>
  unfolding fref_def uncurry_def nres_rel_def
  by auto

lemma (in -) nofail_ASSERT_bind: \<open>nofail (do {ASSERT(P); (\<Phi> :: 'a nres)}) \<longleftrightarrow> P \<and> nofail \<Phi>\<close>
  by (auto simp: nofail_def ASSERT_eq iASSERT_def)


lemma RES_RES11_RETURN_RES:
   \<open>RES A \<bind> (\<lambda>(a, b, c, d, e, g, h, i, j, k, l). RES (f a b c d e g h i j k l)) =
   RES (\<Union>((\<lambda>(a, b, c, d, e, g, h, i, j, k, l). f a b c d e g h i j k l) ` A))\<close>
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def Bex_def
    split: prod.splits)

lemma RES_RES13_RETURN_RES_bound:
   \<open>RES A \<bind> (\<lambda>(a, b, c, d, e, g, h, i, j, k, l, m, n). RES (f a b c d e g h i j k l m n)) =
   RES (\<Union>((\<lambda>(a, b, c, d, e, g, h, i, j, k, l, m, n). f a b c d e g h i j k l m n) ` A))\<close>
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def Bex_def
    split: prod.splits)

lemma ref_two_step'': \<open>R \<subseteq> R' \<Longrightarrow> A \<le> B \<Longrightarrow> \<Down> R A \<le>  \<Down> R' B\<close>
  by (simp add: "weaken_\<Down>" ref_two_step')

lemma RES_RETURN_RES3':
  \<open>RES \<Phi> \<bind> (\<lambda>(T, T', T''). RETURN (f T T' T'')) = RES ((\<lambda>(a, b, c). f a b c) ` \<Phi>)\<close>
  apply (subst RES_SPEC_conv)
  apply (subst RES_RETURN_RES3)
  by auto

lemma RETURN_le_RES_no_return3:
  \<open>f \<le> SPEC (\<lambda>(S,T,U). g S T U \<in> \<Phi>) \<Longrightarrow> do {(S, T, U) \<leftarrow> f; RETURN (g S T U)} \<le> RES \<Phi>\<close>
  by (cases f)
   (auto simp: RES_RETURN_RES3')

lemma RES_RETURN_RES4':
  \<open>RES \<Phi> \<bind> (\<lambda>(T, T', T'', T'''). RETURN (f T T' T'' T''')) = RES ((\<lambda>(a, b, c, d). f a b c d) ` \<Phi>)\<close>
  apply (subst RES_SPEC_conv)
  apply (subst RES_RETURN_RES4)
  by auto

lemma RETURN_le_RES_no_return4:
  \<open>f \<le> SPEC (\<lambda>(S,T,U,V). g S T U V \<in> \<Phi>) \<Longrightarrow> do {(S, T, U, V) \<leftarrow> f; RETURN (g S T U V)} \<le> RES \<Phi>\<close>
  by (cases f)
    (auto simp: RES_RETURN_RES4')

lemma RES_RETURN_RES5':
  \<open>RES \<Phi> \<bind> (\<lambda>(T, T', T'', T''', T''''). RETURN (f T T' T'' T''' T'''')) =
    RES ((\<lambda>(a, b, c, d, e). f a b c d e) ` \<Phi>)\<close>
  apply (subst RES_SPEC_conv)
  apply (subst RES_RETURN_RES5)
  by auto

lemma RETURN_le_RES_no_return5:
  \<open>f \<le> SPEC (\<lambda>(S,T,U,V, W). g S T U V W \<in> \<Phi>) \<Longrightarrow> do {(S, T, U, V, W) \<leftarrow> f; RETURN (g S T U V W)} \<le> RES \<Phi>\<close>
  by (cases f)
    (auto simp: RES_RETURN_RES5')

end
