Theory utp_rel_laws
section ‹ Relational Calculus Laws ›
theory utp_rel_laws
  imports 
    utp_rel
    utp_recursion
begin
subsection ‹ Conditional Laws ›
  
lemma comp_cond_left_distr:
  "((P ◃ b ▹⇩r Q) ;; R) = ((P ;; R) ◃ b ▹⇩r (Q ;; R))"
  by (rel_auto)
lemma cond_seq_left_distr:
  "outα ♯ b ⟹ ((P ◃ b ▹ Q) ;; R) = ((P ;; R) ◃ b ▹ (Q ;; R))"
  by (rel_auto)
lemma cond_seq_right_distr:
  "inα ♯ b ⟹ (P ;; (Q ◃ b ▹ R)) = ((P ;; Q) ◃ b ▹ (P ;; R))"
  by (rel_auto)
text ‹ Alternative expression of conditional using assumptions and choice ›
lemma rcond_rassume_expand: "P ◃ b ▹⇩r Q = ([b]⇧⊤ ;; P) ⊓ ([(¬ b)]⇧⊤ ;; Q)"
  by (rel_auto)
subsection ‹ Precondition and Postcondition Laws ›
  
theorem precond_equiv:
  "P = (P ;; true) ⟷ (outα ♯ P)"
  by (rel_auto)
theorem postcond_equiv:
  "P = (true ;; P) ⟷ (inα ♯ P)"
  by (rel_auto)
lemma precond_right_unit: "outα ♯ p ⟹ (p ;; true) = p"
  by (metis precond_equiv)
lemma postcond_left_unit: "inα ♯ p ⟹ (true ;; p) = p"
  by (metis postcond_equiv)
theorem precond_left_zero:
  assumes "outα ♯ p" "p ≠ false"
  shows "(true ;; p) = true"
  using assms by (rel_auto)
theorem feasibile_iff_true_right_zero:
  "P ;; true = true ⟷ `∃ outα ∙ P`"
  by (rel_auto)
    
subsection ‹ Sequential Composition Laws ›
    
lemma seqr_assoc: "(P ;; Q) ;; R = P ;; (Q ;; R)"
  by (rel_auto)
lemma seqr_left_unit [simp]:
  "II ;; P = P"
  by (rel_auto)
lemma seqr_right_unit [simp]:
  "P ;; II = P"
  by (rel_auto)
lemma seqr_left_zero [simp]:
  "false ;; P = false"
  by pred_auto
lemma seqr_right_zero [simp]:
  "P ;; false = false"
  by pred_auto
lemma impl_seqr_mono: "⟦ `P ⇒ Q`; `R ⇒ S` ⟧ ⟹ `(P ;; R) ⇒ (Q ;; S)`"
  by (pred_blast)
lemma seqr_mono:
  "⟦ P⇩1 ⊑ P⇩2; Q⇩1 ⊑ Q⇩2 ⟧ ⟹ (P⇩1 ;; Q⇩1) ⊑ (P⇩2 ;; Q⇩2)"
  by (rel_blast)
    
lemma seqr_monotonic:
  "⟦ mono P; mono Q ⟧ ⟹ mono (λ X. P X ;; Q X)"
  by (simp add: mono_def, rel_blast)
    
lemma Monotonic_seqr_tail [closure]:
  assumes "Monotonic F"
  shows "Monotonic (λ X. P ;; F(X))"
  by (simp add: assms monoD monoI seqr_mono)
    
lemma seqr_exists_left:
  "((∃ $x ∙ P) ;; Q) = (∃ $x ∙ (P ;; Q))"
  by (rel_auto)
lemma seqr_exists_right:
  "(P ;; (∃ $x´ ∙ Q)) = (∃ $x´ ∙ (P ;; Q))"
  by (rel_auto)
lemma seqr_or_distl:
  "((P ∨ Q) ;; R) = ((P ;; R) ∨ (Q ;; R))"
  by (rel_auto)
lemma seqr_or_distr:
  "(P ;; (Q ∨ R)) = ((P ;; Q) ∨ (P ;; R))"
  by (rel_auto)
lemma seqr_inf_distl:
  "((P ⊓ Q) ;; R) = ((P ;; R) ⊓ (Q ;; R))"
  by (rel_auto)
lemma seqr_inf_distr:
  "(P ;; (Q ⊓ R)) = ((P ;; Q) ⊓ (P ;; R))"
  by (rel_auto)
lemma seqr_and_distr_ufunc:
  "ufunctional P ⟹ (P ;; (Q ∧ R)) = ((P ;; Q) ∧ (P ;; R))"
  by (rel_auto)
lemma seqr_and_distl_uinj:
  "uinj R ⟹ ((P ∧ Q) ;; R) = ((P ;; R) ∧ (Q ;; R))"
  by (rel_auto)
lemma seqr_unfold:
  "(P ;; Q) = (❙∃ v ∙ P⟦«v»/$❙v´⟧ ∧ Q⟦«v»/$❙v⟧)"
  by (rel_auto)
lemma seqr_middle:
  assumes "vwb_lens x"
  shows "(P ;; Q) = (❙∃ v ∙ P⟦«v»/$x´⟧ ;; Q⟦«v»/$x⟧)"
  using assms
  by (rel_auto', metis vwb_lens_wb wb_lens.source_stability)
lemma seqr_left_one_point:
  assumes "vwb_lens x"
  shows "((P ∧ $x´ =⇩u «v») ;; Q) = (P⟦«v»/$x´⟧ ;; Q⟦«v»/$x⟧)"
  using assms
  by (rel_auto, metis vwb_lens_wb wb_lens.get_put)
lemma seqr_right_one_point:
  assumes "vwb_lens x"
  shows "(P ;; ($x =⇩u «v» ∧ Q)) = (P⟦«v»/$x´⟧ ;; Q⟦«v»/$x⟧)"
  using assms
  by (rel_auto, metis vwb_lens_wb wb_lens.get_put)
lemma seqr_left_one_point_true:
  assumes "vwb_lens x"
  shows "((P ∧ $x´) ;; Q) = (P⟦true/$x´⟧ ;; Q⟦true/$x⟧)"
  by (metis assms seqr_left_one_point true_alt_def upred_eq_true)
lemma seqr_left_one_point_false:
  assumes "vwb_lens x"
  shows "((P ∧ ¬$x´) ;; Q) = (P⟦false/$x´⟧ ;; Q⟦false/$x⟧)"
  by (metis assms false_alt_def seqr_left_one_point upred_eq_false)
lemma seqr_right_one_point_true:
  assumes "vwb_lens x"
  shows "(P ;; ($x ∧ Q)) = (P⟦true/$x´⟧ ;; Q⟦true/$x⟧)"
  by (metis assms seqr_right_one_point true_alt_def upred_eq_true)
lemma seqr_right_one_point_false:
  assumes "vwb_lens x"
  shows "(P ;; (¬$x ∧ Q)) = (P⟦false/$x´⟧ ;; Q⟦false/$x⟧)"
  by (metis assms false_alt_def seqr_right_one_point upred_eq_false)
lemma seqr_insert_ident_left:
  assumes "vwb_lens x" "$x´ ♯ P" "$x ♯ Q"
  shows "(($x´ =⇩u $x ∧ P) ;; Q) = (P ;; Q)"
  using assms
  by (rel_simp, meson vwb_lens_wb wb_lens_weak weak_lens.put_get)
lemma seqr_insert_ident_right:
  assumes "vwb_lens x" "$x´ ♯ P" "$x ♯ Q"
  shows "(P ;; ($x´ =⇩u $x ∧ Q)) = (P ;; Q)"
  using assms
  by (rel_simp, metis (no_types, opaque_lifting) vwb_lens_def wb_lens_def weak_lens.put_get)
lemma seq_var_ident_lift:
  assumes "vwb_lens x" "$x´ ♯ P" "$x ♯ Q"
  shows "(($x´ =⇩u $x ∧ P) ;; ($x´ =⇩u $x ∧ Q)) = ($x´ =⇩u $x ∧ (P ;; Q))"
  using assms by (rel_auto', metis (no_types, lifting) vwb_lens_wb wb_lens_weak weak_lens.put_get)
lemma seqr_bool_split:
  assumes "vwb_lens x"
  shows "P ;; Q = (P⟦true/$x´⟧ ;; Q⟦true/$x⟧ ∨ P⟦false/$x´⟧ ;; Q⟦false/$x⟧)"
  using assms
  by (subst seqr_middle[of x], simp_all)
lemma cond_inter_var_split:
  assumes "vwb_lens x"
  shows "(P ◃ $x´ ▹ Q) ;; R = (P⟦true/$x´⟧ ;; R⟦true/$x⟧ ∨ Q⟦false/$x´⟧ ;; R⟦false/$x⟧)"
proof -
  have "(P ◃ $x´ ▹ Q) ;; R = (($x´ ∧ P) ;; R ∨ (¬ $x´ ∧ Q) ;; R)"
    by (simp add: cond_def seqr_or_distl)
  also have "... = ((P ∧ $x´) ;; R ∨ (Q ∧ ¬$x´) ;; R)"
    by (rel_auto)
  also have "... = (P⟦true/$x´⟧ ;; R⟦true/$x⟧ ∨ Q⟦false/$x´⟧ ;; R⟦false/$x⟧)"
    by (simp add: seqr_left_one_point_true seqr_left_one_point_false assms)
  finally show ?thesis .
qed
theorem seqr_pre_transfer: "inα ♯ q ⟹ ((P ∧ q) ;; R) = (P ;; (q⇧- ∧ R))"
  by (rel_auto)
theorem seqr_pre_transfer':
  "((P ∧ ⌈q⌉⇩>) ;; R) = (P ;; (⌈q⌉⇩< ∧ R))"
  by (rel_auto)
theorem seqr_post_out: "inα ♯ r ⟹ (P ;; (Q ∧ r)) = ((P ;; Q) ∧ r)"
  by (rel_blast)
lemma seqr_post_var_out:
  fixes x :: "(bool ⟹ 'α)"
  shows "(P ;; (Q ∧ $x´)) = ((P ;; Q) ∧ $x´)"
  by (rel_auto)
theorem seqr_post_transfer: "outα ♯ q ⟹ (P ;; (q ∧ R)) = ((P ∧ q⇧-) ;; R)"
  by (rel_auto)
lemma seqr_pre_out: "outα ♯ p ⟹ ((p ∧ Q) ;; R) = (p ∧ (Q ;; R))"
  by (rel_blast)
lemma seqr_pre_var_out:
  fixes x :: "(bool ⟹ 'α)"
  shows "(($x ∧ P) ;; Q) = ($x ∧ (P ;; Q))"
  by (rel_auto)
lemma seqr_true_lemma:
  "(P = (¬ ((¬ P) ;; true))) = (P = (P ;; true))"
  by (rel_auto)
lemma seqr_to_conj: "⟦ outα ♯ P; inα ♯ Q ⟧ ⟹ (P ;; Q) = (P ∧ Q)"
  by (metis postcond_left_unit seqr_pre_out utp_pred_laws.inf_top.right_neutral)
lemma shEx_lift_seq_1 [uquant_lift]:
  "((❙∃ x ∙ P x) ;; Q) = (❙∃ x ∙ (P x ;; Q))"
  by rel_auto
lemma shEx_mem_lift_seq_1 [uquant_lift]:
  assumes "outα ♯ A"
  shows "((❙∃ x ∈ A ∙ P x) ;; Q) = (❙∃ x ∈ A ∙ (P x ;; Q))"
  using assms by rel_blast
lemma shEx_lift_seq_2 [uquant_lift]:
  "(P ;; (❙∃ x ∙ Q x)) = (❙∃ x ∙ (P ;; Q x))"
  by rel_auto
lemma shEx_mem_lift_seq_2 [uquant_lift]:
  assumes "inα ♯ A"
  shows "(P ;; (❙∃ x ∈ A ∙ Q x)) = (❙∃ x ∈ A ∙ (P ;; Q x))"
  using assms by rel_blast
subsection ‹ Iterated Sequential Composition Laws ›
  
lemma iter_seqr_nil [simp]: "(;; i : [] ∙ P(i)) = II"
  by (simp add: seqr_iter_def)
    
lemma iter_seqr_cons [simp]: "(;; i : (x # xs) ∙ P(i)) = P(x) ;; (;; i : xs ∙ P(i))"
  by (simp add: seqr_iter_def)
subsection ‹ Quantale Laws ›
lemma seq_Sup_distl: "P ;; (⨅ A) = (⨅ Q∈A. P ;; Q)"
  by (transfer, auto)
lemma seq_Sup_distr: "(⨅ A) ;; Q = (⨅ P∈A. P ;; Q)"
  by (transfer, auto)
lemma seq_UINF_distl: "P ;; (⨅ Q∈A ∙ F(Q)) = (⨅ Q∈A ∙ P ;; F(Q))"
  by (simp add: UINF_as_Sup_collect seq_Sup_distl)
lemma seq_UINF_distl': "P ;; (⨅ Q ∙ F(Q)) = (⨅ Q ∙ P ;; F(Q))"
  by (metis UINF_mem_UNIV seq_UINF_distl)
lemma seq_UINF_distr: "(⨅ P∈A ∙ F(P)) ;; Q = (⨅ P∈A ∙ F(P) ;; Q)"
  by (simp add: UINF_as_Sup_collect seq_Sup_distr)
lemma seq_UINF_distr': "(⨅ P ∙ F(P)) ;; Q = (⨅ P ∙ F(P) ;; Q)"
  by (metis UINF_mem_UNIV seq_UINF_distr)
lemma seq_SUP_distl: "P ;; (⨅i∈A. Q(i)) = (⨅i∈A. P ;; Q(i))"
  by (metis image_image seq_Sup_distl)
lemma seq_SUP_distr: "(⨅i∈A. P(i)) ;; Q = (⨅i∈A. P(i) ;; Q)"
  by (simp add: seq_Sup_distr)
subsection ‹ Skip Laws ›
    
lemma cond_skip: "outα ♯ b ⟹ (b ∧ II) = (II ∧ b⇧-)"
  by (rel_auto)
lemma pre_skip_post: "(⌈b⌉⇩< ∧ II) = (II ∧ ⌈b⌉⇩>)"
  by (rel_auto)
lemma skip_var:
  fixes x :: "(bool ⟹ 'α)"
  shows "($x ∧ II) = (II ∧ $x´)"
  by (rel_auto)
lemma skip_r_unfold:
  "vwb_lens x ⟹ II = ($x´ =⇩u $x ∧ II↾⇩αx)"
  by (rel_simp, metis mwb_lens.put_put vwb_lens_mwb vwb_lens_wb wb_lens.get_put)
lemma skip_r_alpha_eq:
  "II = ($❙v´ =⇩u $❙v)"
  by (rel_auto)
lemma skip_ra_unfold:
  "II⇘x;y⇙ = ($x´ =⇩u $x ∧ II⇘y⇙)"
  by (rel_auto)
lemma skip_res_as_ra:
  "⟦ vwb_lens y; x +⇩L y ≈⇩L 1⇩L; x ⨝ y ⟧ ⟹ II↾⇩αx = II⇘y⇙"
  apply (rel_auto)
   apply (metis (no_types, lifting) lens_indep_def)
  apply (metis vwb_lens.put_eq)
  done
subsection ‹ Assignment Laws ›
  
lemma assigns_subst [usubst]:
  "⌈σ⌉⇩s † ⟨ρ⟩⇩a = ⟨ρ ∘ σ⟩⇩a"
  by (rel_auto)
lemma assigns_r_comp: "(⟨σ⟩⇩a ;; P) = (⌈σ⌉⇩s † P)"
  by (rel_auto)
lemma assigns_r_feasible:
  "(⟨σ⟩⇩a ;; true) = true"
  by (rel_auto)
lemma assign_subst [usubst]:
  "⟦ mwb_lens x; mwb_lens y ⟧ ⟹ [$x ↦⇩s ⌈u⌉⇩<] † (y := v) = (x, y) := (u, [x ↦⇩s u] † v)"
  by (rel_auto)
lemma assign_vacuous_skip:
  assumes "vwb_lens x"
  shows "(x := &x) = II"
  using assms by rel_auto
text ‹ The following law shows the case for the above law when $x$ is only mainly-well behaved. We
  require that the state is one of those in which $x$ is well defined using and assumption. ›
lemma assign_vacuous_assume:
  assumes "mwb_lens x"
  shows "[(&❙v ∈⇩u «𝒮⇘x⇙»)]⇧⊤ ;; (x := &x) = [(&❙v ∈⇩u «𝒮⇘x⇙»)]⇧⊤"
  using assms by rel_auto
lemma assign_simultaneous:
  assumes "vwb_lens y" "x ⨝ y"
  shows "(x,y) := (e, &y) = (x := e)"
  by (simp add: assms usubst_upd_comm usubst_upd_var_id)
lemma assigns_idem: "mwb_lens x ⟹ (x,x) := (u,v) = (x := v)"
  by (simp add: usubst)
lemma assigns_comp: "(⟨f⟩⇩a ;; ⟨g⟩⇩a) = ⟨g ∘ f⟩⇩a"
  by (simp add: assigns_r_comp usubst)
lemma assigns_cond: "(⟨f⟩⇩a ◃ b ▹⇩r ⟨g⟩⇩a) = ⟨f ◃ b ▹⇩s g⟩⇩a"
  by (rel_auto)
lemma assigns_r_conv:
  "bij f ⟹ ⟨f⟩⇩a⇧- = ⟨inv f⟩⇩a"
  by (rel_auto, simp_all add: bij_is_inj bij_is_surj surj_f_inv_f)
lemma assign_pred_transfer:
  fixes x :: "('a ⟹ 'α)"
  assumes "$x ♯ b" "outα ♯ b"
  shows "(b ∧ x := v) = (x := v ∧ b⇧-)"
  using assms by (rel_blast)
    
lemma assign_r_comp: "x := u ;; P = P⟦⌈u⌉⇩</$x⟧"
  by (simp add: assigns_r_comp usubst alpha)
    
lemma assign_test: "mwb_lens x ⟹ (x := «u» ;; x := «v») = (x := «v»)"
  by (simp add: assigns_comp usubst)
lemma assign_twice: "⟦ mwb_lens x; x ♯ f ⟧ ⟹ (x := e ;; x := f) = (x := f)"
  by (simp add: assigns_comp usubst unrest)
 
lemma assign_commute:
  assumes "x ⨝ y" "x ♯ f" "y ♯ e"
  shows "(x := e ;; y := f) = (y := f ;; x := e)"
  using assms
  by (rel_simp, simp_all add: lens_indep_comm)
lemma assign_cond:
  fixes x :: "('a ⟹ 'α)"
  assumes "outα ♯ b"
  shows "(x := e ;; (P ◃ b ▹ Q)) = ((x := e ;; P) ◃ (b⟦⌈e⌉⇩</$x⟧) ▹ (x := e ;; Q))"
  by (rel_auto)
lemma assign_rcond:
  fixes x :: "('a ⟹ 'α)"
  shows "(x := e ;; (P ◃ b ▹⇩r Q)) = ((x := e ;; P) ◃ (b⟦e/x⟧) ▹⇩r (x := e ;; Q))"
  by (rel_auto)
lemma assign_r_alt_def:
  fixes x :: "('a ⟹ 'α)"
  shows "x := v = II⟦⌈v⌉⇩</$x⟧"
  by (rel_auto)
lemma assigns_r_ufunc: "ufunctional ⟨f⟩⇩a"
  by (rel_auto)
lemma assigns_r_uinj: "inj f ⟹ uinj ⟨f⟩⇩a"
  by (rel_simp, simp add: inj_eq)
    
lemma assigns_r_swap_uinj:
  "⟦ vwb_lens x; vwb_lens y; x ⨝ y ⟧ ⟹ uinj ((x,y) := (&y,&x))"
  by (metis assigns_r_uinj pr_var_def swap_usubst_inj)
lemma assign_unfold:
  "vwb_lens x ⟹ (x := v) = ($x´ =⇩u ⌈v⌉⇩< ∧ II↾⇩αx)"
  apply (rel_auto, auto simp add: comp_def)
  using vwb_lens.put_eq by fastforce
subsection ‹ Non-deterministic Assignment Laws ›
lemma nd_assign_comp:
  "x ⨝ y ⟹ x := * ;; y := * = x,y := *"
  apply (rel_auto) using lens_indep_comm by fastforce+
lemma nd_assign_assign:
  "⟦ vwb_lens x; x ♯ e ⟧ ⟹ x := * ;; x := e = x := e"
  by (rel_auto)
subsection ‹ Converse Laws ›
lemma convr_invol [simp]: "p⇧-⇧- = p"
  by pred_auto
lemma lit_convr [simp]: "«v»⇧- = «v»"
  by pred_auto
lemma uivar_convr [simp]:
  fixes x :: "('a ⟹ 'α)"
  shows "($x)⇧- = $x´"
  by pred_auto
lemma uovar_convr [simp]:
  fixes x :: "('a ⟹ 'α)"
  shows "($x´)⇧- = $x"
  by pred_auto
lemma uop_convr [simp]: "(uop f u)⇧- = uop f (u⇧-)"
  by (pred_auto)
lemma bop_convr [simp]: "(bop f u v)⇧- = bop f (u⇧-) (v⇧-)"
  by (pred_auto)
lemma eq_convr [simp]: "(p =⇩u q)⇧- = (p⇧- =⇩u q⇧-)"
  by (pred_auto)
lemma not_convr [simp]: "(¬ p)⇧- = (¬ p⇧-)"
  by (pred_auto)
lemma disj_convr [simp]: "(p ∨ q)⇧- = (q⇧- ∨ p⇧-)"
  by (pred_auto)
lemma conj_convr [simp]: "(p ∧ q)⇧- = (q⇧- ∧ p⇧-)"
  by (pred_auto)
lemma seqr_convr [simp]: "(p ;; q)⇧- = (q⇧- ;; p⇧-)"
  by (rel_auto)
lemma pre_convr [simp]: "⌈p⌉⇩<⇧- = ⌈p⌉⇩>"
  by (rel_auto)
lemma post_convr [simp]: "⌈p⌉⇩>⇧- = ⌈p⌉⇩<"
  by (rel_auto)
subsection ‹ Assertion and Assumption Laws ›
declare sublens_def [lens_defs del]
  
lemma assume_false: "[false]⇧⊤ = false"
  by (rel_auto)
  
lemma assume_true: "[true]⇧⊤ = II"
  by (rel_auto)
    
lemma assume_seq: "[b]⇧⊤ ;; [c]⇧⊤ = [(b ∧ c)]⇧⊤"
  by (rel_auto)
lemma assert_false: "{false}⇩⊥ = true"
  by (rel_auto)
lemma assert_true: "{true}⇩⊥ = II"
  by (rel_auto)
    
lemma assert_seq: "{b}⇩⊥ ;; {c}⇩⊥ = {(b ∧ c)}⇩⊥"
  by (rel_auto)
subsection ‹ Frame and Antiframe Laws ›
named_theorems frame
lemma frame_all [frame]: "Σ:[P] = P"
  by (rel_auto)
lemma frame_none [frame]: 
  "∅:[P] = (P ∧ II)"
  by (rel_auto)
lemma frame_commute:
  assumes "$y ♯ P" "$y´ ♯ P" "$x ♯ Q" "$x´ ♯ Q" "x ⨝ y" 
  shows "x:[P] ;; y:[Q] = y:[Q] ;; x:[P]"
  apply (insert assms)
  apply (rel_auto)
   apply (rename_tac s s' s⇩0)
   apply (subgoal_tac "(s ⊕⇩L s' on y) ⊕⇩L s⇩0 on x = s⇩0 ⊕⇩L s' on y")
    apply (metis lens_indep_get lens_indep_sym lens_override_def)
   apply (simp add: lens_indep.lens_put_comm lens_override_def)
  apply (rename_tac s s' s⇩0)
  apply (subgoal_tac "put⇘y⇙ (put⇘x⇙ s (get⇘x⇙ (put⇘x⇙ s⇩0 (get⇘x⇙ s')))) (get⇘y⇙ (put⇘y⇙ s (get⇘y⇙ s⇩0))) 
                      = put⇘x⇙ s⇩0 (get⇘x⇙ s')")
   apply (metis lens_indep_get lens_indep_sym)
  apply (metis lens_indep.lens_put_comm)
  done
lemma frame_contract_RID:
  assumes "vwb_lens x" "P is RID(x)" "x ⨝ y"
  shows "(x;y):[P] = y:[P]"
proof -
  from assms(1,3) have "(x;y):[RID(x)(P)] = y:[RID(x)(P)]"
    apply (rel_auto)
     apply (simp add: lens_indep.lens_put_comm)
    apply (metis (no_types) vwb_lens_wb wb_lens.get_put)
    done
  thus ?thesis
    by (simp add: Healthy_if assms)
qed
 
lemma frame_miracle [simp]:
  "x:[false] = false"
  by (rel_auto)
lemma frame_skip [simp]:
  "vwb_lens x ⟹ x:[II] = II"
  by (rel_auto)
    
lemma frame_assign_in [frame]:
  "⟦ vwb_lens a; x ⊆⇩L a ⟧ ⟹ a:[x := v] = x := v"
  by (rel_auto, simp_all add: lens_get_put_quasi_commute lens_put_of_quotient)
lemma frame_conj_true [frame]:
  "⟦ {$x,$x´} ♮ P; vwb_lens x ⟧ ⟹ (P ∧ x:[true]) = x:[P]"
  by (rel_auto)
    
lemma frame_is_assign [frame]:
  "vwb_lens x ⟹ x:[$x´ =⇩u ⌈v⌉⇩<] = x := v"
  by (rel_auto)
    
lemma frame_seq [frame]:
  "⟦ vwb_lens x; {$x,$x´} ♮ P; {$x,$x´} ♮ Q ⟧ ⟹ x:[P ;; Q] = x:[P] ;; x:[Q]"
  apply (rel_auto)
   apply (metis mwb_lens.put_put vwb_lens_mwb vwb_lens_wb wb_lens_def weak_lens.put_get)
  apply (metis mwb_lens.put_put vwb_lens_mwb)
  done
lemma frame_to_antiframe [frame]:
  "⟦ x ⨝ y; x +⇩L y = 1⇩L ⟧ ⟹ x:[P] = y:⟦P⟧"
  by (rel_auto, metis lens_indep_def, metis lens_indep_def surj_pair)
lemma rel_frext_miracle [frame]: 
  "a:[false]⇧+ = false"
  by (rel_auto)
    
lemma rel_frext_skip [frame]: 
  "vwb_lens a ⟹ a:[II]⇧+ = II"
  by (rel_auto)
lemma rel_frext_seq [frame]:
  "vwb_lens a ⟹ a:[P ;; Q]⇧+ = (a:[P]⇧+ ;; a:[Q]⇧+)"
  apply (rel_auto)
   apply (rename_tac s s' s⇩0)
   apply (rule_tac x="put⇘a⇙ s s⇩0" in exI)
   apply (auto)
  apply (metis mwb_lens.put_put vwb_lens_mwb)
  done
lemma rel_frext_assigns [frame]:
  "vwb_lens a ⟹ a:[⟨σ⟩⇩a]⇧+ = ⟨σ ⊕⇩s a⟩⇩a"
  by (rel_auto)
lemma rel_frext_rcond [frame]:
  "a:[P ◃ b ▹⇩r Q]⇧+ = (a:[P]⇧+ ◃ b ⊕⇩p a ▹⇩r a:[Q]⇧+)"
  by (rel_auto)
lemma rel_frext_commute: 
  "x ⨝ y ⟹ x:[P]⇧+ ;; y:[Q]⇧+ = y:[Q]⇧+ ;; x:[P]⇧+"
  apply (rel_auto)
   apply (rename_tac a c b)
   apply (subgoal_tac "⋀b a. get⇘y⇙ (put⇘x⇙ b a) = get⇘y⇙ b")
    apply (metis (no_types, opaque_lifting) lens_indep_comm lens_indep_get)
   apply (simp add: lens_indep.lens_put_irr2)
  apply (subgoal_tac "⋀b c. get⇘x⇙ (put⇘y⇙ b c) = get⇘x⇙ b")
   apply (subgoal_tac "⋀b a. get⇘y⇙ (put⇘x⇙ b a) = get⇘y⇙ b")
    apply (metis (mono_tags, lifting) lens_indep_comm)
   apply (simp_all add: lens_indep.lens_put_irr2)    
  done
    
lemma antiframe_disj [frame]: "(x:⟦P⟧ ∨ x:⟦Q⟧) = x:⟦P ∨ Q⟧"
  by (rel_auto)
lemma antiframe_seq [frame]:
  "⟦ vwb_lens x; $x´ ♯ P; $x ♯ Q ⟧  ⟹ (x:⟦P⟧ ;; x:⟦Q⟧) = x:⟦P ;; Q⟧"
  apply (rel_auto)
   apply (metis vwb_lens_wb wb_lens_def weak_lens.put_get)
  apply (metis vwb_lens_wb wb_lens.put_twice wb_lens_def weak_lens.put_get)
  done
  
lemma nameset_skip: "vwb_lens x ⟹ (ns x ∙ II) = II⇘x⇙"
  by (rel_auto, meson vwb_lens_wb wb_lens.get_put)
    
lemma nameset_skip_ra: "vwb_lens x ⟹ (ns x ∙ II⇘x⇙) = II⇘x⇙"
  by (rel_auto)
    
declare sublens_def [lens_defs]
    
subsection ‹ While Loop Laws ›
theorem while_unfold:
  "while b do P od = ((P ;; while b do P od) ◃ b ▹⇩r II)"
proof -
  have m:"mono (λX. (P ;; X) ◃ b ▹⇩r II)"
    by (auto intro: monoI seqr_mono cond_mono)
  have "(while b do P od) = (ν X ∙ (P ;; X) ◃ b ▹⇩r II)"
    by (simp add: while_top_def)
  also have "... = ((P ;; (ν X ∙ (P ;; X) ◃ b ▹⇩r II)) ◃ b ▹⇩r II)"
    by (subst lfp_unfold, simp_all add: m)
  also have "... = ((P ;; while b do P od) ◃ b ▹⇩r II)"
    by (simp add: while_top_def)
  finally show ?thesis .
qed
theorem while_false: "while false do P od = II"
  by (subst while_unfold, rel_auto)
theorem while_true: "while true do P od = false"
  apply (simp add: while_top_def alpha)
  apply (rule antisym)
   apply (simp_all)
  apply (rule lfp_lowerbound)
  apply (rel_auto)
  done
theorem while_bot_unfold:
  "while⇩⊥ b do P od = ((P ;; while⇩⊥ b do P od) ◃ b ▹⇩r II)"
proof -
  have m:"mono (λX. (P ;; X) ◃ b ▹⇩r II)"
    by (auto intro: monoI seqr_mono cond_mono)
  have "(while⇩⊥ b do P od) = (μ X ∙ (P ;; X) ◃ b ▹⇩r II)"
    by (simp add: while_bot_def)
  also have "... = ((P ;; (μ X ∙ (P ;; X) ◃ b ▹⇩r II)) ◃ b ▹⇩r II)"
    by (subst gfp_unfold, simp_all add: m)
  also have "... = ((P ;; while⇩⊥ b do P od) ◃ b ▹⇩r II)"
    by (simp add: while_bot_def)
  finally show ?thesis .
qed
theorem while_bot_false: "while⇩⊥ false do P od = II"
  by (simp add: while_bot_def mu_const alpha)
theorem while_bot_true: "while⇩⊥ true do P od = (μ X ∙ P ;; X)"
  by (simp add: while_bot_def alpha)
text ‹ An infinite loop with a feasible body corresponds to a program error (non-termination). ›
theorem while_infinite: "P ;; true⇩h = true ⟹ while⇩⊥ true do P od = true"
  apply (simp add: while_bot_true)
  apply (rule antisym)
   apply (simp)
  apply (rule gfp_upperbound)
  apply (simp)
  done
subsection ‹ Algebraic Properties ›