Theory Hoare_Total_EX2

theory Hoare_Total_EX2
imports Hoare
(* Author: Tobias Nipkow *)

theory Hoare_Total_EX2
imports Hoare

subsubsection "Hoare Logic for Total Correctness --- With Logical Variables"

text‹This is the standard set of rules that you find in many publications.
In the while-rule, a logical variable is needed to remember the pre-value
of the variant (an expression that decreases by one with each iteration).
In this theory, logical variables are modeled explicitly.
A simpler (but not quite as flexible) approach is found in theory ‹Hoare_Total_EX›:
pre and post-condition are connected via a universally quantified HOL variable.›

type_synonym lvname = string
type_synonym assn2 = "(lvname ⇒ nat) ⇒ state ⇒ bool"

definition hoare_tvalid :: "assn2 ⇒ com ⇒ assn2 ⇒ bool"
  ("⊨t {(1_)}/ (_)/ {(1_)}" 50) where
"⊨t {P}c{Q}  ⟷  (∀l s. P l s ⟶ (∃t. (c,s) ⇒ t ∧ Q l t))"

  hoaret :: "assn2 ⇒ com ⇒ assn2 ⇒ bool" ("⊢t ({(1_)}/ (_)/ {(1_)})" 50)

Skip:  "⊢t {P} SKIP {P}"  |

Assign:  "⊢t {λl s. P l (s[a/x])} x::=a {P}"  |

Seq: "⟦ ⊢t {P1} c1 {P2}; ⊢t {P2} c2 {P3} ⟧ ⟹ ⊢t {P1} c1;;c2 {P3}" |

If: "⟦ ⊢t {λl s. P l s ∧ bval b s} c1 {Q}; ⊢t {λl s. P l s ∧ ¬ bval b s} c2 {Q} ⟧
  ⟹ ⊢t {P} IF b THEN c1 ELSE c2 {Q}" |

  "⟦ ⊢t {λl. P (l(x := Suc(l(x))))} c {P};
     ∀l s. l x > 0 ∧ P l s ⟶ bval b s;
     ∀l s. l x = 0 ∧ P l s ⟶ ¬ bval b s ⟧
   ⟹ ⊢t {λl s. ∃n. P (l(x:=n)) s} WHILE b DO c {λl s. P (l(x := 0)) s}" |

conseq: "⟦ ∀l s. P' l s ⟶ P l s; ⊢t {P}c{Q}; ∀l s. Q l s ⟶ Q' l s  ⟧ ⟹
           ⊢t {P'}c{Q'}"

text‹Building in the consequence rule:›

lemma strengthen_pre:
  "⟦ ∀l s. P' l s ⟶ P l s;  ⊢t {P} c {Q} ⟧ ⟹ ⊢t {P'} c {Q}"
by (metis conseq)

lemma weaken_post:
  "⟦ ⊢t {P} c {Q};  ∀l s. Q l s ⟶ Q' l s ⟧ ⟹  ⊢t {P} c {Q'}"
by (metis conseq)

lemma Assign': "∀l s. P l s ⟶ Q l (s[a/x]) ⟹ ⊢t {P} x ::= a {Q}"
by (simp add: strengthen_pre[OF _ Assign])

text‹The soundness theorem:›

theorem hoaret_sound: "⊢t {P}c{Q}  ⟹  ⊨t {P}c{Q}"
proof(unfold hoare_tvalid_def, induction rule: hoaret.induct)
  case (While P x c b)
  have "⟦ l x = n; P l s ⟧ ⟹ ∃t. (WHILE b DO c, s) ⇒ t ∧ P (l(x := 0)) t" for n l s
  proof(induction "n" arbitrary: l s)
    case 0 thus ?case using While.hyps(3) WhileFalse
      by (metis fun_upd_triv)
    case Suc
    thus ?case using While.IH While.hyps(2) WhileTrue
      by (metis fun_upd_same fun_upd_triv fun_upd_upd zero_less_Suc)
  thus ?case by fastforce
  case If thus ?case by auto blast
qed fastforce+

definition wpt :: "com ⇒ assn2 ⇒ assn2" ("wpt") where
"wpt c Q  =  (λl s. ∃t. (c,s) ⇒ t ∧ Q l t)"

lemma [simp]: "wpt SKIP Q = Q"
by(auto intro!: ext simp: wpt_def)

lemma [simp]: "wpt (x ::= e) Q = (λl s. Q l (s(x := aval e s)))"
by(auto intro!: ext simp: wpt_def)

lemma wpt_Seq[simp]: "wpt (c1;;c2) Q = wpt c1 (wpt c2 Q)"
by (auto simp: wpt_def fun_eq_iff)

lemma [simp]:
 "wpt (IF b THEN c1 ELSE c2) Q = (λl s. wpt (if bval b s then c1 else c2) Q l s)"
by (auto simp: wpt_def fun_eq_iff)

text‹Function @{text wpw} computes the weakest precondition of a While-loop
that is unfolded a fixed number of times.›

fun wpw :: "bexp ⇒ com ⇒ nat ⇒ assn2 ⇒ assn2" where
"wpw b c 0 Q l s = (¬ bval b s ∧ Q l s)" |
"wpw b c (Suc n) Q l s = (bval b s ∧ (∃s'. (c,s) ⇒ s' ∧  wpw b c n Q l s'))"

lemma WHILE_Its:
  "(WHILE b DO c,s) ⇒ t ⟹ Q l t ⟹ ∃n. wpw b c n Q l s"
proof(induction "WHILE b DO c" s t arbitrary: l rule: big_step_induct)
  case WhileFalse thus ?case using wpw.simps(1) by blast
  case WhileTrue show ?case
    using wpw.simps(2) WhileTrue(1,2) WhileTrue(5)[OF WhileTrue(6)] by blast

definition support :: "assn2 ⇒ string set" where
"support P = {x. ∃l1 l2 s. (∀y. y ≠ x ⟶ l1 y = l2 y) ∧ P l1 s ≠ P l2 s}"

lemma support_wpt: "support (wpt c Q) ⊆ support Q"
by(simp add: support_def wpt_def) blast

lemma support_wpw0: "support (wpw b c n Q) ⊆ support Q"
proof(induction n)
  case 0 show ?case by (simp add: support_def) blast
  case Suc
  have 1: "support (λl s. A s ∧ B l s) ⊆ support B" for A B
    by(auto simp: support_def)
  have 2: "support (λl s. ∃s'. A s s' ∧ B l s') ⊆ support B" for A B
    by(auto simp: support_def) blast+
  from Suc 1 2 show ?case by simp (meson order_trans)

lemma support_wpw_Un:
  "support (%l. wpw b c (l x) Q l) ⊆ insert x (UN n. support(wpw b c n Q))"
using support_wpw0[of b c _ Q]
apply(auto simp add: support_def subset_iff)
apply metis
apply metis

lemma support_wpw: "support (%l. wpw b c (l x) Q l) ⊆ insert x (support Q)"
using support_wpw0[of b c _ Q] support_wpw_Un[of b c _ Q]
by blast

lemma assn2_lupd: "x ∉ support Q ⟹ Q (l(x:=n)) = Q l"
by(simp add: support_def fun_upd_other fun_eq_iff)
  (metis (no_types, lifting) fun_upd_def)

abbreviation "new Q ≡ SOME x. x ∉ support Q"

lemma wpw_lupd: "x ∉ support Q ⟹ wpw b c n Q (l(x := u)) = wpw b c n Q l"
by(induction n) (auto simp: assn2_lupd fun_eq_iff)

lemma wpt_is_pre: "finite(support Q) ⟹ ⊢t {wpt c Q} c {Q}"
proof (induction c arbitrary: Q)
  case SKIP show ?case by (auto intro:hoaret.Skip)
  case Assign show ?case by (auto intro:hoaret.Assign)
  case (Seq c1 c2) show ?case
    by (auto intro:hoaret.Seq Seq finite_subset[OF support_wpt])
  case If thus ?case by (auto intro:hoaret.If hoaret.conseq)
  case (While b c)
  let ?x = "new Q"
  have "∃x. x ∉ support Q" using While.prems infinite_UNIV_listI
    using ex_new_if_finite by blast
  hence [simp]: "?x ∉ support Q" by (rule someI_ex)
  let ?w = "WHILE b DO c"
  have fsup: "finite (support (λl. wpw b c (l x) Q l))" for x
    using finite_subset[OF support_wpw] While.prems by simp
  have c1: "∀l s. wpt ?w Q l s ⟶ (∃n. wpw b c n Q l s)"
    unfolding wpt_def by (metis WHILE_Its)
  have c2: "∀l s. l ?x = 0 ∧ wpw b c (l ?x) Q l s ⟶ ¬ bval b s"
    by (simp cong: conj_cong)
  have w2: "∀l s. 0 < l ?x ∧ wpw b c (l ?x) Q l s ⟶ bval b s"
    by (auto simp: gr0_conv_Suc cong: conj_cong)
  have 1: "∀l s. wpw b c (Suc(l ?x)) Q l s ⟶
                  (∃t. (c, s) ⇒ t ∧ wpw b c (l ?x) Q l t)"
    by simp
  have *: "⊢t {λl. wpw b c (Suc (l ?x)) Q l} c {λl. wpw b c (l ?x) Q l}"
    by(rule strengthen_pre[OF 1
          While.IH[of "λl. wpw b c (l ?x) Q l", unfolded wpt_def, OF fsup]])
  show ?case
  apply(rule conseq[OF _ hoaret.While[OF _ w2 c2]])
    apply (simp_all add: c1 * assn2_lupd wpw_lupd del: wpw.simps(2))

theorem hoaret_complete: "finite(support Q) ⟹ ⊨t {P}c{Q} ⟹ ⊢t {P}c{Q}"
apply(rule strengthen_pre[OF _ wpt_is_pre])
apply(auto simp: hoare_tvalid_def wpt_def)