Theory More_Refinement_Libs.WB_More_Refinement_Loops

theory WB_More_Refinement_Loops
imports
  Weidenbach_Book_Base.WB_List_More
  "HOL-Library.Cardinality"
  "HOL-Eisbach.Eisbach"
  "HOL-Library.Rewrite"
  "Refine_Monadic.Refine_While"
  "Refine_Monadic.Refine_Foreach"
begin

lemma SPEC_add_information: P  A  SPEC Q  A  SPEC(λx. Q x  P)
  by auto

lemma bind_refine_spec: (x. Φ x  f x   R M)  M'  SPEC Φ  M'  f   R M
  by (auto simp add: pw_le_iff refine_pw_simps)

lemma intro_spec_iff:
  (RES X  f  M) = (xX. f x  M)
  using intro_spec_refine_iff[of X f Id M] by auto

lemma case_prod_bind:
  assumes x1 x2. x = (x1, x2)  f x1 x2   R I
  shows (case x of (x1, x2)  f x1 x2)   R I
  using assms by (cases x) auto

lemma (in transfer) transfer_bool[refine_transfer]:
  assumes "α fa  Fa"
  assumes "α fb  Fb"
  shows "α (case_bool fa fb x)  case_bool Fa Fb x"
  using assms by (auto split: bool.split)

lemma ref_two_step': A  B   R A    R B
  by (auto intro: ref_two_step)

lemma RES_RETURN_RES: RES Φ  (λT. RETURN (f T)) = RES (f ` Φ)
  by (simp add: bind_RES_RETURN_eq setcompr_eq_image)

lemma RES_RES_RETURN_RES: RES A  (λT. RES (f T)) = RES ((f ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps)

lemma RES_RES2_RETURN_RES: RES A  (λ(T, T'). RES (f T T')) = RES ((uncurry f ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def)

lemma RES_RES3_RETURN_RES:
   RES A  (λ(T, T', T''). RES (f T T' T'')) = RES (((λ(a, b, c). f a b c) ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def)

(* taken from IICF*)
lemma meta_same_imp_rule: "(PROP P; PROP P  PROP Q)  (PROP P  PROP Q)"
  by rule
lemma split_prod_bound: "(λp. f p) = (λ(a,b). f (a,b))" by auto

lemma RES_RETURN_RES3:
   SPEC Φ  (λ(T, T', T''). RETURN (f T T' T'')) = RES ((λ(a, b, c). f a b c) ` {T. Φ T})
  using RES_RETURN_RES[of Collect Φ λ(a, b, c). f a b c]
  apply (subst (asm)(2) split_prod_bound)
  apply (subst (asm)(3) split_prod_bound)
  by auto

lemma RES_RES_RETURN_RES2: RES A  (λ(T, T'). RETURN (f T T')) = RES (uncurry f ` A)
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def)

lemma bind_refine_res: (x. x  Φ  f x   R M)  M'  RES Φ  M'  f   R M
  by (auto simp add: pw_le_iff refine_pw_simps)

lemma RES_RETURN_RES_RES2:
   RES Φ  (λ(T, T'). RETURN (f T T')) = RES (uncurry f ` Φ)
  using RES_RES2_RETURN_RES[of Φ λT T'. {f T T'}]
  apply (subst (asm)(2) split_prod_bound)
  by (auto simp: RETURN_def uncurry_def)

text 
  This theorem adds the invariant at the beginning of next iteration to the current invariant,
  i.e., the invariant is added as a post-condition on the current iteration.

  This is useful to reduce duplication in theorems while refining.


lemma RECT_WHILEI_body_add_post_condition:
    RECT (WHILEI_body (⤜) RETURN I' b' f) x' =
     (RECT (WHILEI_body (⤜) RETURN (λx'. I' x'  (b' x'  f x' = FAIL  f x'  SPEC I')) b' f) x')
  (is RECT ?f x' = RECT ?f' x')
proof -
  have le: flatf_gfp ?f x'  flatf_gfp ?f' x' for x'
  proof (induct arbitrary: x' rule: flatf_ord.fixp_induct[where b = top and
        f = ?f'])
    case 1
    then show ?case
      unfolding fun_lub_def pw_le_iff
      by (rule ccpo.admissibleI)
        (smt chain_fun flat_lub_in_chain mem_Collect_eq nofail_simps(1))
  next
    case 2
    then show ?case by (auto simp: WHILEI_mono_ge)
  next
    case 3
    then show ?case by simp
  next
    case (4 x)
    have  (RES X  f  M) = (xX. f x  M) for x f M X
      using intro_spec_refine_iff[of _ _ Id] by auto
    thm bind_refine_RES(2)[of _ Id, simplified]
    have [simp]: flatf_mono FAIL (WHILEI_body (⤜) RETURN I' b' f)
      by (simp add: WHILEI_mono_ge)

    have flatf_gfp ?f x' = ?f (?f (flatf_gfp ?f)) x'
      apply (subst flatf_ord.fixp_unfold)
       apply (solves simp)
      apply (subst flatf_ord.fixp_unfold)
       apply (solves simp)
      ..
    also have  = WHILEI_body (⤜)
        RETURN (λx'. I' x'  (b' x'  f x' = FAIL  f x'  SPEC I')) b' f
        (WHILEI_body (⤜) RETURN I' b' f (flatf_gfp (WHILEI_body (⤜) RETURN I' b' f))) x'
      apply (subst (1) WHILEI_body_def, subst (1) WHILEI_body_def)
      apply (subst (2) WHILEI_body_def, subst (2) WHILEI_body_def)
      apply simp_all
      apply (cases f x')
       apply (auto simp: RES_RETURN_RES nofail_def[symmetric] pw_RES_bind_choose
          split: if_splits)
      done
    also have  =  WHILEI_body (⤜)
      RETURN (λx'. I' x'  (b' x'  f x' = FAIL  f x'  SPEC I')) b' f
      ((flatf_gfp (WHILEI_body (⤜) RETURN I' b' f))) x'
      apply (subst (2) flatf_ord.fixp_unfold)
       apply (solves simp)
      ..
    finally have unfold1: flatf_gfp (WHILEI_body (⤜) RETURN I' b' f) x' =
         ?f' (flatf_gfp (WHILEI_body (⤜) RETURN I' b' f)) x'
      .
    have [intro!]: (x. g x  (h:: 'a  'a nres) x)  fx  g  fx  h for g h fx fy
      by (refine_rcg bind_refine'[where R = Id, simplified]) fast
    show ?case
      apply (subst unfold1)
      using 4 unfolding WHILEI_body_def by auto
  qed

  have ge: flatf_gfp ?f x'   flatf_gfp ?f' x' for x'
  proof (induct arbitrary: x' rule: flatf_ord.fixp_induct[where b = top and
        f = ?f])
    case 1
    then show ?case
      unfolding fun_lub_def pw_le_iff
      by (rule ccpo.admissibleI) (smt chain_fun flat_lub_in_chain mem_Collect_eq nofail_simps(1))
  next
    case 2
    then show ?case by (auto simp: WHILEI_mono_ge)
  next
    case 3
    then show ?case by simp
  next
    case (4 x)
    have  (RES X  f  M) = (xX. f x  M) for x f M X
      using intro_spec_refine_iff[of _ _ Id] by auto
    thm bind_refine_RES(2)[of _ Id, simplified]
    have [simp]: flatf_mono FAIL ?f'
      by (simp add: WHILEI_mono_ge)
    have H: A = FAIL  ¬nofail A for A by (auto simp: nofail_def)
    have flatf_gfp ?f' x' = ?f' (?f' (flatf_gfp ?f')) x'
      apply (subst flatf_ord.fixp_unfold)
       apply (solves simp)
      apply (subst flatf_ord.fixp_unfold)
       apply (solves simp)
      ..
    also have  = ?f (?f' (flatf_gfp ?f')) x'
      apply (subst (1) WHILEI_body_def, subst (1) WHILEI_body_def)
      apply (subst (2) WHILEI_body_def, subst (2) WHILEI_body_def)
      apply simp_all
      apply (cases f x')
       apply (auto simp: RES_RETURN_RES nofail_def[symmetric] pw_RES_bind_choose
          eq_commute[of FAIL] H
          split: if_splits
          cong: if_cong)
      done
    also have  = ?f (flatf_gfp ?f') x'
      apply (subst (2) flatf_ord.fixp_unfold)
       apply (solves simp)
      ..
    finally have unfold1: flatf_gfp ?f' x' =
         ?f (flatf_gfp ?f') x'
      .
    have [intro!]: (x. g x (h:: 'a  'a nres) x)  fx  g  fx  h for g h fx fy
      by (refine_rcg bind_refine'[where R = Id, simplified]) fast
    show ?case
      apply (subst unfold1)
      using 4
      unfolding WHILEI_body_def
      by (auto intro: bind_refine'[where R = Id, simplified])
  qed
  show ?thesis
    unfolding RECT_def
    using le[of x'] ge[of x'] by (auto simp: WHILEI_body_trimono)
qed

lemma WHILEIT_add_post_condition:
 (WHILEIT I' b' f' x') =
  (WHILEIT (λx'. I' x'  (b' x'  f' x' = FAIL  f' x'  SPEC I'))
    b' f' x')
  unfolding WHILEIT_def
  apply (subst RECT_WHILEI_body_add_post_condition)
  ..

lemma WHILEIT_rule_stronger_inv:
  assumes
    wf R and
    I s and
    I' s and
    s. I s  I' s  b s  f s  SPEC (λs'. I s'   I' s'  (s', s)  R) and
    s. I s  I' s  ¬ b s  Φ s
 shows WHILETIb f s  SPEC Φ
proof -
  have WHILETIb f s  WHILETλs. I s  I' sb f s
    by (metis (mono_tags, lifting) WHILEIT_weaken)
  also have WHILETλs. I s  I' sb f s  SPEC Φ
    by (rule WHILEIT_rule) (use assms in auto simp: )
  finally show ?thesis .
qed

lemma RES_RETURN_RES2:
   SPEC Φ  (λ(T, T'). RETURN (f T T')) = RES (uncurry f ` {T. Φ T})
  using RES_RETURN_RES[of Collect Φ uncurry f]
  apply (subst (asm)(2) split_prod_bound)
  by auto

lemma WHILEIT_rule_stronger_inv_RES:
  assumes
    wf R and
    I s and
    I' s
    s. I s  I' s  b s  f s  SPEC (λs'. I s'   I' s'  (s', s)  R) and
   s. I s  I' s  ¬ b s  s  Φ
 shows WHILETIb f s  RES Φ
proof -
  have RES_SPEC: RES Φ = SPEC(λs. s  Φ)
    by auto
  have WHILETIb f s  WHILETλs. I s  I' sb f s
    by (metis (mono_tags, lifting) WHILEIT_weaken)
  also have WHILETλs. I s  I' sb f s  RES Φ
    unfolding RES_SPEC
    by (rule WHILEIT_rule) (use assms in auto simp: )
  finally show ?thesis .
qed


subsubsection Merge Post-Conditions

lemma Down_add_assumption_middle:
  assumes
    nofail U and
    V   {(T1, T0). Q T1 T0  P T1  Q' T1 T0} U and
    W   {(T2, T1). R T2 T1} V
  shows W   {(T2, T1). R T2 T1  P T1} V
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by blast

lemma Down_del_assumption_middle:
  assumes
    S1   {(T1, T0). Q T1 T0  P T1  Q' T1 T0} S0
  shows S1   {(T1, T0). Q T1 T0  Q' T1 T0} S0
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by blast

lemma Down_add_assumption_beginning:
  assumes
    nofail U and
    V   {(T1, T0). P T1  Q' T1 T0} U and
    W   {(T2, T1). R T2 T1} V
  shows W   {(T2, T1). R T2 T1  P T1} V
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by blast

lemma Down_add_assumption_beginning_single:
  assumes
    nofail U and
    V   {(T1, T0). P T1} U and
    W   {(T2, T1). R T2 T1} V
  shows W   {(T2, T1). R T2 T1  P T1} V
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by blast

lemma Down_del_assumption_beginning:
  fixes U :: 'a nres and V :: 'b nres and Q Q' :: 'b  'a  bool
  assumes
    V   {(T1, T0). Q T1 T0  Q' T1 T0} U
  shows V   {(T1, T0). Q' T1 T0} U
  using assms unfolding nres_rel_def fun_rel_def pw_le_iff pw_conc_inres pw_conc_nofail
  by blast

method unify_Down_invs2_normalisation_post =
  ((unfold meta_same_imp_rule True_implies_equals conj_assoc)?)

method unify_Down_invs2 =
  (match premises in
      ― ‹if the relation 2-1 has not assumption, we add True. Then we call out method again and
           this time it will match since it has an assumption.
      I: S1   R10 S0 and
      J[thin]: S2   R21 S1
       for S1:: 'b nres and S0 :: 'a nres and S2 :: 'c nres and R10 R21 
        insert True_implies_equals[where P = S2   R21 S1, symmetric,
           THEN equal_elim_rule1, OF J]
    ¦ I[thin]: S1   {(T1, T0). P T1} S0 (multi) and
      J[thin]: _ for S1:: 'b nres and S0 :: 'a nres and P :: 'b  bool 
       match J[uncurry] in
         J[curry]: _  S2   {(T2, T1). R T2 T1} S1 for S2 :: 'c nres and R 
          insert Down_add_assumption_beginning_single[where P = P and R = R and
               W = S2 and V = S1 and U = S0, OF _ I J];
           unify_Down_invs2_normalisation_post
       ¦ _  fail
   ¦ I[thin]: S1   {(T1, T0). P T1  Q' T1 T0} S0 (multi) and
     J[thin]: _ for S1:: 'b nres and S0 :: 'a nres and Q' and P :: 'b  bool 
       match J[uncurry] in
         J[curry]: _  S2   {(T2, T1). R T2 T1} S1 for S2 :: 'c nres and R 
          insert Down_add_assumption_beginning[where Q' = Q' and P = P and R = R and
              W = S2 and V = S1 and U = S0,
              OF _ I J];
           insert Down_del_assumption_beginning[where Q = λS _. P S and Q' = Q' and V = S1 and
             U = S0, OF I];
          unify_Down_invs2_normalisation_post
       ¦ _  fail
   ¦ I[thin]: S1   {(T1, T0). Q T0 T1 Q' T1 T0} S0 (multi) and
     J: _ for S1:: 'b nres and S0 :: 'a nres and Q Q' 
       match J[uncurry] in
         J[curry]: _  S2   {(T2, T1). R T2 T1} S1 for S2 :: 'c nres and R 
          insert Down_del_assumption_beginning[where Q = λ x y. Q y x and Q' = Q', OF I];
           unify_Down_invs2_normalisation_post
       ¦ _  fail
  )

text Example:
lemma
  assumes
    nofail S0 and
    1: S1   {(T1, T0). Q T1 T0  P T1  P' T1  P''' T1  Q' T1 T0  P42 T1} S0 and
    2: S2   {(T2, T1). R T2 T1} S1
  shows S2
       {(T2, T1).
           R T2 T1 
           P T1  P' T1  P''' T1  P42 T1}
         S1
  using assms apply -
  apply unify_Down_invs2+
  apply fast
  done

subsubsection Inversion Tactics

lemma refinement_trans_long:
  A = A'  B = B'  R  R'  A   R B  A'   R' B'
  by (meson pw_ref_iff subsetCE)

lemma mem_set_trans:
  A  B  a  A  a  B
  by auto

lemma fun_rel_syn_invert:
  a = a'  b  b'  (fun_rel_syn a b)  (a'  b')
  by (auto simp: refine_rel_defs)

lemma nres_rel_mono:
  a  a'   a nres_rel  a' nres_rel
  by (fastforce simp: refine_rel_defs nres_rel_def pw_ref_iff)

lemma weaken_SPEC2: m'  SPEC Φ  m = m'  (x. Φ x  Ψ x)  m  SPEC Ψ
  using weaken_SPEC by auto

method match_spec_trans =
  (match conclusion in f  SPEC R for f :: 'a nres and R :: 'a  bool 
    print_term f; match premises in I: _  _  f'  SPEC R' for f' :: 'a nres and
         R' :: 'a  bool
        print_term f'; rule weaken_SPEC2[of f' R' f R])

lemma bind_rule_complete_RES: (M  f  RES Φ) = (M  SPEC (λx. f x  RES Φ))
  by (auto simp: pw_le_iff refine_pw_simps)


subsubsection More Simplification Theorems

lemma nofail_Down_nofail: nofail gS  fS   R gS  nofail fS
  using pw_ref_iff by blast

text This is the refinement version of @{thm WHILEIT_add_post_condition}.
lemma WHILEIT_refine_with_post:
  assumes R0: "I' x'  (x,x')R"
  assumes IREF: "x x'.  (x,x')R; I' x'   I x"
  assumes COND_REF: "x x'.  (x,x')R; I x; I' x'   b x = b' x'"
  assumes STEP_REF:
    "x x'.  (x,x')R; b x; b' x'; I x; I' x'; f' x'  SPEC I'   f x  R (f' x')"
  shows "WHILEIT I b f x R (WHILEIT I' b' f' x')"
  apply (subst (2) WHILEIT_add_post_condition)
  apply (rule WHILEIT_refine)
  subgoal using R0 by blast
  subgoal using IREF by blast
  subgoal using COND_REF by blast
  subgoal using STEP_REF by auto
  done


subsection Some Refinement

lemma Collect_eq_comp: {(c, a). a = f c} O {(x, y). P x y} = {(c, y). P (f c) y}
  by auto

lemma Collect_eq_comp_right:
  {(x, y). P x y} O {(c, a). a = f c} = {(x, c). y. P x y  c = f y} 
  by auto


lemma no_fail_spec_le_RETURN_itself: nofail f  f  SPEC(λx. RETURN x  f)
  by (metis RES_rule nres_order_simps(21) the_RES_inv)

lemma refine_add_invariants':
  assumes
    f S   {(S, S'). Q' S S'  Q S} gS and
    y   {((i, S), S'). P i S S'} (f S) and
    nofail gS
  shows y   {((i, S), S'). P i S S'  Q S'} (f S)
  using assms unfolding pw_le_iff pw_conc_inres pw_conc_nofail
  by force

lemma "weaken_⇓": R'  R  f   R' g  f   R g
  by (meson pw_ref_iff subset_eq)

method match_Down =
  (match conclusion in f   R g for f g R 
    match premises in I: f   R' g for R'
        rule "weaken_⇓"[OF _ I])

lemma Ball2_split_def: ((x, y)  A. P x y)  (x y. (x, y)  A  P x y)
  by blast

lemma in_pair_collect_simp: "(a,b){(a,b). P a b}  P a b"
  by auto

lemma refine_SPEC_refine_Down:
  f  SPEC C  f   {(T', T). T = T'  C T'} (SPEC C)
  apply (rule iffI)
  subgoal
    by (rule SPEC_refine)  auto
  subgoal
    by (metis (no_types, lifting) RETURN_ref_SPECD SPEC_cons_rule dual_order.trans
         in_pair_collect_simp no_fail_spec_le_RETURN_itself nofail_Down_nofail nofail_simps(2))
  done


lemma Down_id_eq: " Id a = a"
  by auto

lemma Down_itself_via_SPEC:
  assumes I  SPEC P and x. P x  (x, x)  R
  shows I   R I
  using assms by (meson inres_SPEC pw_ref_I)
lemma RES_ASSERT_moveout:
  "(a. a  P  Q a)  do {a  RES P; ASSERT(Q a); (f a)} =
   do {a RES P; (f a)}"
  apply (subst dual_order.eq_iff)
  apply (rule conjI)
  subgoal
    by (refine_rcg bind_refine_RES[where R=Id, unfolded Down_id_eq])
      auto
  subgoal
    by (refine_rcg bind_refine_RES[where R=Id, unfolded Down_id_eq])
      auto
  done

lemma bind_if_inverse:
  do {
    S  H;
    if b then f S else g S
    } =
    (if b then do {S  H; f S} else do {S  H; g S})
   for H :: 'a nres
  by auto


lemma bind_cong_nres: (x. g x = g' x)  (do {a  f :: 'a nres;  g a}) =
  (do {a  f :: 'a nres;  g' a})
  by auto

lemma case_prod_cong:
  (a b. f a b = g a b)  (case x of (a, b)  f a b) = (case x of (a, b)  g a b)
  by (cases x) auto

lemma if_replace_cond: (if b then P b else Q b) = (if b then P True else Q False)
  by auto


lemma foldli_cong2:
  assumes
    le: length l = length l' and
    σ: σ = σ' and
    c: c = c' and
    H: σ x. x < length l  c' σ  f (l ! x) σ = f' (l' ! x) σ
  shows foldli l c f σ = foldli l' c' f' σ'
proof -
  show ?thesis
    using le H unfolding c[symmetric] σ[symmetric]
  proof (induction l arbitrary: l' σ)
    case Nil
    then show ?case by simp
  next
    case (Cons a l l'') note IH=this(1) and le = this(2) and H = this(3)
    show ?case
      using le H[of Suc _] H[of 0] IH[of tl l'' f' (hd l'') σ]
      by (cases l'') auto
  qed
qed

lemma foldli_foldli_list_nth:
  foldli xs c P a = foldli [0..<length xs] c (λi. P (xs ! i)) a
proof (induction xs arbitrary: a)
  case Nil
  then show ?case by auto
next
  case (Cons x xs) note IH = this(1)
  have 1: [0..<length (x # xs)] = 0 # [1..<length (x#xs)]
    by (subst upt_rec)  simp
  have 2: [1..<length (x#xs)] = map Suc [0..<length xs]
    by (induction xs) auto
  have AB: foldli [0..<length (x # xs)] c (λi. P ((x # xs) ! i)) a =
      foldli (0 # [1..<length (x#xs)]) c (λi. P ((x # xs) ! i)) a
      (is ?A = ?B)
    unfolding 1 ..
  {
    assume [simp]: c a
    have foldli (0 # [1..<length (x#xs)]) c (λi. P ((x # xs) ! i)) a =
       foldli [1..<length (x#xs)] c (λi. P ((x # xs) ! i)) (P x a)
      by simp
    also have   = foldli [0..<length xs] c (λi. P (xs ! i)) (P x a)
      unfolding 2
      by (rule foldli_cong2) auto
    finally have ?A = foldli [0..<length xs] c (λi. P (xs ! i)) (P x a)
      using AB
      by simp
  }
  moreover {
    assume [simp]: ¬c a
    have ?B = a
      by simp
  }
  ultimately show ?case by (auto simp: IH)
qed

lemma RES_RES13_RETURN_RES: do {
  (M, N, D, Q, W, vm, φ, clvls, cach, lbd, outl, stats, fast_ema, slow_ema, ccount,
       vdom, avdom, lcount)  RES A;
  RES (f M N D Q W vm φ clvls cach lbd outl stats fast_ema slow_ema ccount
      vdom avdom lcount)
} = RES ((M, N, D, Q, W, vm, φ, clvls, cach, lbd, outl, stats, fast_ema, slow_ema, ccount,
       vdom, avdom, lcount)A. f M N D Q W vm φ clvls cach lbd outl stats fast_ema slow_ema ccount
      vdom avdom lcount)
  by (force simp:  pw_eq_iff refine_pw_simps uncurry_def)


lemma RES_SPEC_conv: RES P = SPEC (λv. v  P)
  by auto

lemma add_invar_refineI_P: A   {(x,y). R x y} B  (nofail A A  SPEC P) 
  A   {(x,y). R x y  P x} B
  using add_invar_refineI[of λ_. A _  _ λ_. B P, where R={(x,y). R x y} and I=P]
  by auto


lemma (in -)WHILEIT_rule_stronger_inv_RES':
  assumes
    wf R and
    I s and
    I' s
    s. I s  I' s  b s  f s  SPEC (λs'. I s'   I' s'  (s', s)  R) and
   s. I s  I' s  ¬ b s  RETURN s   H (RES Φ)
 shows WHILETIb f s   H (RES Φ)
proof -
  have RES_SPEC: RES Φ = SPEC(λs. s  Φ)
    by auto
  have WHILETIb f s  WHILETλs. I s  I' sb f s
    by (metis (mono_tags, lifting) WHILEIT_weaken)
  also have WHILETλs. I s  I' sb f s   H (RES Φ)
    unfolding RES_SPEC conc_fun_SPEC
    apply (rule WHILEIT_rule[OF assms(1)])
    subgoal using assms(2,3) by auto
    subgoal using assms(4) by auto
    subgoal using assms(5) unfolding RES_SPEC conc_fun_SPEC by auto
    done
  finally show ?thesis .
qed

lemma same_in_Id_option_rel:
  x = x'  (x, x')  Idoption_rel
  by auto

definition find_in_list_between :: ('a  bool)  nat  nat  'a list  nat option nres where
  find_in_list_between P a b C = do {
      (x, _)  WHILETλ(found, i). i  a  i  length C  i  b  (j{a..<i}. ¬P (C!j)) 
        (j. found = Some j  (i = j  P (C ! j)  j < b  j  a))(λ(found, i). found = None  i < b)
        (λ(_, i). do {
          ASSERT(i < length C);
          if P (C!i) then RETURN (Some i, i) else RETURN (None, i+1)
        })
        (None, a);
      RETURN x
  }

lemma find_in_list_between_spec:
  assumes a  length C and b  length C and a  b
  shows
    find_in_list_between P a b C  SPEC(λi.
       (i  None   P (C ! the i)  the i  a  the i < b) 
       (i = None  (j. j  a  j < b  ¬P (C!j))))
  unfolding find_in_list_between_def
  apply (refine_vcg WHILEIT_rule[where R = measure (λ(f, i). Suc (length C) -
    (i + (if f = None then 0 else 1)))])
  subgoal by auto
  subgoal by auto
  subgoal using assms by auto
  subgoal using assms by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal using assms by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by (auto simp: less_Suc_eq)
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  done


lemma nfoldli_cong2:
  assumes
    le: length l = length l' and
    σ: σ = σ' and
    c: c = c' and
    H: σ x. x < length l  c' σ  f (l ! x) σ = f' (l' ! x) σ
  shows nfoldli l c f σ = nfoldli l' c' f' σ'
proof -
  show ?thesis
    using le H unfolding c[symmetric] σ[symmetric]
  proof (induction l arbitrary: l' σ)
    case Nil
    then show ?case by simp
  next
    case (Cons a l l'') note IH=this(1) and le = this(2) and H = this(3)
    show ?case
      using le H[of Suc _] H[of 0] IH[of tl l'' _]
      by (cases l'')
        (auto intro: bind_cong_nres)
  qed
qed

lemma nfoldli_nfoldli_list_nth:
  nfoldli xs c P a = nfoldli [0..<length xs] c (λi. P (xs ! i)) a
proof (induction xs arbitrary: a)
  case Nil
  then show ?case by auto
next
  case (Cons x xs) note IH = this(1)
  have 1: [0..<length (x # xs)] = 0 # [1..<length (x#xs)]
    by (subst upt_rec)  simp
  have 2: [1..<length (x#xs)] = map Suc [0..<length xs]
    by (induction xs) auto
  have AB: nfoldli [0..<length (x # xs)] c (λi. P ((x # xs) ! i)) a =
      nfoldli (0 # [1..<length (x#xs)]) c (λi. P ((x # xs) ! i)) a
      (is ?A = ?B)
    unfolding 1 ..
  {
    assume [simp]: c a
    have nfoldli (0 # [1..<length (x#xs)]) c (λi. P ((x # xs) ! i)) a =
       do {
         σ  (P x a);
         nfoldli [1..<length (x#xs)] c (λi. P ((x # xs) ! i)) σ
        }
      by simp
    moreover have nfoldli [1..<length (x#xs)] c (λi. P ((x # xs) ! i)) σ  =
       nfoldli [0..<length xs] c (λi. P (xs ! i)) σ for σ
      unfolding 2
      by (rule nfoldli_cong2) auto
    ultimately have ?A = do {
         σ  (P x a);
         nfoldli [0..<length xs] c (λi. P (xs ! i))  σ
        }
      using AB
      by (auto intro: bind_cong_nres)
  }
  moreover {
    assume [simp]: ¬c a
    have ?B = RETURN a
      by simp
  }
  ultimately show ?case by (auto simp: IH intro: bind_cong_nres)
qed

(*TODO kill once shared*)
definition "list_mset_rel  br mset (λ_. True)"

lemma
  Nil_list_mset_rel_iff:
    ([], aaa)  list_mset_rel  aaa = {#} and
  empty_list_mset_rel_iff:
    (a, {#})  list_mset_rel  a = []
  by (auto simp: list_mset_rel_def br_def)

definition list_rel_mset_rel where list_rel_mset_rel_internal:
list_rel_mset_rel  λR. Rlist_rel O list_mset_rel

lemma list_rel_mset_rel_def[refine_rel_defs]:
  Rlist_rel_mset_rel = Rlist_rel O list_mset_rel
  unfolding relAPP_def list_rel_mset_rel_internal ..

lemma list_rel_mset_rel_imp_same_length: (a, b)  Rlist_rel_mset_rel  length a = size b
  by (auto simp: list_rel_mset_rel_def list_mset_rel_def br_def
      dest: list_rel_imp_same_length)

lemma while_upt_while_direct1:
  "b  a 
  do {
    (_,σ)  WHILET (FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x})
      ([a..<b],σ);
    RETURN σ
  }  do {
    (_,σ)  WHILET (λ(i, x). i < b  c x) (λ(i, x). do {ASSERT (i < b);  σ'f i x; RETURN (i+1,σ')
}) (a,σ);
    RETURN σ
  }"
  apply (rewrite at _   Down_id_eq[symmetric])
  apply (refine_vcg WHILET_refine[where R = {((l, x'), (i::nat, x::'a)). x= x'  i  b  i  a 
     l = drop (i-a) [a..<b]}])
  subgoal by auto
  subgoal by (auto simp: FOREACH_cond_def)
  subgoal by (auto simp: FOREACH_body_def intro!: bind_refine[OF Id_refine])
  subgoal by auto
  done

lemma while_upt_while_direct2:
  "b  a 
  do {
    (_,σ)  WHILET (FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x})
      ([a..<b],σ);
    RETURN σ
  }  do {
    (_,σ)  WHILET (λ(i, x). i < b  c x) (λ(i, x). do {ASSERT (i < b);  σ'f i x; RETURN (i+1,σ')
}) (a,σ);
    RETURN σ
  }"
  apply (rewrite at _   Down_id_eq[symmetric])
  apply (refine_vcg WHILET_refine[where R = {((i::nat, x::'a), (l, x')). x= x'  i  b  i  a 
    l = drop (i-a) [a..<b]}])
  subgoal by auto
  subgoal by (auto simp: FOREACH_cond_def)
  subgoal by (auto simp: FOREACH_body_def intro!: bind_refine[OF Id_refine])
  subgoal by (auto simp: FOREACH_body_def intro!: bind_refine[OF Id_refine])
  subgoal by auto
  done

lemma while_upt_while_direct:
  "b  a 
  do {
    (_,σ)  WHILET (FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x})
      ([a..<b],σ);
    RETURN σ
  } = do {
    (_,σ)  WHILET (λ(i, x). i < b  c x) (λ(i, x). do {ASSERT (i < b);  σ'f i x; RETURN (i+1,σ')
}) (a,σ);
    RETURN σ
  }"
  using while_upt_while_direct1[of a b] while_upt_while_direct2[of a b]
  unfolding dual_order.eq_iff by fast


lemma while_nfoldli:
  "do {
    (_,σ)  WHILET (FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x}) (l,σ);
    RETURN σ
  }  nfoldli l c f σ"
  apply (induct l arbitrary: σ)
  apply (subst WHILET_unfold)
  apply (simp add: FOREACH_cond_def)

  apply (subst WHILET_unfold)
  apply (auto
    simp: FOREACH_cond_def FOREACH_body_def
    intro: bind_mono Refine_Basic.bind_mono(1))
 done

lemma nfoldli_while: "nfoldli l c f σ
          
         (WHILETI(FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x}) (l, σ) 
          (λ(_, σ). RETURN σ))"
proof (induct l arbitrary: σ)
  case Nil thus ?case by (subst WHILEIT_unfold) (auto simp: FOREACH_cond_def)
next
  case (Cons x ls)
  show ?case
  proof (cases "c σ")
    case False thus ?thesis
      apply (subst WHILEIT_unfold)
      unfolding FOREACH_cond_def
      by simp
  next
    case [simp]: True
    from Cons show ?thesis
      apply (subst WHILEIT_unfold)
      unfolding FOREACH_cond_def FOREACH_body_def
      apply clarsimp
      apply (rule Refine_Basic.bind_mono)
      apply simp_all
      done
  qed
qed

lemma while_eq_nfoldli: "do {
    (_,σ)  WHILET (FOREACH_cond c) (λx. do {ASSERT (FOREACH_cond c x); FOREACH_body f x}) (l,σ);
    RETURN σ
  } = nfoldli l c f σ"
  apply (rule antisym)
  apply (rule while_nfoldli)
  apply (rule order_trans[OF nfoldli_while[where I="λ_. True"]])
  apply (simp add: WHILET_def)
  done
lemma RES_RETURN_RES5:
   SPEC Φ  (λ(T1, T2, T3, T4, T5). RETURN (f T1 T2 T3 T4 T5)) =
    RES ((λ(a, b, c, d, e). f a b c d e) ` {T. Φ T})
  using RES_RETURN_RES[of Collect Φ λ(a, b, c, d, e). f a b c d e]
  apply (subst (asm)(2) split_prod_bound)
  apply (subst (asm)(3) split_prod_bound)
  apply (subst (asm)(4) split_prod_bound)
  apply (subst (asm)(5) split_prod_bound)
  by simp

lemma RES_RETURN_RES6:
   SPEC Φ  (λ(T1, T2, T3, T4, T5, T6). RETURN (f T1 T2 T3 T4 T5 T6)) =
    RES ((λ(a, b, c, d, e, f'). f a b c d e f') ` {T. Φ T})
  using RES_RETURN_RES[of Collect Φ λ(a, b, c, d, e, f'). f a b c d e f']
  apply (subst (asm)(2) split_prod_bound)
  apply (subst (asm)(3) split_prod_bound)
  apply (subst (asm)(4) split_prod_bound)
  apply (subst (asm)(5) split_prod_bound)
  apply (subst (asm)(6) split_prod_bound)
  by simp

lemma RES_RETURN_RES7:
   SPEC Φ  (λ(T1, T2, T3, T4, T5, T6, T7). RETURN (f T1 T2 T3 T4 T5 T6 T7)) =
    RES ((λ(a, b, c, d, e, f', g). f a b c d e f' g) ` {T. Φ T})
  using RES_RETURN_RES[of Collect Φ λ(a, b, c, d, e, f', g). f a b c d e f' g]
  apply (subst (asm)(2) split_prod_bound)
  apply (subst (asm)(3) split_prod_bound)
  apply (subst (asm)(4) split_prod_bound)
  apply (subst (asm)(5) split_prod_bound)
  apply (subst (asm)(6) split_prod_bound)
  apply (subst (asm)(7) split_prod_bound)
  by simp

lemma RES_RES7_RETURN_RES:
   RES A  (λ(a, b, c, d, e, g, h). RES (f a b c d e g h)) =
   RES (((λ(a, b, c, d, e, g, h). f a b c d e g h) ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def Bex_def
    split: prod.splits)


lemma RES_RES8_RETURN_RES:
   RES A  (λ(a, b, c, d, e, g, h, i). RES (f a b c d e g h i)) =
   RES (((λ(a, b, c, d, e, g, h, i). f a b c d e g h i) ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def Bex_def
    split: prod.splits)

lemma RES_RES9_RETURN_RES:
   RES A  (λ(a, b, c, d, e, g, h, i, j). RES (f a b c d e g h i j)) =
   RES (((λ(a, b, c, d, e, g, h, i, j). f a b c d e g h i j) ` A))
  by (auto simp:  pw_eq_iff refine_pw_simps uncurry_def Bex_def
    split: prod.splits)

lemma fold_eq_nfoldli:
  "RETURN (fold f l s) = nfoldli l (λ_. True) (λx s. RETURN (f x s)) s"
  by (induction l arbitrary: s) auto

text this is the less dumb version of the refinement for while loops, keeping the loop invariant.
lemma WHILEIT_refine_with_all_loopinvariants:
  assumes R0: "I' x'  (x,x')R"
  assumes IREF: "x x'.  (x,x')R; I' x'   I x"
  assumes COND_REF: "x x'.  (x,x')R; I x; I' x'   b x = b' x'"
  assumes STEP_REF:
    "x x'.  (x,x')R; b x; b' x'; I x; I' x'; f' x'  SPEC I'   f x  R (f' x')"
  shows "WHILEIT I b f x {(a,b). (a,b)  R  I a  I' b} (WHILEIT I' b' f' x')"
  apply (subst (2) WHILEIT_add_post_condition)
  apply (rule WHILEIT_refine)
  subgoal using R0 IREF by blast
  subgoal using IREF by blast
  subgoal using COND_REF by blast
  subgoal using STEP_REF apply auto
    by (smt (verit, best) IREF in_pair_collect_simp inres_SPEC pw_ref_iff)
  done

text This lemma cannot be moved to theoryWeidenbach_Book_Base.WB_List_More, because the syntax
 termCARD('a) does not exist there.
lemma finite_length_le_CARD:
  assumes distinct (xs :: 'a :: finite list)
  shows length xs  CARD('a)
proof -
  have set xs  UNIV
    by auto
  show ?thesis
    by (metis assms card_ge_UNIV distinct_card le_cases)
qed

lemma WHILEIT_refine_with_invariant_and_break:
  assumes R0: I' x'  (x,x')R
  assumes IREF: x x'.  (x,x')R; I' x'   I x
  assumes COND_REF: x x'.  (x,x')R; I x; I' x'   b x = b' x'
  assumes STEP_REF:
    x x'.  (x,x')R; b x; b' x'; I x; I' x'   f x  R (f' x')
  shows WHILEIT I b f x {(x, x'). (x, x')  R  I x   I' x'  ¬b' x'} (WHILEIT I' b' f' x')
    (is _  ?R' _)
  apply (subst (2)WHILEIT_add_post_condition)
  apply (refine_vcg WHILEIT_refine_genR[where R'=R and R = ?R'])
  subgoal by (auto intro: assms)[]
  subgoal by (auto intro: assms)[]
  subgoal using COND_REF by (auto)
  subgoal by (auto intro: assms)[]
  subgoal by (auto intro: assms)[]
  done

end