(*  Title:       Variations on a Theme
 *  Author:      Sophie Tourret <stourret at mpi-inf.mpg.de>, 2018-2020 *)

section \<open>Variations on a Theme\<close>

text \<open>In this section, section 2.4 of the report is covered, demonstrating
  that various notions of redundancy are equivalent.\<close>

theory Calculus_Variations
  imports Calculus
begin

locale reduced_calculus = calculus Bot Inf entails Red_I Red_F
  for
    Bot :: "'f set" and
    Inf :: \<open>'f inference set\<close> and
    entails :: "'f set \<Rightarrow> 'f set \<Rightarrow> bool" (infix "\<Turnstile>" 50) and
    Red_I :: "'f set \<Rightarrow> 'f inference set" and
    Red_F :: "'f set \<Rightarrow> 'f set"
 + assumes
   inf_in_red_inf: "Inf_between UNIV (Red_F N) \<subseteq> Red_I N"
begin

(* lem:reduced-rc-implies-sat-equiv-reduced-sat *)
lemma sat_eq_reduc_sat: "saturated N \<longleftrightarrow> reduc_saturated N"
proof
  fix N
  assume "saturated N"
  then show "reduc_saturated N"
    using Red_I_without_red_F saturated_without_red_F
    unfolding saturated_def reduc_saturated_def
    by blast
next
  fix N
  assume red_sat_n: "reduc_saturated N"
  show "saturated N" unfolding saturated_def
    using red_sat_n inf_in_red_inf unfolding reduc_saturated_def Inf_from_def Inf_between_def
    by blast
qed

end

locale reducedly_statically_complete_calculus = calculus +
  assumes reducedly_statically_complete:
    "B \<in> Bot \<Longrightarrow> reduc_saturated N \<Longrightarrow> N \<Turnstile> {B} \<Longrightarrow> \<exists>B'\<in>Bot. B' \<in> N"

locale reducedly_statically_complete_reduced_calculus = reduced_calculus +
  assumes reducedly_statically_complete:
    "B \<in> Bot \<Longrightarrow> reduc_saturated N \<Longrightarrow> N \<Turnstile> {B} \<Longrightarrow> \<exists>B'\<in>Bot. B' \<in> N"
begin

sublocale reducedly_statically_complete_calculus
  by (simp add: calculus_axioms reducedly_statically_complete
    reducedly_statically_complete_calculus_axioms.intro
    reducedly_statically_complete_calculus_def)

(* cor:reduced-rc-implies-st-ref-comp-equiv-reduced-st-ref-comp 1/2 *)
sublocale statically_complete_calculus
proof
  fix B N
  assume
    bot_elem: \<open>B \<in> Bot\<close> and
    saturated_N: "saturated N" and
    refut_N: "N \<Turnstile> {B}"
  have reduc_saturated_N: "reduc_saturated N" using saturated_N sat_eq_reduc_sat by blast
  show "\<exists>B'\<in>Bot. B' \<in> N" using reducedly_statically_complete[OF bot_elem reduc_saturated_N refut_N] .
qed

end

context reduced_calculus
begin

(* cor:reduced-rc-implies-st-ref-comp-equiv-reduced-st-ref-comp 2/2 *)
lemma stat_ref_comp_imp_red_stat_ref_comp:
  "statically_complete_calculus Bot Inf entails Red_I Red_F \<Longrightarrow>
   reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F"
proof
  fix B N
  assume
    stat_ref_comp: "statically_complete_calculus Bot Inf (\<Turnstile>) Red_I Red_F" and
    bot_elem: \<open>B \<in> Bot\<close> and
    saturated_N: "reduc_saturated N" and
    refut_N: "N \<Turnstile> {B}"
  have reduc_saturated_N: "saturated N" using saturated_N sat_eq_reduc_sat by blast
  show "\<exists>B'\<in>Bot. B' \<in> N"
    using statically_complete_calculus.statically_complete[OF stat_ref_comp
      bot_elem reduc_saturated_N refut_N] .
qed

end

context calculus
begin

definition Red_Red_I :: "'f set \<Rightarrow> 'f inference set" where
  "Red_Red_I N = Red_I N \<union> Inf_between UNIV (Red_F N)"

lemma reduced_calc_is_calc: "calculus Bot Inf entails Red_Red_I Red_F"
proof
  fix N
  show "Red_Red_I N \<subseteq> Inf"
    unfolding Red_Red_I_def Inf_between_def Inf_from_def using Red_I_to_Inf by auto
next
  fix B N
  assume
    b_in: "B \<in> Bot" and
    n_entails: "N \<Turnstile> {B}"
  show "N - Red_F N \<Turnstile> {B}"
    by (simp add: Red_F_Bot b_in n_entails)
next
  fix N N' :: "'f set"
  assume "N \<subseteq> N'"
  then show "Red_F N \<subseteq> Red_F N'" by (simp add: Red_F_of_subset)
next
  fix N N' :: "'f set"
  assume n_in: "N \<subseteq> N'"
  then have "Inf_from (UNIV - (Red_F N')) \<subseteq> Inf_from (UNIV - (Red_F N))"
    using Red_F_of_subset[OF n_in] unfolding Inf_from_def by auto
  then have "Inf_between UNIV (Red_F N) \<subseteq> Inf_between UNIV (Red_F N')"
    unfolding Inf_between_def by auto
  then show "Red_Red_I N \<subseteq> Red_Red_I N'"
    unfolding Red_Red_I_def using Red_I_of_subset[OF n_in] by blast
next
  fix N N' :: "'f set"
  assume "N' \<subseteq> Red_F N"
  then show "Red_F N \<subseteq> Red_F (N - N')" by (simp add: Red_F_of_Red_F_subset)
next
  fix N N' :: "'f set"
  assume np_subs: "N' \<subseteq> Red_F N"
  have "Red_F N \<subseteq> Red_F (N - N')" by (simp add: Red_F_of_Red_F_subset np_subs)
  then have "Inf_from (UNIV - (Red_F (N - N'))) \<subseteq> Inf_from (UNIV - (Red_F N))"
    by (metis Diff_subset Red_F_of_subset eq_iff)
  then have "Inf_between UNIV (Red_F N) \<subseteq> Inf_between UNIV (Red_F (N - N'))"
    unfolding Inf_between_def by auto
  then show "Red_Red_I N \<subseteq> Red_Red_I (N - N')"
    unfolding Red_Red_I_def using Red_I_of_Red_F_subset[OF np_subs] by blast
next
  fix \<iota> N
  assume "\<iota> \<in> Inf"
    "concl_of \<iota> \<in> N"
  then show "\<iota> \<in> Red_Red_I N"
    by (simp add: Red_I_of_Inf_to_N Red_Red_I_def)
qed

lemma inf_subs_reduced_red_inf: "Inf_between UNIV (Red_F N) \<subseteq> Red_Red_I N"
  unfolding Red_Red_I_def by simp

(* lem:red'-is-reduced-redcrit *)
text \<open>The following is a lemma and not a sublocale as was previously used in similar cases.
  Here, a sublocale cannot be used because it would create an infinitely descending
  chain of sublocales. \<close>
lemma reduc_calc: "reduced_calculus Bot Inf entails Red_Red_I Red_F"
  using inf_subs_reduced_red_inf reduced_calc_is_calc
  by (simp add: reduced_calculus.intro reduced_calculus_axioms_def)

interpretation reduc_calc: reduced_calculus Bot Inf entails Red_Red_I Red_F
  by (fact reduc_calc)

(* lem:saturation-red-vs-red'-1 *)
lemma sat_imp_red_calc_sat: "saturated N \<Longrightarrow> reduc_calc.saturated N"
  unfolding saturated_def reduc_calc.saturated_def Red_Red_I_def by blast

(* lem:saturation-red-vs-red'-2 1/2 (i) \<longleftrightarrow> (ii) *)
lemma red_sat_eq_red_calc_sat: "reduc_saturated N \<longleftrightarrow> reduc_calc.saturated N"
proof
  assume red_sat_n: "reduc_saturated N"
  show "reduc_calc.saturated N"
    unfolding reduc_calc.saturated_def
  proof
    fix \<iota>
    assume i_in: "\<iota> \<in> Inf_from N"
    show "\<iota> \<in> Red_Red_I N"
      using i_in red_sat_n
      unfolding reduc_saturated_def Inf_between_def Inf_from_def Red_Red_I_def by blast
  qed
next
  assume red_sat_n: "reduc_calc.saturated N"
  show "reduc_saturated N"
    unfolding reduc_saturated_def
  proof
    fix \<iota>
    assume i_in: "\<iota> \<in> Inf_from (N - Red_F N)"
    show "\<iota> \<in> Red_I N"
      using i_in red_sat_n
      unfolding Inf_from_def reduc_calc.saturated_def Red_Red_I_def Inf_between_def by blast
  qed
qed

(* lem:saturation-red-vs-red'-2 2/2 (i) \<longleftrightarrow> (iii) *)
lemma red_sat_eq_sat: "reduc_saturated N \<longleftrightarrow> saturated (N - Red_F N)"
  unfolding reduc_saturated_def saturated_def by (simp add: Red_I_without_red_F)

(* thm:reduced-stat-ref-compl 1/3 (i) \<longleftrightarrow> (iii) *)
theorem stat_is_stat_red: "statically_complete_calculus Bot Inf entails Red_I Red_F \<longleftrightarrow>
  statically_complete_calculus Bot Inf entails Red_Red_I Red_F"
proof
  assume
    stat_ref1: "statically_complete_calculus Bot Inf entails Red_I Red_F"
  show "statically_complete_calculus Bot Inf entails Red_Red_I Red_F"
    using reduc_calc.calculus_axioms
    unfolding statically_complete_calculus_def statically_complete_calculus_axioms_def
  proof
    show "\<forall>B N. B \<in> Bot \<longrightarrow> reduc_calc.saturated N \<longrightarrow> N \<Turnstile> {B} \<longrightarrow> (\<exists>B'\<in>Bot. B' \<in> N)"
    proof (clarify)
      fix B N
      assume
        b_in: "B \<in> Bot" and
        n_sat: "reduc_calc.saturated N" and
        n_imp_b: "N \<Turnstile> {B}"
      have "saturated (N - Red_F N)" using red_sat_eq_red_calc_sat[of N] red_sat_eq_sat[of N] n_sat by blast
      moreover have "(N - Red_F N) \<Turnstile> {B}" using n_imp_b b_in by (simp add: reduc_calc.Red_F_Bot)
      ultimately show "\<exists>B'\<in>Bot. B'\<in> N"
        using stat_ref1 by (meson DiffD1 b_in statically_complete_calculus.statically_complete)
    qed
  qed
next
  assume
    stat_ref3: "statically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  show "statically_complete_calculus Bot Inf entails Red_I Red_F"
    unfolding statically_complete_calculus_def statically_complete_calculus_axioms_def
    using calculus_axioms
  proof
    show "\<forall>B N. B \<in> Bot \<longrightarrow> saturated N \<longrightarrow> N \<Turnstile> {B} \<longrightarrow> (\<exists>B'\<in>Bot. B' \<in> N)"
    proof clarify
      fix B N
      assume
        b_in: "B \<in> Bot" and
        n_sat: "saturated N" and
        n_imp_b: "N \<Turnstile> {B}"
      then show "\<exists>B'\<in> Bot. B' \<in> N"
        using stat_ref3 sat_imp_red_calc_sat[OF n_sat]
        by (meson statically_complete_calculus.statically_complete)
    qed
  qed
qed

(* thm:reduced-stat-ref-compl 2/3 (iv) \<longleftrightarrow> (iii) *)
theorem red_stat_red_is_stat_red:
  "reducedly_statically_complete_calculus Bot Inf entails Red_Red_I Red_F \<longleftrightarrow>
   statically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  using reduc_calc.stat_ref_comp_imp_red_stat_ref_comp
  by (metis reduc_calc.sat_eq_reduc_sat reducedly_statically_complete_calculus.axioms(2)
    reducedly_statically_complete_calculus_axioms_def reduced_calc_is_calc
    statically_complete_calculus.intro statically_complete_calculus_axioms.intro)

(* thm:reduced-stat-ref-compl 3/3 (ii) \<longleftrightarrow> (iii) *)
theorem red_stat_is_stat_red:
  "reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F \<longleftrightarrow>
   statically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  using reduc_calc.calculus_axioms calculus_axioms red_sat_eq_red_calc_sat
  unfolding statically_complete_calculus_def statically_complete_calculus_axioms_def
    reducedly_statically_complete_calculus_def reducedly_statically_complete_calculus_axioms_def
  by blast

lemma sup_red_f_in_red_liminf:
  "chain derive Ns \<Longrightarrow> Sup_llist (lmap Red_F Ns) \<subseteq> Red_F (Liminf_llist Ns)"
proof
  fix N
  assume
    deriv: "chain derive Ns" and
    n_in_sup: "N \<in> Sup_llist (lmap Red_F Ns)"
  obtain i0 where i_smaller: "enat i0 < llength Ns" and n_in: "N \<in> Red_F (lnth Ns i0)"
    using n_in_sup by (metis Sup_llist_imp_exists_index llength_lmap lnth_lmap)
  have "Red_F (lnth Ns i0) \<subseteq> Red_F (Liminf_llist Ns)"
    using i_smaller by (simp add: deriv Red_F_subset_Liminf)
  then show "N \<in> Red_F (Liminf_llist Ns)"
    using n_in by fast
qed

lemma sup_red_inf_in_red_liminf:
  "chain derive Ns \<Longrightarrow> Sup_llist (lmap Red_I Ns) \<subseteq> Red_I (Liminf_llist Ns)"
proof
  fix \<iota>
  assume
    deriv: "chain derive Ns" and
    i_in_sup: "\<iota> \<in> Sup_llist (lmap Red_I Ns)"
  obtain i0 where i_smaller: "enat i0 < llength Ns" and n_in: "\<iota> \<in> Red_I (lnth Ns i0)"
    using i_in_sup unfolding Sup_llist_def by auto
  have "Red_I (lnth Ns i0) \<subseteq> Red_I (Liminf_llist Ns)"
    using i_smaller by (simp add: deriv Red_I_subset_Liminf)
  then show "\<iota> \<in> Red_I (Liminf_llist Ns)"
    using n_in by fast
qed

definition reduc_fair :: "'f set llist \<Rightarrow> bool" where
  "reduc_fair Ns \<longleftrightarrow>
   Inf_from (Liminf_llist Ns - Sup_llist (lmap Red_F Ns)) \<subseteq> Sup_llist (lmap Red_I Ns)"

(* lem:red-fairness-implies-red-saturation *)
lemma reduc_fair_imp_Liminf_reduc_sat:
  "chain derive Ns \<Longrightarrow> reduc_fair Ns \<Longrightarrow> reduc_saturated (Liminf_llist Ns)"
  unfolding reduc_saturated_def
proof -
  fix Ns
  assume
    deriv: "chain derive Ns" and
    red_fair: "reduc_fair Ns"
  have "Inf_from (Liminf_llist Ns - Red_F (Liminf_llist Ns))
    \<subseteq> Inf_from (Liminf_llist Ns - Sup_llist (lmap Red_F Ns))"
    using sup_red_f_in_red_liminf[OF deriv] unfolding Inf_from_def by blast
  then have "Inf_from (Liminf_llist Ns - Red_F (Liminf_llist Ns)) \<subseteq> Sup_llist (lmap Red_I Ns)"
    using red_fair unfolding reduc_fair_def by simp
  then show "Inf_from (Liminf_llist Ns - Red_F (Liminf_llist Ns)) \<subseteq> Red_I (Liminf_llist Ns)"
    using sup_red_inf_in_red_liminf[OF deriv] by fast
qed

end

locale reducedly_dynamically_complete_calculus = calculus +
  assumes
    reducedly_dynamically_complete: "B \<in> Bot \<Longrightarrow> chain derive Ns \<Longrightarrow> reduc_fair Ns \<Longrightarrow>
      lhd Ns \<Turnstile> {B} \<Longrightarrow> \<exists>i \<in> {i. enat i < llength Ns}. \<exists> B'\<in>Bot. B' \<in> lnth Ns i"
begin

sublocale reducedly_statically_complete_calculus
proof
  fix B N
  assume
    bot_elem: \<open>B \<in> Bot\<close> and
    saturated_N: "reduc_saturated N" and
    refut_N: "N \<Turnstile> {B}"
  define Ns where "Ns = LCons N LNil"
  have[simp]: \<open>\<not> lnull Ns\<close> by (auto simp: Ns_def)
  have deriv_D: \<open>chain (\<rhd>) Ns\<close> by (simp add: chain.chain_singleton Ns_def)
  have liminf_is_N: "Liminf_llist Ns = N" by (simp add: Ns_def Liminf_llist_LCons)
  have head_D: "N = lhd Ns" by (simp add: Ns_def)
  have "Sup_llist (lmap Red_F Ns) = Red_F N" by (simp add: Ns_def)
  moreover have "Sup_llist (lmap Red_I Ns) = Red_I N" by (simp add: Ns_def)
  ultimately have fair_D: "reduc_fair Ns"
    using saturated_N liminf_is_N unfolding reduc_fair_def reduc_saturated_def
    by (simp add: reduc_fair_def reduc_saturated_def liminf_is_N)
  obtain i B' where B'_is_bot: \<open>B' \<in> Bot\<close> and B'_in: "B' \<in> lnth Ns i" and \<open>i < llength Ns\<close>
    using reducedly_dynamically_complete[of B Ns] bot_elem fair_D head_D saturated_N deriv_D refut_N
    by auto
  then have "i = 0"
    by (auto simp: Ns_def enat_0_iff)
  show \<open>\<exists>B'\<in>Bot. B' \<in> N\<close>
    using B'_is_bot B'_in unfolding \<open>i = 0\<close> head_D[symmetric] Ns_def by auto
qed

end

sublocale reducedly_statically_complete_calculus \<subseteq> reducedly_dynamically_complete_calculus
proof
  fix B Ns
  assume
    bot_elem: \<open>B \<in> Bot\<close> and
    deriv: \<open>chain (\<rhd>) Ns\<close> and
    fair: \<open>reduc_fair Ns\<close> and
    unsat: \<open>lhd Ns \<Turnstile> {B}\<close>
    have non_empty: \<open>\<not> lnull Ns\<close> using chain_not_lnull[OF deriv] .
    have subs: \<open>lhd Ns \<subseteq> Sup_llist Ns\<close>
      using lhd_subset_Sup_llist[of Ns] non_empty by (simp add: lhd_conv_lnth)
    have \<open>Sup_llist Ns \<Turnstile> {B}\<close>
      using unsat subset_entailed[OF subs] entails_trans[of "Sup_llist Ns" "lhd Ns"] by auto
    then have Sup_no_Red: \<open>Sup_llist Ns - Red_F (Sup_llist Ns) \<Turnstile> {B}\<close>
      using bot_elem Red_F_Bot by auto
    have Sup_no_Red_in_Liminf: \<open>Sup_llist Ns - Red_F (Sup_llist Ns) \<subseteq> Liminf_llist Ns\<close>
      using deriv Red_in_Sup by auto
    have Liminf_entails_Bot: \<open>Liminf_llist Ns \<Turnstile> {B}\<close>
      using Sup_no_Red subset_entailed[OF Sup_no_Red_in_Liminf] entails_trans by blast
    have \<open>reduc_saturated (Liminf_llist Ns)\<close>
    using deriv fair reduc_fair_imp_Liminf_reduc_sat unfolding reduc_saturated_def
      by auto
   then have \<open>\<exists>B'\<in>Bot. B' \<in> Liminf_llist Ns\<close>
     using bot_elem reducedly_statically_complete Liminf_entails_Bot
     by auto
   then show \<open>\<exists>i\<in>{i. enat i < llength Ns}. \<exists>B'\<in>Bot. B' \<in> lnth Ns i\<close>
     unfolding Liminf_llist_def by auto
qed

context calculus
begin

lemma dyn_equiv_stat: "dynamically_complete_calculus Bot Inf entails Red_I Red_F =
  statically_complete_calculus Bot Inf entails Red_I Red_F"
proof
  assume "dynamically_complete_calculus Bot Inf entails Red_I Red_F"
  then interpret dynamically_complete_calculus Bot Inf entails Red_I Red_F
    by simp
  show "statically_complete_calculus Bot Inf entails Red_I Red_F"
    by (simp add: statically_complete_calculus_axioms)
next
  assume "statically_complete_calculus Bot Inf entails Red_I Red_F"
  then interpret statically_complete_calculus Bot Inf entails Red_I Red_F
    by simp
  show "dynamically_complete_calculus Bot Inf entails Red_I Red_F"
    by (simp add: dynamically_complete_calculus_axioms)
qed

lemma red_dyn_equiv_red_stat:
  "reducedly_dynamically_complete_calculus Bot Inf entails Red_I Red_F =
   reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F"
proof
  assume "reducedly_dynamically_complete_calculus Bot Inf entails Red_I Red_F"
  then interpret reducedly_dynamically_complete_calculus Bot Inf entails Red_I Red_F
    by simp
  show "reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F"
    by (simp add: reducedly_statically_complete_calculus_axioms)
next
  assume "reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F"
  then interpret reducedly_statically_complete_calculus Bot Inf entails Red_I Red_F
    by simp
  show "reducedly_dynamically_complete_calculus Bot Inf entails Red_I Red_F"
    by (simp add: reducedly_dynamically_complete_calculus_axioms)
qed

interpretation reduc_calc: reduced_calculus Bot Inf entails Red_Red_I Red_F
  by (fact reduc_calc)

(* thm:reduced-dyn-ref-compl 1/3 (v) \<longleftrightarrow> (vii) *)
theorem dyn_ref_eq_dyn_ref_red:
  "dynamically_complete_calculus Bot Inf entails Red_I Red_F \<longleftrightarrow>
   dynamically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  using dyn_equiv_stat stat_is_stat_red reduc_calc.dyn_equiv_stat by meson

(* thm:reduced-dyn-ref-compl 2/3 (viii) \<longleftrightarrow> (vii) *)
theorem red_dyn_ref_red_eq_dyn_ref_red:
  "reducedly_dynamically_complete_calculus Bot Inf entails Red_Red_I Red_F \<longleftrightarrow>
   dynamically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  using red_dyn_equiv_red_stat dyn_equiv_stat red_stat_red_is_stat_red
  by (simp add: reduc_calc.dyn_equiv_stat reduc_calc.red_dyn_equiv_red_stat)

(* thm:reduced-dyn-ref-compl 3/3 (vi) \<longleftrightarrow> (vii) *)
theorem red_dyn_ref_eq_dyn_ref_red:
  "reducedly_dynamically_complete_calculus Bot Inf entails Red_I Red_F \<longleftrightarrow>
   dynamically_complete_calculus Bot Inf entails Red_Red_I Red_F"
  using red_dyn_equiv_red_stat dyn_equiv_stat red_stat_is_stat_red
    reduc_calc.dyn_equiv_stat reduc_calc.red_dyn_equiv_red_stat
  by blast

end

end


header {* Common Proof Methods and Idioms *}
theory Idioms
imports "../Sep_Main" "Open_List" Circ_List Hash_Set_Impl
begin
text_raw{*\label{thy:ex:idioms}*}

  text {*
    This theory gives a short documentation of common proof techniques and
    idioms for the separation logic framework. For this purpose, it presents
    some proof snippets (inspired by the other example theories), and heavily
    comments on them.
    *}

  subsection {* The Method @{text "sep_auto"}*}
  text {* The most versatile method of our framework is @{text "sep_auto"},
    which integrates the verification condition generator, the entailment
    solver and some pre- and postprocessing tactics based on the simplifier
    and classical reasoner. It can be applied to a Hoare-triple or entailment
    subgoal, and will try to solve it, and any emerging new goals. It stops
    when the goal is either solved or it gets stuck somewhere. *}

  text {* As a simple example for @{text "sep_auto"} consider the following
    program that does some operations on two circular lists: *}
  definition "test \<equiv> do {
    l1 \<leftarrow> cs_empty;
    l2 \<leftarrow> cs_empty;
    l1 \<leftarrow> cs_append ''a'' l1;
    l2 \<leftarrow> cs_append ''c'' l2;
    l1 \<leftarrow> cs_append ''b'' l1;
    l2 \<leftarrow> cs_append ''e'' l2;
    l2 \<leftarrow> cs_prepend ''d'' l2;
    l2 \<leftarrow> cs_rotate l2;
    return (l1,l2)
  }"

  text {* The @{text "sep_auto"} method does all the
    necessary frame-inference automatically, and thus manages to prove
    the following lemma in one step: *}
  lemma "<emp>
    test
    <\<lambda>(l1,l2). cs_list [''a'',''b''] l1
      * cs_list [''c'',''e'',''d''] l2>\<^sub>t"
    unfolding test_def
    apply (sep_auto)
    done

  text {* @{text "sep_auto"} accepts all the section-options of the classical
    reasoner and simplifier, e.g., @{text "simp add/del:"}, @{text "intro:"}.
    Moreover, it has some more section options, the most useful being
    @{text "heap add/del:"} to add or remove Hoare-rules that are applied
    with frame-inference. A complete documentation of the accepted options can
    be found in Section~\ref{sec:auto:overview}.
    *}

  text {* As a typical example, consider the following proof: *}
  lemma complete_ht_rehash:
    "<is_hashtable l ht> ht_rehash ht
    <\<lambda>r. is_hashtable l ht * is_hashtable (ls_rehash l) r>"
  proof -
    have LEN: " l \<noteq> [] \<Longrightarrow> Suc 0 < 2 * length l" by (cases l) auto
    show ?thesis
      apply (rule cons_pre_rule[OF ht_imp_len])
      unfolding ht_rehash_def
      apply (sep_auto
        heap: complete_ht_new_sz complete_ht_copy
        simp: ls_rehash_def LEN
      ) -- "Here we add a heap-rule, and some simp-rules"
      done
  qed

  subsection {* Applying Single Rules *}
  text {* \paragraph{Hoare Triples} In this example, we show how to do
    a proof step-by-step. *}

  lemma
    "<os_list xs n> os_prepend x n <os_list (x # xs)>"
    unfolding os_prepend_def
    txt {* The rules to deconstruct compound statements are contained in the
      @{text "sep_decon_rules"} collection *}
    thm sep_decon_rules
    apply (rule sep_decon_rules)
    txt {* The rules for statement that deend on the heap are
      contained in the @{text sep_heap_rules} collection. The
      @{text "fi_rule"}-lemma prepares frame inference for them *}
    apply (rule sep_heap_rules[THEN fi_rule])
    apply frame_inference -- "This method does the frame-inference"

    txt {* The consequence rule comes in three versions,
      @{text "const_rule"}, @{text "cons_pre_rule"},
      and @{text "cons_post_rule"}*}
    apply (rule cons_post_rule)
    apply (rule sep_decon_rules)

    txt {* A simplification unfolds @{text "os_list"} and extract the
      pure part of the assumption *}
    apply (clarsimp)

    txt {* We can use @{text "ent_ex_postI"} to manually introduce
      existentials in entailsments *}
    apply (rule_tac x=xa in ent_ex_postI)
    apply (rule_tac x=n in ent_ex_postI)
    txt {* The simplifier has a setup for assertions, so it will do the rest *}
    apply simp
    done

  text {* Note that the proof above can be done with @{text "sep_auto"},
    the "Swiss army knife" of our framework *}
  lemma
    "<os_list xs n> os_prepend x n <os_list (x # xs)>"
    unfolding os_prepend_def by sep_auto

  text {* \paragraph{Entailment} This example presents an actual proof
    from the circular list theory, where we have to manually apply a
    rule and give some hints to frame inference *}
  lemma cs_append_rule:
    "<cs_list l p> cs_append x p <cs_list (l@[x])>"
    apply (cases p)
    apply (sep_auto simp: cs_append.simps)

    apply (sep_auto simp: cs_append.simps heap: lseg_append)
    txt {* At this point, we are left with an entailment subgoal that sep-auto
      cannot solve. A closer look reveals that we could use the rule
      @{text "lseg_append"}.

      With the @{text "ent_frame_fwd"}-rule, we can manually apply a rule to
      solve an entailment, involving frame inference. In this case, we have
      the additional problem that frame-inference guesses
      a wrong instantiation, and is not able to infer the frame.
      So we have to pre-instantiate the rule, as done below. *}
    apply (rule_tac s1=pp in ent_frame_fwd[OF lseg_append])
    apply frame_inference -- "Now frame-inference is able to infer the frame"

    txt {* Now we are left with a trivial entailment, modulo commutativity of
      star. This can be handled by the entailment solver: *}
    apply solve_entails
    done

  subsection {* Functions with Explicit Recursion *}
  text {* If the termination argument of a function depends on one of
    its parameters, we can use the function package. For example,
    the following function inserts elements from a list into a hash-set: *}

  fun ins_from_list
    :: "('x::{heap,hashable}) list \<Rightarrow> 'x hashset \<Rightarrow> 'x hashset Heap"
    where
    "ins_from_list [] hs = return hs" |
    "ins_from_list (x # l) hs = do { hs \<leftarrow> hs_ins x hs; ins_from_list l hs }"

  text {* Proofs over such functions are usually done by structural
    induction on the explicit parameter, in this case, on the list *}
  lemma ins_from_list_correct:
    "<is_hashset s hs> ins_from_list l hs <is_hashset (s\<union>set l)>\<^sub>t"
  proof (induction l arbitrary: hs s)
    case (Cons x l)
    txt {* In the induction step, the induction hypothesis has to be
      declared as a heap-rule, as @{text "sep_auto"} currently does not
      look for potential heap-rules among the premises of the subgoal *}
    show ?case by (sep_auto heap: Cons.IH)
  qed sep_auto

  subsection {*
    Functions with Recursion Involving the Heap
    *}
  text {* If the termination argument of a function depends on data stored on
    the heap, @{text "partial_function"} is a useful tool.

    Note that, despite the name, proving a Hoare-Triple @{text "<\<dots>> \<dots> <\<dots>>"}
    for something defined with @{text "partial_function"} implies total
    correctness.
    *}

  text {* In the following example, we compute the sum of a list, using an
    iterator. Note that the partial-function package does not provide a
    code generator setup by default, so we have to add a @{text "[code]"}
    attribute manually*}
  partial_function (heap) os_sum' :: "int os_list_it \<Rightarrow> int \<Rightarrow> int Heap"
    where [code]:
    "os_sum' it s = do {
      b \<leftarrow> os_it_has_next it;
      if b then do {
        (x,it') \<leftarrow> os_it_next it;
        os_sum' it' (s+x)
      } else return s
    }"

  text {* The proof that the function is correct can be done by induction
    over the representation of the list that we still have to iterate over.
    Note that for iterators over sets, we need induction on finite sets,
    cf. also @{text "To_List_Ga.thy"} *}

  lemma os_sum'_rule:
    "<os_is_it l p l' it>
    os_sum' it s
    <\<lambda>r. os_list l p * \<up>(r = s + listsum l')>\<^sub>t"
  proof (induct l' arbitrary: it s)
    case Nil thus ?case
      txt {* To unfold the definition of a partial function, we have to use
        @{text "subst"}. Note that @{text "simp"} would loop, unfolding the
        function arbitrarily deep *}
      apply (subst os_sum'.simps)
      txt {* @{text "sep_auto"} accepts all the section parameters that
        @{text "auto"} does, eg. @{text "intro: "} *}
      apply (sep_auto intro: os.quit_iteration)
      done
  next
    case (Cons x l')
    show ?case
      apply (subst os_sum'.simps)
      txt {* Additionally, @{text "sep_auto"} accepts some more section
        parameters. The most common one, @{text "heap: "}, declares rules
        to be used with frame inference. See Section~\ref{sec:auto:overview}
        for a complete overview.*}
      apply (sep_auto heap: Cons.hyps)
      done
  qed

  subsection {* Precision Proofs *}
  text {*
    Precision lemmas show that an assertion uniquely determines some of its
    parameters. Our example shows that two list segments from the same start
    pointer and with the same list, also have to end at the same end pointer.
    *}

  lemma lseg_prec3:
    "\<forall>q q'. h \<Turnstile> (lseg l p q * F1) \<and>\<^sub>A (lseg l p q' * F2) \<longrightarrow> q=q'"
    apply (intro allI)
  proof (induct l arbitrary: p F1 F2)
    case Nil thus ?case
      apply simp -- "A precision solver for references and arrays is included
        in the standard simplifier setup. Building a general precision solver
        remains future work."
      by metis -- "Unfortunately, the simplifier cannot cope with arbitrarily
        directed equations, so we have to use some more powerful tool"
  next
    case (Cons x l)
    show ?case
      apply clarsimp

      apply (subgoal_tac "na=n")

      txt {* The @{text "prec_frame"} and @{text "prec_frame'"} rules are
        useful to do precision proofs *}
      apply (erule prec_frame'[OF Cons.hyps])
      apply frame_inference
      apply frame_inference

      apply (drule prec_frame[OF sngr_prec])
      apply frame_inference
      apply frame_inference
      apply simp
      done
  qed
end