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}
   ∧ (∀i≥mu. ∀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
(*>*)