Theory Healthiness

(*
 * Copyright (C) 2014 NICTA
 * All rights reserved.
 *)

(* Author: David Cock - David.Cock@nicta.com.au *)

section ‹Healthiness›

theory Healthiness imports Embedding begin

subsection ‹The Healthiness of the Embedding›

text ‹Healthiness is mostly derived by structural induction using
  the simplifier.  @{term Abort}, @{term Skip} and @{term Apply}
  form base cases.›

lemma healthy_wp_Abort:
  "healthy (wp Abort)"
proof(rule healthy_parts)
  fix b and P::"'a  real"
  assume nP: "nneg P" and bP: "bounded_by b P"
  thus "bounded_by b (wp Abort P)"
    unfolding wp_eval by(blast)
  show "nneg (wp Abort P)"
    unfolding wp_eval by(blast)
next
  fix P Q::"'a expect"
  show "wp Abort P  wp Abort Q"
    unfolding wp_eval by(blast)
next
  fix P and c and s::'a
  show "c * wp Abort P s = wp Abort (λs. c * P s) s"
    unfolding wp_eval by(auto)
qed

lemma nearly_healthy_wlp_Abort:
  "nearly_healthy (wlp Abort)"
proof(rule nearly_healthyI)
  fix P::"'s  real"
  show "unitary (wlp Abort P)"
    by(simp add:wp_eval)
next
  fix P Q :: "'s expect"
  assume "P  Q" and "unitary P" and "unitary Q"
  thus "wlp Abort P  wlp Abort Q"
    unfolding wp_eval by(blast)
qed

lemma healthy_wp_Skip:
  "healthy (wp Skip)"
  by(force intro!:healthy_parts simp:wp_eval)

lemma nearly_healthy_wlp_Skip:
  "nearly_healthy (wlp Skip)"
  by(auto simp:wp_eval)

lemma healthy_wp_Seq:
  fixes t::"'s prog" and u
  assumes ht: "healthy (wp t)" and hu: "healthy (wp u)"
  shows "healthy (wp (t ;; u))"
proof(rule healthy_parts, simp_all add:wp_eval)
  fix b and P::"'s  real"
  assume "bounded_by b P" and "nneg P"
  with hu have "bounded_by b (wp u P)" and "nneg (wp u P)" by(auto)
  with ht show "bounded_by b (wp t (wp u P))"
           and "nneg (wp t (wp u P))" by(auto)
next
  fix P::"'s  real" and Q
  assume "sound P" and "sound Q" and "P  Q"
  with hu have "sound (wp u P)" and "sound (wp u Q)"
    and "wp u P  wp u Q" by(auto)
  with ht show "wp t (wp u P)  wp t (wp u Q)" by(auto)
next
  fix P::"'s  real" and c::real and s
  assume pos: "0  c" and sP: "sound P"
  with ht and hu have "c * wp t (wp u P) s = wp t (λs. c * wp u P s) s"
    by(auto intro!:scalingD)
  also with hu and pos and sP have "... = wp t (wp u (λs. c * P s)) s"
    by(simp add:scalingD[OF healthy_scalingD])
  finally show "c * wp t (wp u P) s = wp t (wp u (λs. c * P s)) s" .
qed

lemma nearly_healthy_wlp_Seq:
  fixes t::"'s prog" and u
  assumes ht: "nearly_healthy (wlp t)" and hu: "nearly_healthy (wlp u)"
  shows "nearly_healthy (wlp (t ;; u))"
proof(rule nearly_healthyI, simp_all add:wp_eval)
  fix b and P::"'s  real"
  assume "unitary P"
  with hu have "unitary (wlp u P)" by(auto)
  with ht show "unitary (wlp t (wlp u P))" by(auto)
next
  fix P Q::"'s  real"
  assume "unitary P" and "unitary Q" and "P  Q"
  with hu have "unitary (wlp u P)" and "unitary (wlp u Q)"
    and "wlp u P  wlp u Q" by(auto)
  with ht show "wlp t (wlp u P)  wlp t (wlp u Q)" by(auto)
qed

lemma healthy_wp_PC:
  fixes f::"'s prog" 
  assumes hf: "healthy (wp f)" and hg: "healthy (wp g)"
      and uP: "unitary P"
  shows "healthy (wp (fP⇙⊕ g))"
proof(intro healthy_parts bounded_byI nnegI le_funI, simp_all add:wp_eval)
  fix b and Q::"'s  real" and s::'s
  assume nQ: "nneg Q" and bQ: "bounded_by b Q"

  txt ‹Non-negative:›
  from nQ and bQ and hf have "0  wp f Q s" by(auto)
  with uP have "0  P s * ..." by(auto intro:mult_nonneg_nonneg)
  moreover {
    from uP have "0  1 - P s"
      by auto
    with nQ and bQ and hg have "0  ... * wp g Q s"
      by (metis healthy_nnegD2 mult_nonneg_nonneg nneg_def)
  }
  ultimately show "0  P s * wp f Q s + (1 - P s) * wp g Q s"
    by(auto intro:mult_nonneg_nonneg)

  txt ‹Bounded:›
  from nQ bQ hf have "wp f Q s  b" by(auto)
  with uP nQ bQ hf have "P s * wp f Q s  P s * b"
    by(blast intro!:mult_mono)
  moreover {
    from nQ bQ hg uP
    have "wp g Q s  b" and "0  1 - P s"
      by auto
    with nQ bQ hg have "(1 - P s) * wp g Q s  (1 - P s) * b"
      by(blast intro!:mult_mono)
  }
  ultimately have "P s * wp f Q s + (1 - P s) * wp g Q s 
                   P s * b + (1 - P s) * b"
    by(blast intro:add_mono)
  also have "... = b" by(auto simp:algebra_simps)
  finally show "P s * wp f Q s + (1 - P s) * wp g Q s  b" .
next
  txt ‹Monotonic:›
  fix Q R::"'s  real" and s
  assume sQ: "sound Q" and sR: "sound R" and le: "Q  R"

  with hf have "wp f Q s  wp f R s" by(blast dest:mono_transD)
  with uP have "P s * wp f Q s  P s * wp f R s"
    by(auto intro:mult_left_mono)
  moreover {
    from sQ sR le hg
    have "wp g Q s  wp g R s" by(blast dest:mono_transD)
    moreover from uP have "0  1 - P s"
      by auto
    ultimately have "(1 - P s) * wp g Q s  (1 - P s) * wp g R s"
      by(auto intro:mult_left_mono)
  }
  ultimately show "P s * wp f Q s + (1 - P s) * wp g Q s 
                   P s * wp f R s + (1 - P s) * wp g R s" by(auto)
next
  txt ‹Scaling:›
  fix Q::"'s  real" and c::real and s::'s
  assume sQ: "sound Q" and pos: "0  c"
  have "c * (P s * wp f Q s + (1 - P s) * wp g Q s) =
        P s * (c * wp f Q s) + (1 - P s) * (c * wp g Q s)"
    by(simp add:distrib_left)
  also have "... = P s * wp f (λs.  c * Q s) s +
                   (1 - P s) * wp g (λs. c * Q s) s"
    using hf hg sQ pos
    by(simp add:scalingD[OF healthy_scalingD])
  finally show "c * (P s * wp f Q s + (1 - P s) * wp g Q s) =
                P s * wp f (λs. c * Q s) s + (1 - P s) * wp g (λs. c * Q s) s" .
qed

lemma nearly_healthy_wlp_PC:
  fixes f::"'s prog" 
  assumes hf: "nearly_healthy (wlp f)"
      and hg: "nearly_healthy (wlp g)"
      and uP: "unitary P"
  shows "nearly_healthy (wlp (fP⇙⊕ g))"
proof(intro nearly_healthyI unitaryI2 nnegI bounded_byI le_funI,
      simp_all add:wp_eval)
  fix Q::"'s expect" and s::'s
  assume uQ: "unitary Q"
  from uQ hf hg have utQ: "unitary (wlp f Q)" "unitary (wlp g Q)" by(auto)
  from uP have nnP: "0  P s" "0  1 - P s"
    by auto
  moreover from utQ have "0  wlp f Q s" "0  wlp g Q s" by(auto)
  ultimately show "0  P s * wlp f Q s + (1 - P s) * wlp g Q s"
    by(auto intro:add_nonneg_nonneg mult_nonneg_nonneg)

  from utQ have "wlp f Q s  1" "wlp g Q s  1" by(auto)
  with nnP have "P s * wlp f Q s + (1 - P s) * wlp g Q s  P s * 1 + (1 - P s) * 1"
    by(blast intro:add_mono mult_left_mono)
  thus "P s * wlp f Q s + (1 - P s) * wlp g Q s  1" by(simp)

  fix R::"'s expect"
  assume uR: "unitary R" and le: "Q  R"
  with uQ have "wlp f Q s  wlp f R s"
    by(auto intro:le_funD[OF nearly_healthy_monoD, OF hf])
  with nnP have "P s * wlp f Q s  P s * wlp f R s"
    by(auto intro:mult_left_mono)
  moreover {
    from uQ uR le have "wlp g Q s  wlp g R s"
      by(auto intro:le_funD[OF nearly_healthy_monoD, OF hg])
    with nnP have "(1 - P s) * wlp g Q s  (1 - P s) * wlp g R s"
      by(auto intro:mult_left_mono)
  }
  ultimately show "P s * wlp f Q s + (1 - P s) * wlp g Q s 
                   P s * wlp f R s + (1 - P s) * wlp g R s"
    by(auto)
qed

lemma healthy_wp_DC:
  fixes f::"'s prog"
  assumes hf: "healthy (wp f)" and hg: "healthy (wp g)"
  shows "healthy (wp (f  g))"
proof(intro healthy_parts bounded_byI nnegI le_funI, simp_all only:wp_eval)
  fix b and P::"'s  real" and s::'s
  assume nP: "nneg P" and bP: "bounded_by b P"

  with hf have "bounded_by b (wp f P)" by(auto)
  hence "wp f P s  b" by(blast)
  thus "min (wp f P s) (wp g P s)  b" by(auto)

  from nP bP assms show "0  min (wp f P s) (wp g P s)" by(auto)
next
  fix P::"'s  real" and Q and s::'s
  from assms have mf: "mono_trans (wp f)" and mg: "mono_trans (wp g)" by(auto)
  assume sP: "sound P" and sQ: "sound Q" and le: "P  Q"
  hence "wp f P s  wp f Q s" and "wp g P s  wp g Q s"
    by(auto intro:le_funD[OF mono_transD[OF mf]] le_funD[OF mono_transD[OF mg]])
  thus "min (wp f P s) (wp g P s)  min (wp f Q s) (wp g Q s)" by(auto)
next
  fix P::"'s  real" and c::real and s::'s
  assume sP: "sound P" and pos: "0  c"
  from assms have sf: "scaling (wp f)" and sg: "scaling (wp g)" by(auto)
  from pos have "c * min (wp f P s) (wp g P s) =
                 min (c * wp f P s) (c * wp g P s)"
    by(simp add:min_distrib)
  also from sP and pos
  have "... = min (wp f (λs. c * P s) s) (wp g (λs. c * P s) s)"
    by(simp add:scalingD[OF sf] scalingD[OF sg])
  finally show "c * min (wp f P s) (wp g P s) =
                min (wp f (λs. c * P s) s) (wp g (λs. c * P s) s)" .
qed

lemma nearly_healthy_wlp_DC:
  fixes f::"'s prog"
  assumes hf: "nearly_healthy (wlp f)"
      and hg: "nearly_healthy (wlp g)"
  shows "nearly_healthy (wlp (f  g))"
proof(intro nearly_healthyI bounded_byI nnegI le_funI unitaryI2,
      simp_all add:wp_eval, safe)
  fix P::"'s  real" and s::'s
  assume uP: "unitary P"
  with hf hg have utP: "unitary (wlp f P)" "unitary (wlp g P)" by(auto)
  thus "0  wlp f P s" "0  wlp g P s" by(auto)

  have "min (wlp f P s) (wlp g P s)  wlp f P s" by(auto)
  also from utP have "...  1" by(auto)
  finally show "min (wlp f P s) (wlp g P s)  1" .

  fix Q::"'s  real"
  assume uQ: "unitary Q" and le: "P  Q"
  have "min (wlp f P s) (wlp g P s)  wlp f P s" by(auto)
  also from uP uQ le have "...  wlp f Q s"
    by(auto intro:le_funD[OF nearly_healthy_monoD, OF hf])
  finally show "min (wlp f P s) (wlp g P s)  wlp f Q s" .

  have "min (wlp f P s) (wlp g P s)  wlp g P s" by(auto)
  also from uP uQ le have "...  wlp g Q s"
    by(auto intro:le_funD[OF nearly_healthy_monoD, OF hg])
  finally show "min (wlp f P s) (wlp g P s)  wlp g Q s" .
qed

lemma healthy_wp_AC:
  fixes f::"'s prog"
  assumes hf: "healthy (wp f)" and hg: "healthy (wp g)"
  shows "healthy (wp (f  g))"
proof(intro healthy_parts bounded_byI nnegI le_funI, simp_all only:wp_eval)
  fix b and P::"'s  real" and s::'s
  assume nP: "nneg P" and bP: "bounded_by b P"

  with hf have "bounded_by b (wp f P)" by(auto)
  hence "wp f P s  b" by(blast)
  moreover {
    from bP nP hg have "bounded_by b (wp g P)" by(auto)
    hence "wp g P s  b" by(blast)
  }
  ultimately show "max (wp f P s) (wp g P s)  b" by(auto)

  from nP bP assms have "0  wp f P s" by(auto)
  thus "0  max (wp f P s) (wp g P s)" by(auto)
next
  fix P::"'s  real" and Q and s::'s
  from assms have mf: "mono_trans (wp f)" and mg: "mono_trans (wp g)" by(auto)
  assume sP: "sound P" and sQ: "sound Q" and le: "P  Q"
  hence "wp f P s  wp f Q s" and "wp g P s  wp g Q s"
    by(auto intro:le_funD[OF mono_transD, OF mf] le_funD[OF mono_transD, OF mg])
  thus "max (wp f P s) (wp g P s)  max (wp f Q s) (wp g Q s)" by(auto)
next
  fix P::"'s  real" and c::real and s::'s
  assume sP: "sound P" and pos: "0  c"
  from assms have sf: "scaling (wp f)" and sg: "scaling (wp g)" by(auto)
  from pos have "c * max (wp f P s) (wp g P s) =
                 max (c * wp f P s) (c * wp g P s)"
    by(simp add:max_distrib)
  also from sP and pos
  have "... = max (wp f (λs. c * P s) s) (wp g (λs. c * P s) s)"
    by(simp add:scalingD[OF sf] scalingD[OF sg])
  finally show "c * max (wp f P s) (wp g P s) =
                max (wp f (λs. c * P s) s) (wp g (λs. c * P s) s)" .
qed

lemma nearly_healthy_wlp_AC:
  fixes f::"'s prog"
  assumes hf: "nearly_healthy (wlp f)"
      and hg: "nearly_healthy (wlp g)"
  shows "nearly_healthy (wlp (f  g))"
proof(intro nearly_healthyI bounded_byI nnegI unitaryI2 le_funI, simp_all only:wp_eval)
  fix b and P::"'s  real" and s::'s
  assume uP: "unitary P"

  with hf have "wlp f P s  1" by(auto)
  moreover from uP hg have "unitary (wlp g P)" by(auto)
  hence "wlp g P s  1" by(auto)
  ultimately show "max (wlp f P s) (wlp g P s)  1" by(auto)

  from uP hf have "unitary (wlp f P)" by(auto)
  hence "0  wlp f P s" by(auto)
  thus "0  max (wlp f P s) (wlp g P s)" by(auto)
next
  fix P::"'s  real" and Q and s::'s
  assume uP: "unitary P" and uQ: "unitary Q" and le: "P  Q"
  hence "wlp f P s  wlp f Q s" and "wlp g P s  wlp g Q s"
    by(auto intro:le_funD[OF nearly_healthy_monoD, OF hf]
                  le_funD[OF nearly_healthy_monoD, OF hg])
  thus "max (wlp f P s) (wlp g P s)  max (wlp f Q s) (wlp g Q s)" by(auto)
qed

lemma healthy_wp_Embed:
  "healthy t  healthy (wp (Embed t))"
  unfolding wp_def Embed_def by(simp)

lemma nearly_healthy_wlp_Embed:
  "nearly_healthy t  nearly_healthy (wlp (Embed t))"
  unfolding wlp_def Embed_def by(simp)

lemma healthy_wp_repeat:
  assumes h_a: "healthy (wp a)"
  shows "healthy (wp (repeat n a))" (is "?X n")
proof(induct n)
  show "?X 0" by(auto simp:wp_eval)
next
  fix n assume IH: "?X n"
  thus "?X (Suc n)" by(simp add:healthy_wp_Seq h_a)
qed

lemma nearly_healthy_wlp_repeat:
  assumes h_a: "nearly_healthy (wlp a)"
  shows "nearly_healthy (wlp (repeat n a))" (is "?X n")
proof(induct n)
  show "?X 0" by(simp add:wp_eval)
next
  fix n assume IH: "?X n"
  thus "?X (Suc n)" by(simp add:nearly_healthy_wlp_Seq h_a)
qed

lemma healthy_wp_SetDC:
  fixes prog::"'b  'a prog" and S::"'a  'b set"
  assumes healthy:  "x s. x  S s  healthy (wp (prog x))"
      and nonempty: "s. x. x  S s"
  shows "healthy (wp (SetDC prog S))" (is "healthy ?T")
proof(intro healthy_parts bounded_byI nnegI le_funI, simp_all only:wp_eval)
  fix b and P::"'a  real" and s::'a
  assume bP: "bounded_by b P" and nP: "nneg P"
  hence sP: "sound P" by(auto)

  from nonempty obtain x where xin: "x  (λa. wp (prog a) P s) ` S s" by(blast)
  moreover from sP and healthy
  have "x(λa. wp (prog a) P s) ` S s. 0  x" by(auto)
  ultimately have "Inf ((λa. wp (prog a) P s) ` S s)  x"
    by(intro cInf_lower bdd_belowI, auto)
  also from xin and healthy and sP and bP have "x  b" by(blast)
  finally show "Inf ((λa. wp (prog a) P s) ` S s)  b" .

  from xin and sP and healthy
  show "0  Inf ((λa. wp (prog a) P s) ` S s)" by(blast intro:cInf_greatest)
next
  fix P::"'a  real" and Q and s::'a
  assume sP: "sound P" and sQ: "sound Q" and le: "P  Q"

  from nonempty obtain x where xin: "x  (λa. wp (prog a) P s) ` S s" by(blast)
  moreover from sP and healthy
  have "x(λa. wp (prog a) P s) ` S s. 0  x" by(auto)
  moreover
  have "x(λa. wp (prog a) Q s) ` S s. y(λa. wp (prog a) P s) ` S s. y  x"
  proof(rule ballI, clarify, rule bexI)
    fix x and a assume ain: "a  S s"
    with healthy and sP and sQ and le show "wp (prog a) P s  wp (prog a) Q s"
      by(auto dest:mono_transD[OF healthy_monoD])
    from ain show "wp (prog a) P s  (λa. wp (prog a) P s) ` S s" by(simp)
  qed
  ultimately
  show "Inf ((λa. wp (prog a) P s) ` S s)  Inf ((λa. wp (prog a) Q s) ` S s)"
    by(intro cInf_mono, blast+)
next
  fix P::"'a  real" and c::real and s::'a
  assume sP: "sound P" and pos: "0  c"
  from nonempty obtain x where xin: "x  (λa. wp (prog a) P s) ` S s" by(blast)
  have "c * Inf ((λa. wp (prog a) P s) ` S s) =
        Inf ((*) c ` ((λa. wp (prog a) P s) ` S s))" (is "?U = ?V")
  proof(rule antisym)
    show "?U  ?V"
    proof(rule cInf_greatest)
      from nonempty show "(*) c ` (λa. wp (prog a) P s) ` S s  {}" by(auto)
      fix x assume "x  (*) c ` (λa. wp (prog a) P s) ` S s"
      then obtain y where yin: "y  (λa. wp (prog a) P s) ` S s" and rwx: "x = c * y" by(auto)
      have "Inf ((λa. wp (prog a) P s) ` S s)  y"
      proof(intro cInf_lower[OF yin] bdd_belowI)
        fix z assume zin: "z  (λa. wp (prog a) P s) ` S s"
        then obtain a where "a  S s" and "z = wp (prog a) P s" by(auto)
        with sP show "0  z" by(auto dest:healthy)
      qed
      with pos rwx show "c * Inf ((λa. wp (prog a) P s) ` S s)  x" by(auto intro:mult_left_mono)
    qed
    show "?V  ?U"
    proof(cases)
      assume cz: "c = 0"
      moreover {
        from nonempty obtain c where "c  S s" by(auto)
        hence "x. xaS s. x = wp (prog xa) P s" by(auto)
      }
      ultimately show ?thesis by(simp add:image_def)
    next
      assume "c  0"
      from nonempty have "S s  {}" by blast
      then have "inverse c * (INF xS s. c * wp (prog x) P s)  (INF aS s. wp (prog a) P s)"
      proof (rule cINF_greatest)
        fix x
        assume "x  S s"
        have "bdd_below ((λx. c * wp (prog x) P s) ` S s)"
        proof (rule bdd_belowI [of _ 0])
          fix z
          assume "z  (λx. c * wp (prog x) P s) ` S s"
          then obtain b where "b  S s" and rwz: "z = c * wp (prog b) P s" by auto
          with sP have "0  wp (prog b) P s" by (auto dest: healthy)
          with pos show "0  z" by (auto simp: rwz intro: mult_nonneg_nonneg)
        qed
        then have "(INF xS s. c * wp (prog x) P s)  c * wp (prog x) P s"
        using x  S s by (rule cINF_lower)
        with c  0 show "inverse c * (INF xS s. c * wp (prog x) P s)  wp (prog x) P s"
          by (simp add: mult_div_mono_left pos)
      qed
      with c  0 have "inverse c * ?V  inverse c * ?U"
        by (simp add: mult.assoc [symmetric] image_comp)
      with pos have "c * (inverse c * ?V)  c * (inverse c * ?U)"
        by(auto intro:mult_left_mono)
      with c  0 show ?thesis by (simp add:mult.assoc [symmetric])
    qed
  qed
  also have "... = Inf ((λa. c * wp (prog a) P s) ` S s)"
    by (simp add: image_comp)
  also from sP and pos have "... = Inf ((λa. wp (prog a) (λs. c * P s) s) ` S s)"
    by(simp add:scalingD[OF healthy_scalingD, OF healthy] cong:image_cong)
  finally show "c * Inf ((λa. wp (prog a) P s) ` S s) =
                Inf ((λa. wp (prog a) (λs. c * P s) s) ` S s)" .
qed

lemma nearly_healthy_wlp_SetDC:
  fixes prog::"'b  'a prog" and S::"'a  'b set"
  assumes healthy:  "x s. x  S s  nearly_healthy (wlp (prog x))"
      and nonempty: "s. x. x  S s"
  shows "nearly_healthy (wlp (SetDC prog S))" (is "nearly_healthy ?T")
proof(intro nearly_healthyI unitaryI2 bounded_byI nnegI le_funI, simp_all only:wp_eval)
  fix b and P::"'a  real" and s::'a
  assume uP: "unitary P"

  from nonempty obtain x where xin: "x  (λa. wlp (prog a) P s) ` S s" by(blast)
  moreover {
    from uP healthy
    have "x(λa. wlp (prog a) P) ` S s. unitary x" by(auto)
    hence "x(λa. wlp (prog a) P) ` S s. 0  x s" by(auto)
    hence "y(λa. wlp (prog a) P s) ` S s. 0  y" by(auto)
  }
  ultimately have "Inf ((λa. wlp (prog a) P s) ` S s)  x" by(intro cInf_lower bdd_belowI, auto)
  also from xin healthy uP have "x  1" by(blast)
  finally show "Inf ((λa. wlp (prog a) P s) ` S s)  1" .

  from xin uP healthy
  show "0  Inf ((λa. wlp (prog a) P s) ` S s)"
    by(blast dest!:unitary_sound[OF nearly_healthy_unitaryD[OF _ uP]]
             intro:cInf_greatest)
next
  fix P::"'a  real" and Q and s::'a
  assume uP: "unitary P" and uQ: "unitary Q" and le: "P  Q"

  from nonempty obtain x where xin: "x  (λa. wlp (prog a) P s) ` S s" by(blast)
  moreover {
    from uP healthy
    have "x(λa. wlp (prog a) P) ` S s. unitary x" by(auto)
    hence "x(λa. wlp (prog a) P) ` S s. 0  x s" by(auto)
    hence "y(λa. wlp (prog a) P s) ` S s. 0  y" by(auto)
  }
  moreover
  have "x(λa. wlp (prog a) Q s) ` S s. y(λa. wlp (prog a) P s) ` S s. y  x"
  proof(rule ballI, clarify, rule bexI)
    fix x and a assume ain: "a  S s"
    from uP uQ le show "wlp (prog a) P s  wlp (prog a) Q s"
      by(auto intro:le_funD[OF nearly_healthy_monoD[OF healthy, OF ain]])
    from ain show "wlp (prog a) P s  (λa. wlp (prog a) P s) ` S s" by(simp)
  qed
  ultimately
  show "Inf ((λa. wlp (prog a) P s) ` S s)  Inf ((λa. wlp (prog a) Q s) ` S s)"
    by(intro cInf_mono, blast+)
qed

lemma healthy_wp_SetPC:
  fixes p::"'s  'a  real"
  and   f::"'a  's prog"
  assumes healthy: "a s. a  supp (p s)  healthy (wp (f a))"
      and sound: "s. sound (p s)"
      and sub_dist: "s. (asupp (p s). p s a)  1"
  shows "healthy (wp (SetPC f p))" (is "healthy ?X")
proof(intro healthy_parts bounded_byI nnegI le_funI, simp_all add:wp_eval)
  fix b and P::"'s  real" and s::'s
  assume bP: "bounded_by b P" and nP: "nneg P"
  hence sP: "sound P" by(auto)

  from sP and bP and healthy have "a s. a  supp (p s)  wp (f a) P s  b"
    by(blast dest:healthy_bounded_byD)
  with sound have "(asupp (p s). p s a * wp (f a) P s)  (asupp (p s). p s a * b)"
    by(blast intro:sum_mono mult_left_mono)
  also have "... = (asupp (p s). p s a) * b"
    by(simp add:sum_distrib_right)
  also {
    from bP and nP have "0  b" by(blast)
    with sub_dist have "(asupp (p s). p s a) * b  1 * b"
      by(rule mult_right_mono)
  }
  also have "1 * b = b" by(simp)
  finally show "(asupp (p s). p s a * wp (f a) P s)  b" .

  show "0  (asupp (p s). p s a * wp (f a) P s)"
  proof(rule sum_nonneg [OF mult_nonneg_nonneg])
    fix x
    from sound show "0  p s x" by(blast)
    assume "x  supp (p s)" with sP and healthy
    show "0  wp (f x) P s" by(blast)
  qed
next
  fix P::"'s  real" and Q::"'s  real" and s
  assume s_P: "sound P" and s_Q: "sound Q" and ent: "P  Q"
  with healthy have "a. a  supp (p s)  wp (f a) P s  wp (f a) Q s"
    by(blast)
  with sound show "(asupp (p s). p s a * wp (f a) P s) 
                   (asupp (p s). p s a * wp (f a) Q s)"
    by(blast intro:sum_mono mult_left_mono)
next
  fix P::"'s  real" and c::real and s::'s
  assume sound: "sound P" and pos: "0  c"
  have "c * (asupp (p s). p s a * wp (f a) P s) =
        (asupp (p s). p s a * (c * wp (f a) P s))"
       (is "?A = ?B")
    by(simp add:sum_distrib_left ac_simps)
  also from sound and pos and healthy
  have "... = (asupp (p s). p s a * wp (f a) (λs. c * P s) s)"
    by(auto simp:scalingD[OF healthy_scalingD])
  finally show "?A = ..." .
qed

lemma nearly_healthy_wlp_SetPC:
  fixes p::"'s  'a  real"
  and   f::"'a  's prog"
  assumes healthy: "a s. a  supp (p s)  nearly_healthy (wlp (f a))"
      and sound: "s. sound (p s)"
      and sub_dist: "s. (asupp (p s). p s a)  1"
  shows "nearly_healthy (wlp (SetPC f p))" (is "nearly_healthy ?X")
proof(intro nearly_healthyI unitaryI2 bounded_byI nnegI le_funI, simp_all only:wp_eval)
  fix b and P::"'s  real" and s::'s
  assume uP: "unitary P"

  from uP healthy have "a. a  supp (p s)  unitary (wlp (f a) P)" by(auto)
  hence "a. a  supp (p s)  wlp (f a) P s  1" by(auto)
  with sound have "(asupp (p s). p s a * wlp (f a) P s)  (asupp (p s). p s a * 1)"
    by(blast intro:sum_mono mult_left_mono)
  also have "... = (asupp (p s). p s a)"
    by(simp add:sum_distrib_right)
  also note sub_dist
  finally show "(asupp (p s). p s a * wlp (f a) P s)  1" .
  show "0  (asupp (p s). p s a * wlp (f a) P s)"
  proof(rule sum_nonneg [OF mult_nonneg_nonneg])
    fix x
    from sound show "0  p s x" by(blast)
    assume "x  supp (p s)" with uP healthy
    show "0  wlp (f x) P s" by(blast)
  qed
next
  fix P::"'s expect" and Q::"'s expect" and s
  assume uP: "unitary P" and uQ: "unitary Q" and le: "P  Q"
  hence "a. a  supp (p s)  wlp (f a) P s  wlp (f a) Q s"
    by(blast intro:le_funD[OF nearly_healthy_monoD, OF healthy])
  with sound show "(asupp (p s). p s a * wlp (f a) P s) 
                   (asupp (p s). p s a * wlp (f a) Q s)"
    by(blast intro:sum_mono mult_left_mono)
qed

lemma healthy_wp_Apply:
  "healthy (wp (Apply f))"
  unfolding Apply_def wp_def by(blast)

lemma nearly_healthy_wlp_Apply:
  "nearly_healthy (wlp (Apply f))"
  by(intro nearly_healthyI unitaryI2 nnegI bounded_byI, auto simp:o_def wp_eval)

lemma healthy_wp_Bind:
  fixes f::"'s  'a"
  assumes hsub: "s. healthy (wp (p (f s)))"
  shows "healthy (wp (Bind f p))"
proof(intro healthy_parts nnegI bounded_byI le_funI, simp_all only:wp_eval)
  fix b and P::"'s expect" and s::'s
  assume bP: "bounded_by b P" and nP: "nneg P"
  with hsub have "bounded_by b (wp (p (f s)) P)" by(auto)
  thus "wp (p (f s)) P s  b" by(auto)
  from bP nP hsub have "nneg (wp (p (f s)) P)" by(auto)
  thus "0  wp (p (f s)) P s" by(auto)
next
  fix P Q::"'s expect" and s::'s
  assume "sound P" "sound Q" "P  Q"
  thus "wp (p (f s)) P s  wp (p (f s)) Q s"
    by(rule le_funD[OF mono_transD, OF healthy_monoD, OF hsub])
next
  fix P::"'s expect" and c::real and s::'s
  assume "sound P" and "0  c"
  thus "c * wp (p (f s)) P s = wp (p (f s)) (λs. c * P s) s"
    by(simp add:scalingD[OF healthy_scalingD, OF hsub])
qed

lemma nearly_healthy_wlp_Bind:
  fixes f::"'s  'a"
  assumes hsub: "s. nearly_healthy (wlp (p (f s)))"
  shows "nearly_healthy (wlp (Bind f p))"
proof(intro nearly_healthyI unitaryI2 nnegI bounded_byI le_funI, simp_all only:wp_eval)
  fix P::"'s expect" and s::'s assume uP: "unitary P"
  with hsub have "unitary (wlp (p (f s)) P)" by(auto)
  thus "0  wlp (p (f s)) P s" "wlp (p (f s)) P s  1" by(auto)

  fix Q::"'s expect"
  assume "unitary Q" "P  Q"
  with uP show "wlp (p (f s)) P s  wlp (p (f s)) Q s"
    by(blast intro:le_funD[OF nearly_healthy_monoD, OF hsub])
qed

subsection ‹Healthiness for Loops›

lemma wp_loop_step_mono:
  fixes t u::"'s trans"
  assumes hb: "healthy (wp body)"
      and le: "le_trans t u"
      and ht: "P. sound P  sound (t P)"
      and hu: "P. sound P  sound (u P)"
  shows "le_trans (wp (body ;; Embed t« G »⇙⊕ Skip))
                  (wp (body ;; Embed u« G »⇙⊕ Skip))"
proof(intro le_transI le_funI, simp add:wp_eval)
  fix P::"'s expect" and s::'s
  assume sP: "sound P"
  with le have "t P  u P" by(auto)
  moreover from sP ht hu have "sound (t P)" "sound (u P)" by(auto)
  ultimately have "wp body (t P) s  wp body (u P) s"
    by(auto intro:le_funD[OF mono_transD, OF healthy_monoD, OF hb])
  thus "«G» s * wp body (t P) s  «G» s * wp body (u P) s "
    by(auto intro:mult_left_mono)
qed

lemma wlp_loop_step_mono:
  fixes t u::"'s trans"
  assumes mb: "nearly_healthy (wlp body)"
      and le: "le_utrans t u"
      and ht: "P. unitary P  unitary (t P)"
      and hu: "P. unitary P  unitary (u P)"
  shows "le_utrans (wlp (body ;; Embed t« G »⇙⊕ Skip))
                   (wlp (body ;; Embed u« G »⇙⊕ Skip))"
proof(intro le_utransI le_funI, simp add:wp_eval)
  fix P::"'s expect" and s::'s
  assume uP: "unitary P"
  with le have "t P  u P" by(auto)
  moreover from uP ht hu have "unitary (t P)" "unitary (u P)" by(auto)
  ultimately have "wlp body (t P) s  wlp body (u P) s"
    by(rule le_funD[OF nearly_healthy_monoD[OF mb]])
  thus "«G» s * wlp body (t P) s  «G» s * wlp body (u P) s "
    by(auto intro:mult_left_mono)
qed

text ‹For each sound expectation, we have a pre fixed point of the loop body.
This lets us use the relevant fixed-point lemmas.›
lemma lfp_loop_fp:
  assumes hb: "healthy (wp body)"
      and sP: "sound P"
  shows "λs. «G» s * wp body (λs. bound_of P) s + «𝒩 G» s * P s  λs. bound_of P"
proof(rule le_funI)
  fix s
  from sP have "sound (λs. bound_of P)" by(auto)
  moreover hence "bounded_by (bound_of P) (λs. bound_of P)" by(auto)
  ultimately have "bounded_by (bound_of P) (wp body (λs. bound_of P))"
    using hb by(auto)
  hence "wp body (λs. bound_of P) s  bound_of P" by(auto)
  moreover from sP have "P s  bound_of P" by(auto)
  ultimately have "«G» s * wp body (λa. bound_of P) s + (1 - «G» s) * P s 
                   «G» s * bound_of P + (1 - «G» s) * bound_of P"
    by(blast intro:add_mono mult_left_mono)
  thus "«G» s * wp body (λa. bound_of P) s + «𝒩 G» s * P s  bound_of P"
    by(simp add:algebra_simps negate_embed)
qed

lemma lfp_loop_greatest:
  fixes P::"'s expect"
  assumes lb: "R. λs. «G» s * wp body R s + «𝒩 G» s * P s  R  sound R  Q  R"
      and hb: "healthy (wp body)"
      and sP: "sound P"
      and sQ: "sound Q"
  shows "Q  lfp_exp (λQ s. «G» s * wp body Q s + «𝒩 G» s * P s)"
  using sP by(auto intro!:lfp_exp_greatest[OF lb sQ] sP lfp_loop_fp hb)

lemma lfp_loop_sound:
  fixes P::"'s expect"
  assumes hb: "healthy (wp body)"
      and sP: "sound P"
  shows "sound (lfp_exp (λQ s. «G» s * wp body Q s + «𝒩 G» s * P s))"
  using assms by(auto intro!:lfp_exp_sound lfp_loop_fp)

lemma wlp_loop_step_unitary:
  fixes t u::"'s trans"
  assumes hb: "nearly_healthy (wlp body)"
      and ht: "P. unitary P  unitary (t P)"
      and uP: "unitary P"
  shows "unitary (wlp (body ;; Embed t« G »⇙⊕ Skip) P)"
proof(intro unitaryI2 nnegI bounded_byI, simp_all add:wp_eval)
  fix s::'s
  from ht uP have utP: "unitary (t P)" by(auto)
  with hb have "unitary (wlp body (t P))" by(auto)
  hence "0  wlp body (t P) s" by(auto)
  with uP show "0  « G » s * wlp body (t P) s + (1 - « G » s) * P s"
    by(auto intro!:add_nonneg_nonneg mult_nonneg_nonneg)
  from ht uP have "bounded_by 1 (t P)" by(auto)
  with utP hb have "bounded_by 1 (wlp body (t P))" by(auto)
  hence "wlp body (t P) s  1" by(auto)
  with uP have "«G» s * wlp body (t P) s + (1 - «G» s) * P s  «G» s * 1 + (1 - «G» s) * 1"
    by(blast intro:add_mono mult_left_mono)
  also have "... = 1" by(simp)
  finally show "«G» s * wlp body (t P) s + (1 - «G» s) * P s  1" .
qed

lemma wp_loop_step_sound:
  fixes t u::"'s trans"
  assumes hb: "healthy (wp body)"
      and ht: "P. sound P  sound (t P)"
      and sP: "sound P"
  shows "sound (wp (body ;; Embed t« G »⇙⊕ Skip) P)"
proof(intro soundI2 nnegI bounded_byI, simp_all add:wp_eval)
  fix s::'s
  from ht sP have stP: "sound (t P)" by(auto)
  with hb have "0  wp body (t P) s" by(auto)
  with sP show "0  « G » s * wp body (t P) s + (1 - « G » s) * P s"
    by(auto intro!:add_nonneg_nonneg mult_nonneg_nonneg)

  from ht sP have "sound (t P)" by(auto)
  moreover hence "bounded_by (bound_of (t P)) (t P)" by(auto)
  ultimately have "wp body (t P) s  bound_of (t P)" using hb by(auto)
  hence "wp body (t P) s  max (bound_of P) (bound_of (t P))" by(auto)
  moreover {
    from sP have "P s  bound_of P" by(auto)
    hence "P s  max (bound_of P) (bound_of (t P))" by(auto)
  }
  ultimately
  have "«G» s * wp body (t P) s + (1 - «G» s) * P s 
        «G» s * max (bound_of P) (bound_of (t P)) +
        (1 - «G» s) * max (bound_of P) (bound_of (t P))"
    by(blast intro:add_mono mult_left_mono)
  also have "... = max (bound_of P) (bound_of (t P))" by(simp add:algebra_simps)
  finally show "«G» s * wp body (t P) s + (1 - «G» s) * P s 
                max (bound_of P) (bound_of (t P))" .
qed

text ‹This gives the equivalence with the alternative definition for
  loopscitep‹\S7, p.~198, footnote 23› in "McIver_M_04".›

lemma wlp_Loop1:
  fixes body :: "'s prog"
  assumes unitary: "unitary P"
      and healthy: "nearly_healthy (wlp body)"
  shows "wlp (do G  body od) P =
   gfp_exp (λQ s. «G» s * wlp body Q s + «𝒩 G» s * P s)"
  (is "?X = gfp_exp (?Y P)")
proof -
  let "?Z u" = "(body ;; Embed u« G »⇙⊕ Skip)"
  show ?thesis
  proof(simp only: wp_eval, intro gfp_pulldown assms le_funI)
    fix u P
    show "wlp (?Z u) P = ?Y P (u P)" by(simp add:wp_eval negate_embed)
  next
    fix t::"'s trans" and P::"'s expect"
    assume ut: "Q. unitary Q  unitary (t Q)" and uP: "unitary P"
    thus "unitary (wlp (?Z t) P)"
      by(rule wlp_loop_step_unitary[OF healthy])
  next
    fix P Q::"'s expect"
    assume uP: "unitary P" and uQ: "unitary Q"
    show "unitary (λa. « G » a * wlp body Q a + « 𝒩 G » a * P a)"
    proof(intro unitaryI2 nnegI bounded_byI)
      fix s::'s
      from healthy uQ
      have "unitary (wlp body Q)" by(auto)
      hence "0  wlp body Q s" by(auto)
      with uP show "0  «G» s * wlp body Q s + «𝒩 G» s * P s"
        by(auto intro!:add_nonneg_nonneg mult_nonneg_nonneg)

      from healthy uQ have "bounded_by 1 (wlp body Q)" by(auto)
      with uP have "«G» s * wlp body Q s + (1 - «G» s) * P s  «G» s * 1 + (1 - «G» s) * 1"
        by(blast intro:add_mono mult_left_mono)
      also have "... = 1" by(simp)
      finally show "«G» s * wlp body Q s + «𝒩 G» s * P s  1"
        by(simp add:negate_embed)
    qed
  next
    fix P Q R::"'s expect" and s::'s
    assume uP: "unitary P" and uQ: "unitary Q" and uR: "unitary R"
       and le: "Q  R"
    hence "wlp body Q s  wlp body R s"
      by(blast intro:le_funD[OF nearly_healthy_monoD, OF healthy])
    thus "«G» s * wlp body Q s + «𝒩 G» s * P s 
          «G» s * wlp body R s + «𝒩 G» s * P s"
      by(auto intro:mult_left_mono)
  next
    fix t u::"'s trans"
    assume "le_utrans t u"
           "P. unitary P  unitary (t P)"
           "P. unitary P  unitary (u P)"
    thus "le_utrans (wlp (?Z t)) (wlp (?Z u))"
      by(blast intro!:wlp_loop_step_mono[OF healthy])
  qed
qed

lemma wp_loop_sound:
  assumes sP: "sound P"
      and hb: "healthy (wp body)"
  shows "sound (wp do G  body od P)"
proof(simp only: wp_eval, intro lfp_trans_sound sP)
  let ?v = "λP s. bound_of P"
  show "le_trans (wp (body ;; Embed ?v« G »⇙⊕ Skip)) ?v"
    by(intro le_transI, simp add:wp_eval lfp_loop_fp[unfolded negate_embed] hb)
  show "P. sound P  sound (?v P)" by(auto)
qed

text ‹Likewise, we can rewrite strict loops.›
lemma wp_Loop1:
  fixes body :: "'s prog"
  assumes sP: "sound P"
      and healthy: "healthy (wp body)"
  shows "wp (do G  body od) P =
   lfp_exp (λQ s. «G» s * wp body Q s + «𝒩 G» s * P s)"
  (is "?X = lfp_exp (?Y P)")
proof -
  let "?Z u" = "(body ;; Embed u« G »⇙⊕ Skip)"
  show ?thesis
  proof(simp only: wp_eval, intro lfp_pulldown assms le_funI sP mono_transI)
    fix u P
    show "wp (?Z u) P = ?Y P (u P)" by(simp add:wp_eval negate_embed)
  next
    fix t::"'s trans" and P::"'s expect"
    assume ut: "Q. sound Q  sound (t Q)" and uP: "sound P"
    with healthy show "sound (wp (?Z t) P)" by(rule wp_loop_step_sound)
  next
    fix P Q::"'s expect"
    assume sP: "sound P" and sQ: "sound Q"
    show "sound (λa. « G » a * wp body Q a + « 𝒩 G » a * P a)"
    proof(intro soundI2 nnegI bounded_byI)
      fix s::'s
      from sQ have "nneg Q" "bounded_by (bound_of Q) Q" by(auto)
      with healthy have "bounded_by (bound_of Q) (wp body Q)" by(auto)
      hence "wp body Q s  bound_of Q" by(auto)
      hence "wp body Q s  max (bound_of P) (bound_of Q)" by(auto)
      moreover {
        from sP have "P s  bound_of P" by(auto)
        hence "P s  max (bound_of P) (bound_of Q)" by(auto)
      }
      ultimately have "«G» s * wp body Q s + «𝒩 G» s * P s 
                       «G» s * max (bound_of P) (bound_of Q) +
                       «𝒩 G» s * max (bound_of P) (bound_of Q)"
        by(auto intro!:add_mono mult_left_mono)
      also have "... = max (bound_of P) (bound_of Q)" by(simp add:algebra_simps negate_embed)
      finally show "«G» s * wp body Q s + «𝒩 G» s * P s  max (bound_of P) (bound_of Q)" .

      from sP have "0  P s" by(auto)
      moreover from sQ healthy have "0  wp body Q s" by(auto)
      ultimately show "0  «G» s * wp body Q s + «𝒩 G» s * P s"
        by(auto intro:add_nonneg_nonneg mult_nonneg_nonneg)
    qed
  next
    fix P Q R::"'s expect" and s::'s
    assume sQ: "sound Q" and sR: "sound R"
       and le: "Q  R"
    hence "wp body Q s  wp body R s"
      by(blast intro:le_funD[OF mono_transD, OF healthy_monoD, OF healthy])
    thus "«G» s * wp body Q s + «𝒩 G» s * P s 
          «G» s * wp body R s + «𝒩 G» s * P s"
      by(auto intro:mult_left_mono)
  next
    fix t u::"'s trans"
    assume le: "le_trans t u"
       and st: "P. sound P  sound (t P)"
       and su: "P. sound P  sound (u P)"
    with healthy show "le_trans (wp (?Z t)) (wp (?Z u))"
      by(rule wp_loop_step_mono)
  next
    from healthy show "le_trans (wp (?Z (λP s. bound_of P))) (λP s. bound_of P)"
      by(intro le_transI, simp add:wp_eval lfp_loop_fp[unfolded negate_embed])
  next
    fix P::"'s expect" and s::'s
    assume "sound P"
    thus "sound (λs. bound_of P)" by(auto)
  qed
qed

lemma nearly_healthy_wlp_loop:
  fixes body::"'s prog"
  assumes hb: "nearly_healthy (wlp body)"
  shows "nearly_healthy (wlp (do G  body od))"
proof(intro nearly_healthyI unitaryI2 nnegI2 bounded_byI2, simp_all add:wlp_Loop1 hb)
  fix P::"'s expect"
  assume uP: "unitary P"
  let "?X R" = "λQ s. « G » s * wlp body Q s + « 𝒩 G » s * R s"

  show "λs. 0  gfp_exp (?X P)"
  proof(rule gfp_exp_upperbound)
    show "unitary (λs. 0::real)" by(auto)
    with hb have "unitary (wlp body (λs. 0))" by(auto)
    with uP show "λs. 0  (?X P (λs. 0))"
      by(blast intro!:le_funI add_nonneg_nonneg mult_nonneg_nonneg)
  qed

  show "gfp_exp (?X P)  λs. 1"
  proof(rule gfp_exp_least)
    show "unitary (λs. 1::real)" by(auto)
    fix Q::"'s expect"
    assume "unitary Q"
    thus "Q  λs. 1" by(auto)
  qed

  fix Q::"'s expect"
  assume uQ: "unitary Q" and le: "P  Q"
  show "gfp_exp (?X P)  gfp_exp (?X Q)"
  proof(rule gfp_exp_least)
    fix R::"'s expect" assume uR: "unitary R"
    assume fp: "R  ?X P R"
    also from le have "...  ?X Q R"
      by(blast intro:add_mono mult_left_mono le_funI)
    finally show "R  gfp_exp (?X Q)"
      using uR by(auto intro:gfp_exp_upperbound)
  next
    show "unitary (gfp_exp (?X Q))"
    proof(rule gfp_exp_unitary, intro unitaryI2 nnegI bounded_byI)
      fix R::"'s expect" and s::'s assume uR: "unitary R"
      with hb have ubP: "unitary (wlp body R)" by(auto)
      with uQ show "0  « G » s * wlp body R s + « 𝒩 G » s * Q s"
        by(blast intro:add_nonneg_nonneg mult_nonneg_nonneg)

      from ubP uQ have "wlp body R s  1" "Q s  1" by(auto)
      hence "« G » s * wlp body R s + « 𝒩 G » s * Q s  «G» s * 1 + «𝒩 G» s * 1"
        by(blast intro:add_mono mult_left_mono)
      thus "« G » s * wlp body R s + « 𝒩 G » s * Q s  1"
        by(simp add:negate_embed)
    qed
  qed
qed

text ‹We show healthiness by appealing to the properties of expectation fixed points, applied
to the alternative loop definition.›
lemma healthy_wp_loop:
  fixes body::"'s prog"
  assumes hb: "healthy (wp body)"
  shows "healthy (wp (do G  body od))"
proof -
  let "?X P" = "(λQ s. «G» s * wp body Q s + «𝒩 G» s * P s)"
  show ?thesis
  proof(intro healthy_parts bounded_byI2 nnegI2, simp_all add:wp_Loop1 hb soundI2 sound_intros)
    fix P::"'s expect" and c::real and s::'s
    assume sP: "sound P" and nnc: "0  c"
    show "c * (lfp_exp (?X P)) s = lfp_exp (?X (λs. c * P s)) s"
    proof(cases)
      assume "c = 0" thus ?thesis
      proof(simp, intro antisym)
        from hb have fp: "λs. «G» s * wp body (λ_. 0) s  λs. 0" by(simp)
        hence "lfp_exp (λP s. «G» s * wp body P s)  λs. 0"
          by(auto intro:lfp_exp_lowerbound)
        thus "lfp_exp (λP s. «G» s * wp body P s) s  0" by(auto)
        have "λs. 0  lfp_exp (λP s. «G» s * wp body P s)"
          by(auto intro:lfp_exp_greatest fp)
        thus "0  lfp_exp (λP s. «G» s * wp body P s) s" by(auto)
      qed
    next
      have onesided: "P c. c  0  0  c  sound P 
            λa. c * lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) a 
                    lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * (c * P b))"
      proof -
        fix P::"'s expect" and c::real
        assume cnz: "c  0" and nnc: "0  c" and sP: "sound P"
        with nnc have cpos: "0 < c" by(auto)
        hence nnic: "0  inverse c" by(auto)
        show "λa. c * lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) a 
              lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * (c * P b))"
        proof(rule lfp_exp_greatest)
          fix Q::"'s expect"
          assume sQ: "sound Q"
             and fp: "λb. «G» b * wp body Q b + «𝒩 G» b * (c * P b)  Q"
          hence "s. «G» s * wp body Q s + «𝒩 G» s * (c * P s)  Q s" by(auto)
          with nnic
          have "s. inverse c * («G» s * wp body Q s + «𝒩 G» s * (c * P s)) 
                     inverse c * Q s"
            by(auto intro:mult_left_mono)
          hence "s. «G» s * (inverse c * wp body Q s) + (inverse c * c) * «𝒩 G» s * P s 
                     inverse c * Q s"
            by(simp add:algebra_simps)
          hence "s. «G» s * wp body (λs. inverse c * Q s) s + «𝒩 G» s * P s 
                     inverse c * Q s"
            by(simp add:cnz scalingD[OF healthy_scalingD, OF hb sQ nnic])
          hence "λs. «G» s * wp body (λs. inverse c * Q s) s + «𝒩 G» s * P s 
                 λs. inverse c * Q s" by(rule le_funI)
          moreover from nnic sQ have "sound (λs. inverse c * Q s)"
            by(iprover intro:sound_intros)
          ultimately have "lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) 
                           λs. inverse c * Q s"
            by(rule lfp_exp_lowerbound)
          hence "s. lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) s  inverse c * Q s"
            by(rule le_funD)
          with nnc
          have "s. c * lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) s 
                     c * (inverse c * Q s)"
            by(auto intro:mult_left_mono)
          also from cnz have "s. ... s = Q s" by(simp)
          finally show "λa. c * lfp_exp (λa b. «G» b * wp body a b + «𝒩 G» b * P b) a  Q"
            by(rule le_funI)
        next
          from sP have "sound (λs. bound_of P)" by(auto)
          with hb sP have "sound (lfp_exp (?X P))"
            by(blast intro:lfp_exp_sound lfp_loop_fp)
          with nnc show "sound (λs. c * lfp_exp (?X P) s)"
            by(auto intro!:sound_intros)

          from hb sP nnc
          show "λs. «G» s * wp body (λs. bound_of (λs. c * P s)) s +
                    «𝒩 G» s * (c * P s)  λs. bound_of (λs. c * P s)"
            by(iprover intro:lfp_loop_fp sound_intros)

          from sP nnc show "sound (λs. bound_of (λs. c * P s))"
            by(auto intro!:sound_intros)
        qed
      qed

      assume nzc: "c  0"
      show ?thesis (is "?X P c s = ?Y P c s")
      proof(rule fun_cong[where x=s], rule antisym)
        from nzc nnc sP show "?X P c  ?Y P c" by(rule onesided)

        from nzc have nzic: "inverse c  0" by(auto)
        moreover with nnc have nnic: "0  inverse c" by(auto)
        moreover from nnc sP have scP: "sound (λs. c * P s)" by(auto intro!:sound_intros)
        ultimately have "?X (λs. c * P s) (inverse c)  ?Y (λs. c * P s) (inverse c)"
          by(rule onesided)
        with nnc have "λs. c * ?X (λs. c * P s) (inverse c) s 
                       λs. c * ?Y (λs. c * P s) (inverse c) s"
          by(blast intro:mult_left_mono)
        with nzc show "?Y P c  ?X P c" by(simp add:mult.assoc[symmetric])
      qed
    qed
  next
    fix P::"'s expect" and b::real
    assume bP: "bounded_by b P" and nP: "nneg P"
    show "lfp_exp (λQ s. «G» s * wp body Q s + «𝒩 G» s * P s)  λs. b"
    proof(intro lfp_exp_lowerbound le_funI)
      fix s::'s
      from bP nP hb have "bounded_by b (wp body (λs. b))" by(auto)
      hence "wp body (λs. b) s  b" by(auto)
      moreover from bP have "P s  b" by(auto)
      ultimately have "«G» s * wp body (λs. b) s + «𝒩 G» s * P s  «G» s * b + «𝒩 G» s * b"
        by(auto intro!:add_mono mult_left_mono)
      also have "... = b" by(simp add:negate_embed field_simps)
      finally show "«G» s * wp body (λs. b) s + «𝒩 G» s * P s  b" .
      from bP nP have "0  b" by(auto)
      thus "sound (λs. b)" by(auto)
    qed
    from hb bP nP show "λs. 0  lfp_exp (λQ s. «G» s * wp body Q s + «𝒩 G» s * P s)"
      by(auto dest!:sound_nneg intro!:lfp_loop_greatest)
  next
    fix P Q::"'s expect"
    assume sP: "sound P" and sQ: "sound Q" and le: "P  Q"
    show "lfp_exp (?X P)  lfp_exp (?X Q)"
    proof(rule lfp_exp_greatest)
      fix R::"'s expect"
      assume sR: "sound R"
         and fp: "λs. «G» s * wp body R s + «𝒩 G» s * Q s  R"
      from le have "λs. «G» s * wp body R s + «𝒩 G» s * P s 
                    λs. «G» s * wp body R s + «𝒩 G» s * Q s"
        by(auto intro:le_funI add_left_mono mult_left_mono)
      also note fp
      finally show "lfp_exp (λR s. «G» s * wp body R s + «𝒩 G» s * P s)  R"
        using sR by(auto intro:lfp_exp_lowerbound)
    next
      from hb sP show "sound (lfp_exp (λR s. «G» s * wp body R s + «𝒩 G» s * P s))"
        by(rule lfp_loop_sound)
      from hb sQ show "λs. «G» s * wp body (λs. bound_of Q) s +  «𝒩 G» s * Q s  λs. bound_of Q"
        by(rule lfp_loop_fp)
      from sQ show "sound (λs. bound_of Q)" by(auto)
    qed
  qed
qed

text ‹Use 'simp add:healthy\_intros' or 'blast intro:healthy\_intros' as
        appropriate to discharge healthiness side-contitions for primitive
        programs automatically.›
lemmas healthy_intros =
  healthy_wp_Abort nearly_healthy_wlp_Abort healthy_wp_Skip   nearly_healthy_wlp_Skip
  healthy_wp_Seq   nearly_healthy_wlp_Seq   healthy_wp_PC     nearly_healthy_wlp_PC
  healthy_wp_DC    nearly_healthy_wlp_DC    healthy_wp_AC     nearly_healthy_wlp_AC
  healthy_wp_Embed nearly_healthy_wlp_Embed healthy_wp_Apply  nearly_healthy_wlp_Apply
  healthy_wp_SetDC nearly_healthy_wlp_SetDC healthy_wp_SetPC  nearly_healthy_wlp_SetPC
  healthy_wp_Bind  nearly_healthy_wlp_Bind  healthy_wp_repeat nearly_healthy_wlp_repeat
  healthy_wp_loop  nearly_healthy_wlp_loop

end