Theory Basis

(*<*)
theory Basis
imports
  "HOL-Library.While_Combinator"
begin

(*>*)
section‹ Point-free notation ›

text‹

We adopt point-free notation for our assertions over program states.

›

abbreviation (input)
  pred_K :: "'b  'a  'b" (_) where
  "f  λs. f"

abbreviation (input)
  pred_not :: "('a  bool)  'a  bool" (¬) where
  "¬a  λs. ¬a s"

abbreviation (input)
  pred_conj :: "('a  bool)  ('a  bool)  'a  bool" (infixr  35) where
  "a  b  λs. a s  b s"

abbreviation (input)
  pred_implies :: "('a  bool)  ('a  bool)  'a  bool" (infixr  25) where
  "a  b  λs. a s  b s"

abbreviation (input)
  pred_eq :: "('a  'b)  ('a  'b)  'a  bool" (infix = 40) where
  "a = b  λs. a s = b s"

abbreviation (input)
  pred_member :: "('a  'b)  ('a  'b set)  'a  bool" (infix  40) where
  "a  b  λs. a s  b s"

abbreviation (input)
  pred_neq :: "('a  'b)  ('a  'b)  'a  bool" (infix  40) where
  "a  b  λs. a s  b s"

abbreviation (input)
  pred_If :: "('a  bool)  ('a  'b)  ('a  'b)  'a  'b" ((if (_)/ then (_)/ else (_)) [0, 0, 10] 10) where
  "if P then x else y  λs. if P s then x s else y s"

abbreviation (input)
  pred_less :: "('a  'b::ord)  ('a  'b)  'a  bool" (infix < 40) where
  "a < b  λs. a s < b s"

abbreviation (input)
  pred_le :: "('a  'b::ord)  ('a  'b)  'a  bool" (infix  40) where
  "a  b  λs. a s  b s"

abbreviation (input)
  pred_plus :: "('a  'b::plus)  ('a  'b)  'a  'b" (infixl + 65) where
  "a + b  λs. a s + b s"

abbreviation (input)
  pred_minus :: "('a  'b::minus)  ('a  'b)  'a  'b" (infixl - 65) where
  "a - b  λs. a s - b s"

abbreviation (input)
  fun_fanout :: "('a  'b)  ('a  'c)  'a  'b × 'c" (infix  35) where
  "f  g  λx. (f x, g x)"

abbreviation (input)
  pred_all :: "('b  'a  bool)  'a  bool" (binder  10) where
  "x. P x  λs. x. P x s"

abbreviation (input)
  pred_ex :: "('b  'a  bool)  'a  bool" (binder  10) where
  "x. P x  λs. x. P x s"

section‹ ``Monoidal'' Hoare logic ›

text‹

In the absence of a general-purpose development of Hoare Logic for
total correctness in Isabelle/HOL\footnote{At the time of writing the
distribution contains several for partial correctness, and one for
total correctness over a language with restricted expressions.  SIMPL
(cite"DBLP:journals/afp/Schirmer08") is overkill for our present
purposes.}, we adopt the following syntactic contrivance that eases
making multiple assertions about function results. ``Programs''
consist of the state-transformer semantics of statements.

›

definition valid :: "('s  bool)  ('s  's)  ('s  bool)  bool" (_/ _/ _) where
  "P c Q  s. P s  Q (c s)"

notation (input) id (SKIP)
notation fcomp (infixl ;; 60)

named_theorems wp_intro "weakest precondition intro rules"

lemma seqI[wp_intro]:
  assumes "Q d R"
  assumes "P c Q"
  shows "P c ;; d R"
using assms by (simp add: valid_def)

lemma iteI[wp_intro]:
  assumes "P' x Q"
  assumes "P'' y Q"
  shows "if b then P' else P'' if b then x else y Q"
using assms by (simp add: valid_def)

lemma assignI[wp_intro]:
  shows "Q  f f Q"
by (simp add: valid_def)

lemma whileI:
  assumes "I' c I"
  assumes "s. I s  if b s then I' s else Q s"
  assumes "wf r"
  assumes "s.  I s; b s   (c s, s)  r"
  shows "I while b c Q"
using assms by (simp add: while_rule valid_def)

lemma hoare_pre:
  assumes "R f Q"
  assumes "s. P s  R s"
  shows "P f Q"
using assms by (simp add: valid_def)

lemma hoare_post_imp:
  assumes "P a Q"
  assumes "s. Q s  R s"
  shows "P a R"
using assms by (simp add: valid_def)

text‹

Note that the @{thm[source] assignI} rule applies to all state
transformers, and therefore the order in which we attempt to use the
@{thm[source] wp_intro} rules matters.

›


section‹ Properties of iterated functions on finite sets ›

text‹

We begin by fixing the @{term "f"} and @{term "x0"} under
consideration in a locale, and establishing Knuth's properties.

The sequence is modelled as a function seq :: nat
⇒ 'a› in the obvious way.

›

locale fx0 =
  fixes f :: "'a::finite  'a"
  fixes x0 :: "'a"
begin

definition seq' :: "'a  nat  'a" where
  "seq' x i  (f ^^ i) x"

abbreviation "seq  seq' x0"
(*<*)

declare (in -) fx0.seq'_def[code]

lemma seq'_simps[simp]:
  "seq' x 0 = x"
  "seq' x (Suc i) = f (seq' x i)"
  "seq' (f x) i  range (seq' x)"
by (auto intro: range_eqI[where x="Suc i"] simp: seq'_def funpow_swap1)

lemma seq_inj:
  " seq' x i = seq' x j; p = i + n; q = j + n   seq' x p = seq' x q"
apply hypsubst_thin
by (induct n) simp_all
(*>*)
text‹

The parameters lambda› and mu› must exist by the
pigeonhole principle.

›

lemma seq'_not_inj_on_card_UNIV:
  shows "¬inj_on (seq' x) {0 .. card (UNIV::'a set)}"
by (simp add: inj_on_iff_eq_card)
   (metis UNIV_I card_mono finite lessI not_less subsetI)

definition properties :: "nat  nat  bool" where
  "properties lambda mu 
     0 < lambda
    inj_on seq {0 ..< mu + lambda}
    (imu. j. seq (i + j * lambda) = seq i)"

lemma properties_existence:
  obtains lambda mu
  where "properties lambda mu"
proof -
  obtain l where l: "inj_on seq {0..l}  ¬inj_on seq {0..Suc l}"
    using ex_least_nat_less[where P="λub. ¬inj_on seq {0..ub}" and n="card (UNIV :: 'a set)"]
          seq'_not_inj_on_card_UNIV
    by fastforce
  moreover
  from l obtain mu where mu: "mu  l  seq (Suc l) = seq mu"
    by (fastforce simp: atLeastAtMostSuc_conv)
  moreover
  define lambda where "lambda = l - mu + 1"
  have "seq (i + j * lambda) = seq i" if "mu  i" for i j
  using that proof (induct j)
    case (Suc j)
    from l mu have F: "seq (l + j + 1) = seq (mu + j)" for j
      by (fastforce elim: seq_inj)
    from mu Suc F[where j="i + j * lambda - mu"] show ?case
      by (simp add: lambda_def field_simps)
  qed simp
  ultimately have "properties lambda mu"
    by (auto simp: properties_def lambda_def atLeastLessThanSuc_atLeastAtMost)
  then show thesis ..
qed

end

text‹

To ease further reasoning, we define a new locale that fixes @{term
"lambda"} and @{term "mu"}, and assume these properties hold. We then
derive further rules that are easy to apply.

›

locale properties = fx0 +
  fixes lambda mu :: "nat"
  assumes P: "properties lambda mu"
begin

lemma properties_lambda_gt_0:
  shows "0 < lambda"
using P by (simp add: properties_def)

lemma properties_loop:
  assumes "mu  i"
  shows "seq (i + j * lambda) = seq i"
using P assms by (simp add: properties_def)

lemma properties_mod_lambda:
  assumes "mu  i"
  shows "seq i = seq (mu + (i - mu) mod lambda)"
using properties_loop[where i="mu + (i - mu) mod lambda" and j="(i - mu) div lambda"] assms
by simp

lemma properties_distinct:
  assumes "j  {0 <..< lambda}"
  shows "seq (i + j)  seq i"
proof(cases "mu  i")
  case True
  from assms have A: "(i + j) mod lambda  i mod lambda" for i
    by (auto simp add: mod_eq_dvd_iff_nat)
  from mu  i
  have "seq (i + j) = seq (mu + (i + j - mu) mod lambda)"
       "seq i = seq (mu + (i - mu) mod lambda)"
    by (auto intro: properties_mod_lambda)
  with P mu  i assms A[where i="i-mu"] show ?thesis
    by (clarsimp simp: properties_def inj_on_eq_iff)
next
  case False with P assms show ?thesis
    by (clarsimp simp: properties_def inj_on_eq_iff)
qed

lemma properties_distinct_contrapos:
  assumes "seq (i + j) = seq i"
  shows "j  {0 <..< lambda}"
using assms by (rule contrapos_pp) (simp add: properties_distinct)

lemma properties_loops_ge_mu:
  assumes "seq (i + j) = seq i"
  assumes "0 < j"
  shows "mu  i"
proof(rule classical)
  assume X: "¬?thesis" show ?thesis
  proof(cases "mu  i + j")
    case True with P X assms show ?thesis
      by (fastforce simp: properties_def inj_on_eq_iff
                    dest: properties_mod_lambda)
  next
    case False with P assms show ?thesis
      by (fastforce simp add: properties_def inj_on_eq_iff)
  qed
qed

end
(*<*)

end
(*>*)