(*
   Title: The Calculus of Communicating Systems
   Author/Maintainer: Jesper Bengtson (jebe@itu.dk), 2012
*)
theory Strong_Bisim_SC
  imports Strong_Sim_SC Strong_Bisim_Pres Struct_Cong
begin

lemma resNil:
  fixes x :: name

  shows "\<lparr>\<nu>x\<rparr>\<zero> \<sim> \<zero>"
proof -
  have "(\<lparr>\<nu>x\<rparr>\<zero>, \<zero>) \<in> {(\<lparr>\<nu>x\<rparr>\<zero>, \<zero>), (\<zero>, \<lparr>\<nu>x\<rparr>\<zero>)}" by simp
  thus ?thesis
    by(coinduct rule: bisimCoinduct)
      (auto intro: resNilLeft resNilRight)
qed

lemma scopeExt:
  fixes x :: name
  and   P :: ccs
  and   Q :: ccs

  assumes "x \<sharp> P"

  shows "\<lparr>\<nu>x\<rparr>(P \<parallel> Q) \<sim> P \<parallel> \<lparr>\<nu>x\<rparr>Q"
proof -
  let ?X = "{(\<lparr>\<nu>x\<rparr>(P \<parallel> Q), P \<parallel> \<lparr>\<nu>x\<rparr>Q) | x P Q. x \<sharp> P} \<union> {(P \<parallel> \<lparr>\<nu>x\<rparr>Q, \<lparr>\<nu>x\<rparr>(P \<parallel> Q)) | x P Q. x \<sharp> P}"
  from assms have "(\<lparr>\<nu>x\<rparr>(P \<parallel> Q), P \<parallel> \<lparr>\<nu>x\<rparr>Q) \<in> ?X" by auto
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (force intro: scopeExtLeft scopeExtRight)+
qed

  shows "P \<oplus> Q \<sim> Q \<oplus> P"
proof -
  have "(P \<oplus> Q, Q \<oplus> P) \<in> {(P \<oplus> Q, Q \<oplus> P), (Q \<oplus> P, P \<oplus> Q)}" by simp
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (auto intro: sumComm reflexive)
qed

  shows "(P \<oplus> Q) \<oplus> R \<sim> P \<oplus> (Q \<oplus> R)"
proof -
  have "((P \<oplus> Q) \<oplus> R, P \<oplus> (Q \<oplus> R)) \<in> {((P \<oplus> Q) \<oplus> R, P \<oplus> (Q \<oplus> R)), (P \<oplus> (Q \<oplus> R), (P \<oplus> Q) \<oplus> R)}" by simp
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (auto intro: sumAssocLeft sumAssocRight reflexive)
qed

lemma sumId:
  fixes P :: ccs

  shows "P \<oplus> \<zero> \<sim> P"
proof -
  have "(P \<oplus> \<zero>, P) \<in> {(P \<oplus> \<zero>, P), (P, P \<oplus> \<zero>)}" by simp
  thus ?thesis by(coinduct rule: bisimCoinduct) (auto intro: sumIdLeft sumIdRight reflexive)
qed

lemma parComm:
  fixes P :: ccs
  and   Q :: ccs

  shows "P \<parallel> Q \<sim> Q \<parallel> P"
proof -
  have "(P \<parallel> Q, Q \<parallel> P) \<in> {(P \<parallel> Q, Q \<parallel> P) | P Q. True} \<union> {(Q \<parallel> P, P \<parallel> Q) | P Q. True}" by auto
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (auto intro: parComm)
qed

  shows "(P \<parallel> Q) \<parallel> R \<sim> P \<parallel> (Q \<parallel> R)"
proof -
  have "((P \<parallel> Q) \<parallel> R, P \<parallel> (Q \<parallel> R)) \<in> {((P \<parallel> Q) \<parallel> R, P \<parallel> (Q \<parallel> R)) | P Q R. True} \<union>
                                     {(P \<parallel> (Q \<parallel> R), (P \<parallel> Q) \<parallel> R) | P Q R. True}" by auto
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (force intro: parAssocLeft parAssocRight)+
qed

lemma parId:
  fixes P :: ccs

  shows "P \<parallel> \<zero> \<sim> P"
proof -
  have "(P \<parallel> \<zero>, P) \<in> {(P \<parallel> \<zero>, P) | P. True} \<union> {(P, P \<parallel> \<zero>) | P. True}" by simp
  thus ?thesis by(coinduct rule: bisimCoinduct) (auto intro: parIdLeft parIdRight)
qed

  assumes "x \<sharp> P"

  shows "\<lparr>\<nu>x\<rparr>P \<sim> P"
proof -
  have "\<lparr>\<nu>x\<rparr>P \<sim> \<lparr>\<nu>x\<rparr>P \<parallel> \<zero>" by(rule parId[THEN symmetric])
  moreover have "\<lparr>\<nu>x\<rparr>P \<parallel> \<zero> \<sim> \<zero> \<parallel> \<lparr>\<nu>x\<rparr>P" by(rule parComm)
  moreover have "\<zero> \<parallel> \<lparr>\<nu>x\<rparr>P \<sim> \<lparr>\<nu>x\<rparr>(\<zero> \<parallel> P)" by(rule scopeExt[THEN symmetric]) auto
  moreover have "\<lparr>\<nu>x\<rparr>(\<zero> \<parallel> P) \<sim> \<lparr>\<nu>x\<rparr>(P \<parallel> \<zero>)" by(rule resPres[OF parComm])
  moreover from \<open>x \<sharp> P\<close> have "\<lparr>\<nu>x\<rparr>(P \<parallel> \<zero>) \<sim> P \<parallel> \<lparr>\<nu>x\<rparr>\<zero>" by(rule scopeExt)
  moreover have  "P \<parallel> \<lparr>\<nu>x\<rparr>\<zero> \<sim> \<lparr>\<nu>x\<rparr>\<zero> \<parallel> P" by(rule parComm)
  moreover have "\<lparr>\<nu>x\<rparr>\<zero> \<parallel> P \<sim> \<zero> \<parallel> P" by(rule parPres[OF resNil])
  moreover have "\<zero> \<parallel> P \<sim> P \<parallel> \<zero>" by(rule parComm)
  moreover have "P \<parallel> \<zero> \<sim> P" by(rule parId)
  ultimately show ?thesis by(metis transitive)
qed

lemma scopeExtSum:
  fixes x :: name
  and   P :: ccs
  and   Q :: ccs

  assumes "x \<sharp> P"

  shows "\<lparr>\<nu>x\<rparr>(P \<oplus> Q) \<sim> P \<oplus> \<lparr>\<nu>x\<rparr>Q"
proof -
  have "(\<lparr>\<nu>x\<rparr>(P \<oplus> Q), P \<oplus> \<lparr>\<nu>x\<rparr>Q) \<in> {(\<lparr>\<nu>x\<rparr>(P \<oplus> Q), P \<oplus> \<lparr>\<nu>x\<rparr>Q), (P \<oplus> \<lparr>\<nu>x\<rparr>Q, \<lparr>\<nu>x\<rparr>(P \<oplus> Q))}"
    by simp
  thus ?thesis using \<open>x \<sharp> P\<close>
    by(coinduct rule: bisimCoinduct)
      (auto intro: scopeExtSumLeft scopeExtSumRight reflexive scopeFresh scopeFresh[THEN symmetric])
qed

lemma resAct:
  fixes x :: name
  and   \<alpha> :: act
  and   P :: ccs

  assumes "x \<sharp> \<alpha>"

  shows "\<lparr>\<nu>x\<rparr>(\<alpha>.(P)) \<sim> \<alpha>.(\<lparr>\<nu>x\<rparr>P)"
proof -
  have "(\<lparr>\<nu>x\<rparr>(\<alpha>.(P)), \<alpha>.(\<lparr>\<nu>x\<rparr>P)) \<in> {(\<lparr>\<nu>x\<rparr>(\<alpha>.(P)), \<alpha>.(\<lparr>\<nu>x\<rparr>P)), (\<alpha>.(\<lparr>\<nu>x\<rparr>P), \<lparr>\<nu>x\<rparr>(\<alpha>.(P)))}"
    by simp
  thus ?thesis using \<open>x \<sharp> \<alpha>\<close>
    by(coinduct rule: bisimCoinduct) (auto intro: resActLeft resActRight reflexive)
qed

lemma resComm:
  fixes x :: name
  and   y :: name
  and   P :: ccs

  shows "\<lparr>\<nu>x\<rparr>(\<lparr>\<nu>y\<rparr>P) \<sim> \<lparr>\<nu>y\<rparr>(\<lparr>\<nu>x\<rparr>P)"
proof -
  have "(\<lparr>\<nu>x\<rparr>(\<lparr>\<nu>y\<rparr>P), \<lparr>\<nu>y\<rparr>(\<lparr>\<nu>x\<rparr>P)) \<in> {(\<lparr>\<nu>x\<rparr>(\<lparr>\<nu>y\<rparr>P), \<lparr>\<nu>y\<rparr>(\<lparr>\<nu>x\<rparr>P)) | x y P. True}" by auto
  thus ?thesis
  by(coinduct rule: bisimCoinduct) (auto intro: resComm)
qed

lemma bangUnfold:
  fixes P

  shows "!P \<sim> P \<parallel> !P"
proof -
  have "(!P, P \<parallel> !P) \<in> {(!P, P \<parallel> !P), (P \<parallel> !P, !P)}" by auto
  thus ?thesis
    by(coinduct rule: bisimCoinduct) (auto intro: bangUnfoldLeft bangUnfoldRight reflexive)
qed

lemma bisimStructCong:
  fixes P :: ccs
  and   Q :: ccs

  assumes "P \<equiv>\<^sub>s Q"

  shows "P \<sim> Q"
using assms
apply(nominal_induct rule: Struct_Cong.strong_induct)
by(auto intro: reflexive symmetric transitive parComm parAssoc parId sumComm
   sumAssoc sumId resNil scopeExt scopeExtSum resAct resComm bangUnfold)

end


(*  Title:      HOL/Datatype_Examples/Derivation_Trees/Prelim.thy
    Author:     Andrei Popescu, TU Muenchen
    Copyright   2012

Preliminaries.
*)

section \<open>Preliminaries\<close>

theory Prelim
imports "~~/src/HOL/Library/FSet"
begin

notation BNF_Def.convol ("\<langle>(_,/ _)\<rangle>")

declare fset_to_fset[simp]

lemma fst_snd_convol_o[simp]: "\<langle>fst o s, snd o s\<rangle> = s"
apply(rule ext) by (simp add: convol_def)

abbreviation sm_abbrev (infix "\<oplus>" 60)
where "f \<oplus> g \<equiv> Sum_Type.map_sum f g"

lemma map_sum_InlD: "(f \<oplus> g) z = Inl x \<Longrightarrow> \<exists>y. z = Inl y \<and> f y = x"
by (cases z) auto

lemma map_sum_InrD: "(f \<oplus> g) z = Inr x \<Longrightarrow> \<exists>y. z = Inr y \<and> g y = x"
by (cases z) auto

abbreviation case_sum_abbrev ("[[_,_]]" 800)
where "[[f,g]] \<equiv> Sum_Type.case_sum f g"

lemma Inl_oplus_elim:
assumes "Inl tr \<in> (id \<oplus> f) ` tns"
shows "Inl tr \<in> tns"
using assms apply clarify by (case_tac x, auto)

lemma Inl_oplus_iff[simp]: "Inl tr \<in> (id \<oplus> f) ` tns \<longleftrightarrow> Inl tr \<in> tns"
using Inl_oplus_elim
by (metis id_def image_iff map_sum.simps(1))

lemma Inl_m_oplus[simp]: "Inl -` (id \<oplus> f) ` tns = Inl -` tns"
using Inl_oplus_iff unfolding vimage_def by auto

lemma Inr_oplus_elim:
assumes "Inr tr \<in> (id \<oplus> f) ` tns"
shows "\<exists> n. Inr n \<in> tns \<and> f n = tr"
using assms apply clarify by (case_tac x, auto)

lemma Inr_oplus_iff[simp]:
"Inr tr \<in> (id \<oplus> f) ` tns \<longleftrightarrow> (\<exists> n. Inr n \<in> tns \<and> f n = tr)"
apply (rule iffI)
 apply (metis Inr_oplus_elim)
by (metis image_iff map_sum.simps(2))

lemma Inr_m_oplus[simp]: "Inr -` (id \<oplus> f) ` tns = f ` (Inr -` tns)"
using Inr_oplus_iff unfolding vimage_def by auto

lemma Inl_Inr_image_cong:
assumes "Inl -` A = Inl -` B" and "Inr -` A = Inr -` B"
shows "A = B"
apply safe using assms apply(case_tac x, auto) by(case_tac x, auto)

end


section \<open>Attracting Strategies\<close>

theory AttractingStrategy
imports
  Main
  Strategy
begin

text \<open>Here we introduce the concept of attracting strategies.\<close>

context ParityGame begin

subsection \<open>Paths Visiting a Set\<close>

text \<open>A path that stays in \<open>A\<close> until eventually it visits \<open>W\<close>.\<close>
definition "visits_via P A W \<equiv> \<exists>n. enat n < llength P \<and> P $ n \<in> W \<and> lset (ltake (enat n) P) \<subseteq> A"

lemma visits_via_monotone: "\<lbrakk> visits_via P A W; A \<subseteq> A' \<rbrakk> \<Longrightarrow> visits_via P A' W"
  unfolding visits_via_def by blast

lemma visits_via_visits: "visits_via P A W \<Longrightarrow> lset P \<inter> W \<noteq> {}"
  unfolding visits_via_def by (meson disjoint_iff_not_equal in_lset_conv_lnth)

lemma (in vmc_path) visits_via_trivial: "v0 \<in> W \<Longrightarrow> visits_via P A W"
  unfolding visits_via_def apply (rule exI[of _ 0]) using zero_enat_def by auto

lemma visits_via_LCons:
  assumes "visits_via P A W"
  shows "visits_via (LCons v0 P) (insert v0 A) W"
proof-
  obtain n where n: "enat n < llength P" "P $ n \<in> W" "lset (ltake (enat n) P) \<subseteq> A"
    using assms unfolding visits_via_def by blast
  define P' where "P' = LCons v0 P"
  have "enat (Suc n) < llength P'" unfolding P'_def
    by (metis n(1) ldropn_Suc_LCons ldropn_Suc_conv_ldropn ldropn_eq_LConsD)
  moreover have "P' $ Suc n \<in> W" unfolding P'_def by (simp add: n(2))
  moreover have "lset (ltake (enat (Suc n)) P') \<subseteq> insert v0 A"
    using lset_ltake_Suc[of P' v0 n A] unfolding P'_def by (simp add: n(3))
  ultimately show ?thesis unfolding visits_via_def P'_def by blast
qed

lemma (in vmc_path_no_deadend) visits_via_ltl:
  assumes "visits_via P A W"
    and v0: "v0 \<notin> W"
  shows "visits_via (ltl P) A W"
proof-
  obtain n where n: "enat n < llength P" "P $ n \<in> W" "lset (ltake (enat n) P) \<subseteq> A"
    using assms(1)[unfolded visits_via_def] by blast
  have "n \<noteq> 0" using v0 n(2) DiffE by force
  then obtain n' where n': "Suc n' = n" using nat.exhaust by metis
  have "\<exists>n. enat n < llength (ltl P) \<and> (ltl P) $ n \<in> W \<and> lset (ltake (enat n) (ltl P)) \<subseteq> A"
    apply (rule exI[of _ n'])
    using n n' enat_Suc_ltl[of n' P] P_lnth_Suc lset_ltake_ltl[of n' P] by auto
  thus ?thesis using visits_via_def by blast
qed

lemma (in vm_path) visits_via_deadend:
  assumes "visits_via P A (deadends p)"
  shows "winning_path p** P"
  using assms visits_via_visits visits_deadend by blast

subsection \<open>Attracting Strategy from a Single Node\<close>

text \<open>
  All @{term \<sigma>}-paths starting from @{term v0} visit @{term W} and until then they stay in @{term A}.
\<close>
definition strategy_attracts_via :: "Player \<Rightarrow> 'a Strategy \<Rightarrow> 'a \<Rightarrow> 'a set \<Rightarrow> 'a set \<Rightarrow> bool" where
  "strategy_attracts_via p \<sigma> v0 A W \<equiv> \<forall>P. vmc_path G P v0 p \<sigma> \<longrightarrow> visits_via P A W"

lemma (in vmc_path) strategy_attracts_viaE:
  assumes "strategy_attracts_via p \<sigma> v0 A W"
  shows "visits_via P A W"
  using strategy_attracts_via_def assms vmc_path_axioms by blast

lemma (in vmc_path) strategy_attracts_via_SucE:
  assumes "strategy_attracts_via p \<sigma> v0 A W" "v0 \<notin> W"
  shows "\<exists>n. enat (Suc n) < llength P \<and> P $ Suc n \<in> W \<and> lset (ltake (enat (Suc n)) P) \<subseteq> A"
proof-
  obtain n where n: "enat n < llength P" "P $ n \<in> W" "lset (ltake (enat n) P) \<subseteq> A"
    using strategy_attracts_viaE[unfolded visits_via_def] assms(1) by blast
  have "n \<noteq> 0" using assms(2) n(2) by (metis P_0)
  thus ?thesis using n not0_implies_Suc by blast
qed

lemma (in vmc_path) strategy_attracts_via_lset:
  assumes "strategy_attracts_via p \<sigma> v0 A W"
  shows "lset P \<inter> W \<noteq> {}"
  using assms[THEN strategy_attracts_viaE, unfolded visits_via_def]
  by (meson disjoint_iff_not_equal lset_lnth_member subset_refl)

lemma strategy_attracts_via_v0:
  assumes \<sigma>: "strategy p \<sigma>" "strategy_attracts_via p \<sigma> v0 A W"
    and v0: "v0 \<in> V"
  shows "v0 \<in> A \<union> W"
proof-
  obtain P where "vmc_path G P v0 p \<sigma>" using strategy_conforming_path_exists_single assms by blast
  then interpret vmc_path G P v0 p \<sigma> .
  obtain n where n: "enat n < llength P" "P $ n \<in> W" "lset (ltake (enat n) P) \<subseteq> A"
    using \<sigma>(2)[unfolded strategy_attracts_via_def visits_via_def] vmc_path_axioms by blast
  show ?thesis proof (cases "n = 0")
    case True thus ?thesis using n(2) by simp
  next
    case False
    hence "lhd (ltake (enat n) P) = lhd P" by (simp add: enat_0_iff(1))
    hence "v0 \<in> lset (ltake (enat n) P)"
      by (metis \<open>n \<noteq> 0\<close> P_not_null P_v0 enat_0_iff(1) llist.set_sel(1) ltake.disc(2))
    thus ?thesis using n(3) by blast
  qed
qed
corollary strategy_attracts_not_outside:
  "\<lbrakk> v0 \<in> V - A - W; strategy p \<sigma> \<rbrakk> \<Longrightarrow> \<not>strategy_attracts_via p \<sigma> v0 A W"
  using strategy_attracts_via_v0 by blast

lemma strategy_attracts_viaI [intro]:
  assumes "\<And>P. vmc_path G P v0 p \<sigma> \<Longrightarrow> visits_via P A W"
  shows "strategy_attracts_via p \<sigma> v0 A W"
  unfolding strategy_attracts_via_def using assms by blast

lemma strategy_attracts_via_no_deadends:
  assumes "v \<in> V" "v \<in> A - W" "strategy_attracts_via p \<sigma> v A W"
  shows "\<not>deadend v"
proof
  assume "deadend v"
  define P where [simp]: "P = LCons v LNil"
  interpret vmc_path G P v p \<sigma> proof
    show "valid_path P" using \<open>v \<in> A - W\<close> \<open>v \<in> V\<close> valid_path_base' by auto
    show "maximal_path P" using \<open>deadend v\<close> by (simp add: maximal_path.intros(2))
    show "path_conforms_with_strategy p P \<sigma>" by (simp add: path_conforms_LCons_LNil)
  qed simp_all
  have "visits_via P A W" using assms(3) strategy_attracts_viaE by blast
  moreover have "llength P = eSuc 0" by simp
  ultimately have "P $ 0 \<in> W" by (simp add: enat_0_iff(1) visits_via_def)
  with \<open>v \<in> A - W\<close> show False by auto
qed

lemma attractor_strategy_on_extends:
  "\<lbrakk> strategy_attracts_via p \<sigma> v0 A W; A \<subseteq> A' \<rbrakk> \<Longrightarrow> strategy_attracts_via p \<sigma> v0 A' W"
  unfolding strategy_attracts_via_def using visits_via_monotone by blast

lemma strategy_attracts_via_trivial: "v0 \<in> W \<Longrightarrow> strategy_attracts_via p \<sigma> v0 A W"
proof
  fix P assume "v0 \<in> W" "vmc_path G P v0 p \<sigma>"
  then interpret vmc_path G P v0 p \<sigma> by blast
  show "visits_via P A W" using visits_via_trivial using \<open>v0 \<in> W\<close> by blast
qed

lemma strategy_attracts_via_successor:
  assumes \<sigma>: "strategy p \<sigma>" "strategy_attracts_via p \<sigma> v0 A W"
    and v0: "v0 \<in> A - W"
    and w0: "v0\<rightarrow>w0" "v0 \<in> VV p \<Longrightarrow> \<sigma> v0 = w0"
  shows "strategy_attracts_via p \<sigma> w0 A W"
proof
  fix P assume "vmc_path G P w0 p \<sigma>"
  then interpret vmc_path G P w0 p \<sigma> .
  define P' where [simp]: "P' = LCons v0 P"
  then interpret P': vmc_path G P' v0 p \<sigma>
    using extension_valid_maximal_conforming w0 by blast
  interpret P': vmc_path_no_deadend G P' v0 p \<sigma> using \<open>v0\<rightarrow>w0\<close> by unfold_locales blast
  have "visits_via P' A W" using \<sigma>(2) P'.strategy_attracts_viaE by blast
  thus "visits_via P A W" using P'.visits_via_ltl v0 by simp
qed

lemma strategy_attracts_VVp:
  assumes \<sigma>: "strategy p \<sigma>" "strategy_attracts_via p \<sigma> v0 A W"
    and v: "v0 \<in> A - W" "v0 \<in> VV p" "\<not>deadend v0"
  shows "\<sigma> v0 \<in> A \<union> W"
proof-
  have "v0\<rightarrow>\<sigma> v0" using \<sigma>(1)[unfolded strategy_def] v(2,3) by blast
  hence "strategy_attracts_via p \<sigma> (\<sigma> v0) A W"
    using strategy_attracts_via_successor \<sigma> v(1) by blast
  thus ?thesis using strategy_attracts_via_v0 \<open>v0\<rightarrow>\<sigma> v0\<close> \<sigma>(1) by blast
qed

lemma strategy_attracts_VVpstar:
  assumes "strategy p \<sigma>" "strategy_attracts_via p \<sigma> v0 A W"
    and "v0 \<in> A - W" "v0 \<notin> VV p" "w0 \<in> V - A - W"
  shows "\<not>v0 \<rightarrow> w0"
  by (metis assms strategy_attracts_not_outside strategy_attracts_via_successor)

subsection \<open>Attracting strategy from a set of nodes\<close>

text \<open>
  All @{term \<sigma>}-paths starting from @{term A} visit @{term W} and until then they stay in @{term A}.
\<close>
definition strategy_attracts :: "Player \<Rightarrow> 'a Strategy \<Rightarrow> 'a set \<Rightarrow> 'a set \<Rightarrow> bool" where
  "strategy_attracts p \<sigma> A W \<equiv> \<forall>v0 \<in> A. strategy_attracts_via p \<sigma> v0 A W"

lemma (in vmc_path) strategy_attractsE:
  assumes "strategy_attracts p \<sigma> A W" "v0 \<in> A"
  shows "visits_via P A W"
  using assms(1)[unfolded strategy_attracts_def] assms(2) strategy_attracts_viaE by blast

lemma strategy_attractsI [intro]:
  assumes "\<And>P v. \<lbrakk> v \<in> A; vmc_path G P v p \<sigma> \<rbrakk> \<Longrightarrow> visits_via P A W"
  shows "strategy_attracts p \<sigma> A W"
  unfolding strategy_attracts_def using assms by blast

lemma (in vmc_path) strategy_attracts_lset:
  assumes "strategy_attracts p \<sigma> A W" "v0 \<in> A"
  shows "lset P \<inter> W \<noteq> {}"
  using assms(1)[unfolded strategy_attracts_def] assms(2) strategy_attracts_via_lset(1)[of A W]
  by blast

lemma strategy_attracts_empty [simp]: "strategy_attracts p \<sigma> {} W" by blast

lemma strategy_attracts_invalid_path:
  assumes P: "P = LCons v (LCons w P')" "v \<in> A - W" "w \<notin> A \<union> W"
  shows "\<not>visits_via P A W" (is "\<not>?A")
proof
  assume ?A
  then obtain n where n: "enat n < llength P" "P $ n \<in> W" "lset (ltake (enat n) P) \<subseteq> A"
    unfolding visits_via_def by blast
  have "n \<noteq> 0" using \<open>v \<in> A - W\<close> n(2) P(1) DiffD2 by force
  moreover have "n \<noteq> Suc 0" using \<open>w \<notin> A \<union> W\<close> n(2) P(1) by auto
  ultimately have "Suc (Suc 0) \<le> n" by presburger
  hence "lset (ltake (enat (Suc (Suc 0))) P) \<subseteq> A" using n(3)
    by (meson contra_subsetD enat_ord_simps(1) lset_ltake_prefix lset_lnth_member lset_subset)
  moreover have "enat (Suc 0) < llength (ltake (eSuc (eSuc 0)) P)" proof-
    have *: "enat (Suc (Suc 0)) < llength P"
      using \<open>Suc (Suc 0) \<le> n\<close> n(1) by (meson enat_ord_simps(2) le_less_linear less_le_trans neq_iff)
    have "llength (ltake (enat (Suc (Suc 0))) P) = min (enat (Suc (Suc 0))) (llength P)" by simp
    hence "llength (ltake (enat (Suc (Suc 0))) P) = enat (Suc (Suc 0))"
      using * by (simp add: min_absorb1)
    thus ?thesis by (simp add: eSuc_enat zero_enat_def)
  qed
  ultimately have "ltake (enat (Suc (Suc 0))) P $ Suc 0 \<in> A" by (simp add: lset_lnth_member)
  hence "P $ Suc 0 \<in> A" by (simp add: lnth_ltake)
  thus False using P(1,3) by auto
qed

text \<open>
  If @{term A} is an attractor set of @{term W} and an edge leaves @{term A} without going through
  @{term W}, then @{term v} belongs to @{term "VV p"} and the attractor strategy @{term \<sigma>} avoids
  this edge.  All other cases give a contradiction.
\<close>
lemma strategy_attracts_does_not_leave:
  assumes \<sigma>: "strategy_attracts p \<sigma> A W" "strategy p \<sigma>"
    and v: "v\<rightarrow>w" "v \<in> A - W" "w \<notin> A \<union> W"
  shows "v \<in> VV p \<and> \<sigma> v \<noteq> w"
proof (rule ccontr)
  assume contra: "\<not>(v \<in> VV p \<and> \<sigma> v \<noteq> w)"
  (* Define a strategy for p** which tries to take this edge. *)
  define \<sigma>' where "\<sigma>' = \<sigma>_arbitrary(v := w)"
  hence "strategy p** \<sigma>'" using \<open>v\<rightarrow>w\<close> by (simp add: valid_strategy_updates)
  then obtain P where P: "vmc2_path G P v p \<sigma> \<sigma>'"
    using \<open>v\<rightarrow>w\<close> strategy_conforming_path_exists \<sigma>(2) by blast
  then interpret vmc2_path G P v p \<sigma> \<sigma>' .
  interpret vmc_path_no_deadend G P v p \<sigma> using \<open>v\<rightarrow>w\<close> by unfold_locales blast
  interpret comp: vmc_path_no_deadend G P v "p**" \<sigma>' using \<open>v\<rightarrow>w\<close> by unfold_locales blast
  have "w = w0" using contra \<sigma>'_def v0_conforms comp.v0_conforms by (cases "v \<in> VV p") auto
  hence "\<not>visits_via P A W"
    using strategy_attracts_invalid_path[of P v w "ltl (ltl P)"] v(2,3) P_LCons' by simp
  thus False by (meson DiffE \<sigma>(1) strategy_attractsE v(2))
qed

text \<open>
  Given an attracting strategy @{term \<sigma>}, we can turn every strategy @{term \<sigma>'} into an attracting
  strategy by overriding @{term \<sigma>'} on a suitable subset of the nodes.  This also means that
  an attracting strategy is still attracting if we override it outside of @{term "A - W"}.
\<close>
lemma strategy_attracts_irrelevant_override:
  assumes "strategy_attracts p \<sigma> A W" "strategy p \<sigma>" "strategy p \<sigma>'"
  shows "strategy_attracts p (override_on \<sigma>' \<sigma> (A - W)) A W"
proof (rule strategy_attractsI, rule ccontr)
  fix P v
  let ?\<sigma> = "override_on \<sigma>' \<sigma> (A - W)"
  assume "vmc_path G P v p ?\<sigma>"
  then interpret vmc_path G P v p ?\<sigma> .
  assume "v \<in> A"
  hence "P $ 0 \<in> A" using \<open>v \<in> A\<close> by simp
  moreover assume contra: "\<not>visits_via P A W"
  ultimately have "P $ 0 \<in> A - W" unfolding visits_via_def by (meson DiffI P_len not_less0 lset_ltake)
  have "\<not>lset P \<subseteq> A - W" proof
    assume "lset P \<subseteq> A - W"
    hence "\<And>v. v \<in> lset P \<Longrightarrow> override_on \<sigma>' \<sigma> (A - W) v = \<sigma> v" by auto
    hence "path_conforms_with_strategy p P \<sigma>"
      using path_conforms_with_strategy_irrelevant_updates[OF P_conforms] by blast
    hence "vmc_path G P (P $ 0) p \<sigma>"
      using conforms_to_another_strategy P_0 by blast
    thus False
      using contra \<open>P $ 0 \<in> A\<close> assms(1)
      by (meson vmc_path.strategy_attractsE)
  qed
  hence "\<exists>n. enat n < llength P \<and> P $ n \<notin> A - W" by (meson lset_subset)
  then obtain n where n: "enat n < llength P \<and> P $ n \<notin> A - W"
    "\<And>i. i < n \<Longrightarrow> \<not>(enat i < llength P \<and> P $ i \<notin> A - W)"
    using ex_least_nat_le[of "\<lambda>n. enat n < llength P \<and> P $ n \<notin> A - W"] by blast
  hence n_min: "\<And>i. i < n \<Longrightarrow> P $ i \<in> A - W"
    using dual_order.strict_trans enat_ord_simps(2) by blast
  have "n \<noteq> 0" using \<open>P $ 0 \<in> A - W\<close> n(1) by meson
  then obtain n' where n': "Suc n' = n" using not0_implies_Suc by blast
  hence "P $ n' \<in> A - W" using n_min by blast
  moreover have "P $ n' \<rightarrow> P $ Suc n'" using P_valid n(1) n' valid_path_edges by blast
  moreover have "P $ Suc n' \<notin> A \<union> W" proof-
    have "P $ n \<notin> W" using contra n(1) n_min unfolding visits_via_def
      by (meson Diff_subset lset_ltake subsetCE)
    thus ?thesis using n(1) n' by blast
  qed
  ultimately have "P $ n' \<in> VV p \<and> \<sigma> (P $ n') \<noteq> P $ Suc n'"
    using strategy_attracts_does_not_leave[of p \<sigma> A W "P $ n'" "P $ Suc n'"]
          assms(1,2) by blast
  thus False
    using n(1) n' vmc_path_conforms \<open>P $ n' \<in> A - W\<close> by (metis override_on_apply_in)
qed

lemma strategy_attracts_trivial [simp]: "strategy_attracts p \<sigma> W W"
  by (simp add: strategy_attracts_def strategy_attracts_via_trivial)

text \<open>If a @{term \<sigma>}-conforming path @{term P} hits an attractor @{term A}, it will visit @{term W}.\<close>
lemma (in vmc_path) attracted_path:
  assumes "W \<subseteq> V"
    and \<sigma>: "strategy_attracts p \<sigma> A W"
    and P_hits_A: "lset P \<inter> A \<noteq> {}"
  shows "lset P \<inter> W \<noteq> {}"
proof-
  obtain n where n: "enat n < llength P" "P $ n \<in> A" using P_hits_A by (meson lset_intersect_lnth)
  define P' where "P' = ldropn n P"
  interpret vmc_path G P' "P $ n" p \<sigma> unfolding P'_def using vmc_path_ldropn n(1) by blast
  have "visits_via P' A W" using \<sigma> n(2) strategy_attractsE by blast
  thus ?thesis unfolding P'_def using visits_via_visits in_lset_ldropnD[of _ n P] by blast
qed

lemma attracted_strategy_step:
  assumes \<sigma>: "strategy p \<sigma>" "strategy_attracts p \<sigma> A W"
    and v0: "\<not>deadend v0" "v0 \<in> A - W" "v0 \<in> VV p"
  shows "\<sigma> v0 \<in> A \<union> W"
  by (metis DiffD1 strategy_attracts_VVp assms strategy_attracts_def)

lemma (in vmc_path_no_deadend) attracted_path_step:
  assumes \<sigma>: "strategy_attracts p \<sigma> A W"
    and v0: "v0 \<in> A - W"
  shows "w0 \<in> A \<union> W"
  by (metis (no_types) DiffD1 P_LCons' \<sigma> strategy_attractsE strategy_attracts_invalid_path v0)

end \<comment> \<open>context ParityGame\<close>

end


theory LinearAlgebra_VectorSpace_E1
imports "$HETS_LIB/Isabelle/MainHCPairs"
uses "$HETS_LIB/Isabelle/prelude"
begin

ML "Header.initialize
    [\"subtype_def\", \"subtype_pred_def\", \"ga_assoc___Xx__\",
     \"ga_right_unit___Xx__\", \"ga_left_unit___Xx__\", \"inv_Group\",
     \"rinv_Group\", \"ga_comm___Xx__\", \"binary_inverse\",
     \"ga_assoc___Xx___2\", \"ga_right_unit___Xx___2\",
     \"ga_left_unit___Xx___2\", \"inv_Group_1\", \"rinv_Group_1\",
     \"ga_comm___Xx___2\", \"ga_assoc___Xx___1\",
     \"ga_right_unit___Xx___1\", \"ga_left_unit___Xx___1\",
     \"distr1_Ring\", \"distr2_Ring\", \"ga_comm___Xx___1\",
     \"noZeroDiv\", \"zeroNeqOne\", \"Ax1\", \"ga_assoc___Xx___2_1\",
     \"ga_right_unit___Xx___2_1\", \"ga_left_unit___Xx___2_1\",
     \"inv_Group_1_1\", \"rinv_Group_1_1\", \"unit\", \"mix_assoc\",
     \"distr_Field\", \"distr_Space\", \"zero_by_left_zero\",
     \"zero_by_right_zero\", \"inverse_by_XMinus1\"]"

typedecl Elem
typedecl NonZero
typedecl Space

consts
X0X1 :: "Elem" ("0''")
X0X2 :: "Space" ("0''''")
X1X1 :: "Elem" ("1''")
X1X2 :: "NonZero" ("1''''")
XMinus__XX1 :: "Elem => Elem" ("(-''/ _)" [56] 56)
XMinus__XX2 :: "Space => Space" ("(-''''/ _)" [56] 56)
X__XMinus__X :: "Space => Space => Space" ("(_/ -''/ _)" [54,54] 52)
X__XPlus__XX1 :: "Elem => Elem => Elem" ("(_/ +''/ _)" [54,54] 52)
X__XPlus__XX2 :: "Space => Space => Space" ("(_/ +''''/ _)" [54,54] 52)
X__Xx__XX1 :: "Elem => Elem => Elem" ("(_/ *''/ _)" [54,54] 52)
X__Xx__XX2 :: "Elem => Space => Space" ("(_/ *''''/ _)" [54,54] 52)
X__Xx__XX3 :: "NonZero => NonZero => NonZero" ("(_/ *'_3/ _)" [54,54] 52)
X_gn_inj :: "'a => 'b" ("gn'_inj/'(_')" [3] 999)
X_gn_proj :: "'a => 'b partial" ("gn'_proj/'(_')" [3] 999)
X_inv :: "NonZero => NonZero" ("inv''/'(_')" [3] 999)
X_isSubtype :: "('s => 't) => ('t => 's) => bool" ("isSubtype/'(_,/ _')" [3,3] 999)
X_isSubtypeWithPred :: "('t => bool) => ('s => 't) => ('t => 's) => bool" ("isSubtypeWithPred/'(_,/ _,/ _')" [3,3,3] 999)

axioms
subtype_def [rule_format] :
"ALL injection.
 ALL projection.
 isSubtype(injection, projection) =
 (ALL x. x = projection (injection x))"

subtype_pred_def [rule_format] :
"ALL P.
 ALL injection.
 ALL projection.
 isSubtypeWithPred(P, injection, projection) =
 ((isSubtype(injection, projection) &
   (ALL x. P x --> x = injection (projection x))) &
  (ALL x. P (injection x)))"

ga_assoc___Xx__ [rule_format] :
"ALL x. ALL y. ALL z. (x +'' y) +'' z = x +'' (y +'' z)"

ga_right_unit___Xx__ [rule_format] : "ALL x. x +'' 0'' = x"

ga_left_unit___Xx__ [rule_format] : "ALL x. 0'' +'' x = x"

inv_Group [rule_format] : "ALL x. -'' x +'' x = 0''"

rinv_Group [rule_format] : "ALL x. x +'' -'' x = 0''"

ga_comm___Xx__ [rule_format] : "ALL x. ALL y. x +'' y = y +'' x"

binary_inverse [rule_format] : "ALL x. ALL y. x -' y = x +'' -'' y"

ga_assoc___Xx___2 [rule_format] :
"ALL x. ALL y. ALL z. (x +' y) +' z = x +' (y +' z)"

ga_right_unit___Xx___2 [rule_format] : "ALL x. x +' 0' = x"

ga_left_unit___Xx___2 [rule_format] : "ALL x. 0' +' x = x"

inv_Group_1 [rule_format] : "ALL x. -' x +' x = 0'"

rinv_Group_1 [rule_format] : "ALL x. x +' -' x = 0'"

ga_comm___Xx___2 [rule_format] : "ALL x. ALL y. x +' y = y +' x"

ga_assoc___Xx___1 [rule_format] :
"ALL x. ALL y. ALL z. (x *' y) *' z = x *' (y *' z)"

ga_right_unit___Xx___1 [rule_format] : "ALL x. x *' 1' = x"

ga_left_unit___Xx___1 [rule_format] : "ALL x. 1' *' x = x"

distr1_Ring [rule_format] :
"ALL x. ALL y. ALL z. (x +' y) *' z = (x *' z) +' (y *' z)"

distr2_Ring [rule_format] :
"ALL x. ALL y. ALL z. z *' (x +' y) = (z *' x) +' (z *' y)"

ga_comm___Xx___1 [rule_format] : "ALL x. ALL y. x *' y = y *' x"

noZeroDiv [rule_format] :
"ALL x. ALL y. x *' y = 0' --> x = 0' | y = 0'"

zeroNeqOne [rule_format] : "~ 1' = 0'"

Ax1 [rule_format] : "ALL x. defOp (gn_proj(x)) = (~ x = 0')"

ga_assoc___Xx___2_1 [rule_format] :
"ALL x. ALL y. ALL z. (x *_3 y) *_3 z = x *_3 (y *_3 z)"

ga_right_unit___Xx___2_1 [rule_format] : "ALL x. x *_3 1'' = x"

ga_left_unit___Xx___2_1 [rule_format] : "ALL x. 1'' *_3 x = x"

inv_Group_1_1 [rule_format] : "ALL x. inv'(x) *_3 x = 1''"

rinv_Group_1_1 [rule_format] : "ALL x. x *_3 inv'(x) = 1''"

unit [rule_format] : "ALL x. gn_inj(1'') *'' x = x"

mix_assoc [rule_format] :
"ALL r. ALL s. ALL x. (r *' s) *'' x = r *'' (s *'' x)"

distr_Field [rule_format] :
"ALL r. ALL s. ALL x. (r +' s) *'' x = (r *'' x) +'' (s *'' x)"

distr_Space [rule_format] :
"ALL r. ALL x. ALL y. r *'' (x +'' y) = (r *'' x) +'' (r *'' y)"

declare ga_assoc___Xx__ [simp]
declare ga_right_unit___Xx__ [simp]
declare ga_left_unit___Xx__ [simp]
declare inv_Group [simp]
declare rinv_Group [simp]
declare ga_comm___Xx__ [simp]
declare ga_assoc___Xx___2 [simp]
declare ga_right_unit___Xx___2 [simp]
declare ga_left_unit___Xx___2 [simp]
declare inv_Group_1 [simp]
declare rinv_Group_1 [simp]
declare ga_comm___Xx___2 [simp]
declare ga_assoc___Xx___1 [simp]
declare ga_right_unit___Xx___1 [simp]
declare ga_left_unit___Xx___1 [simp]
declare ga_comm___Xx___1 [simp]
declare ga_assoc___Xx___2_1 [simp]
declare ga_right_unit___Xx___2_1 [simp]
declare ga_left_unit___Xx___2_1 [simp]
declare inv_Group_1_1 [simp]
declare rinv_Group_1_1 [simp]
declare unit [simp]

theorem zero_by_left_zero : "ALL x. 0' *'' x = 0''"
proof
  fix x
  have "0' = 0' +' 0'" by simp
  hence "0' *'' x = (0' +' 0') *'' x" by simp
  hence fact: "0' *'' x = (0' *'' x) +'' (0' *'' x)" by (simp only: distr_Field)
  have "0'' = -''(0' *'' x) +'' (0' *'' x)" by simp
  also from fact have "\<dots> = -''(0' *'' x) +'' ((0' *'' x) +'' (0' *'' x))" by simp
  also have "\<dots> = (-''(0' *'' x) +'' (0' *'' x)) +'' (0' *'' x)" by (simp only: ga_assoc___Xx__)
  finally show "0' *'' x = 0''"
    by (simp  only: ga_left_unit___Xx__ inv_Group)
qed

ML "Header.record \"zero_by_left_zero\""

theorem zero_by_right_zero : "ALL r. r *'' 0'' = 0''"
using subtype_def subtype_pred_def by (auto)

ML "Header.record \"zero_by_right_zero\""

-- "the 1'' shouldn't be exported, but only 1' at its place!"
theorem inverse_by_XMinus1 : "ALL x. -' gn_inj(1'') *'' x = -'' x"
using subtype_def subtype_pred_def by (auto)

ML "Header.record \"inverse_by_XMinus1\""

end


(*  Title:      HOL/Library/AList.thy
    Author:     Norbert Schirmer, Tobias Nipkow, Martin Wildmoser, TU Muenchen
*)

section \<open>Implementation of Association Lists\<close>

theory AList
  imports Main
begin

context
begin

text \<open>
  The operations preserve distinctness of keys and
  function \<^term>\<open>clearjunk\<close> distributes over them. Since
  \<^term>\<open>clearjunk\<close> enforces distinctness of keys it can be used
  to establish the invariant, e.g. for inductive proofs.
\<close>

subsection \<open>\<open>update\<close> and \<open>updates\<close>\<close>

qualified primrec update :: "'key \<Rightarrow> 'val \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "update k v [] = [(k, v)]"
  | "update k v (p # ps) = (if fst p = k then (k, v) # ps else p # update k v ps)"

lemma update_conv': "map_of (update k v al)  = (map_of al)(k\<mapsto>v)"
  by (induct al) (auto simp add: fun_eq_iff)

corollary update_conv: "map_of (update k v al) k' = ((map_of al)(k\<mapsto>v)) k'"
  by (simp add: update_conv')

lemma dom_update: "fst ` set (update k v al) = {k} \<union> fst ` set al"
  by (induct al) auto

lemma update_keys:
  "map fst (update k v al) =
    (if k \<in> set (map fst al) then map fst al else map fst al @ [k])"
  by (induct al) simp_all

lemma distinct_update:
  assumes "distinct (map fst al)"
  shows "distinct (map fst (update k v al))"
  using assms by (simp add: update_keys)

lemma update_filter:
  "a \<noteq> k \<Longrightarrow> update k v [q\<leftarrow>ps. fst q \<noteq> a] = [q\<leftarrow>update k v ps. fst q \<noteq> a]"
  by (induct ps) auto

lemma update_triv: "map_of al k = Some v \<Longrightarrow> update k v al = al"
  by (induct al) auto

lemma update_nonempty [simp]: "update k v al \<noteq> []"
  by (induct al) auto

lemma update_eqD: "update k v al = update k v' al' \<Longrightarrow> v = v'"
proof (induct al arbitrary: al')
  case Nil
  then show ?case
    by (cases al') (auto split: if_split_asm)
next
  case Cons
  then show ?case
    by (cases al') (auto split: if_split_asm)
qed

lemma update_last [simp]: "update k v (update k v' al) = update k v al"
  by (induct al) auto

text \<open>Note that the lists are not necessarily the same:
        \<^term>\<open>update k v (update k' v' []) = [(k', v'), (k, v)]\<close> and
        \<^term>\<open>update k' v' (update k v []) = [(k, v), (k', v')]\<close>.\<close>

lemma update_swap:
  "k \<noteq> k' \<Longrightarrow> map_of (update k v (update k' v' al)) = map_of (update k' v' (update k v al))"
  by (simp add: update_conv' fun_eq_iff)

lemma update_Some_unfold:
  "map_of (update k v al) x = Some y \<longleftrightarrow>
    x = k \<and> v = y \<or> x \<noteq> k \<and> map_of al x = Some y"
  by (simp add: update_conv' map_upd_Some_unfold)

lemma image_update [simp]: "x \<notin> A \<Longrightarrow> map_of (update x y al) ` A = map_of al ` A"
  by (auto simp add: update_conv')

qualified definition updates ::
    "'key list \<Rightarrow> 'val list \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where "updates ks vs = fold (case_prod update) (zip ks vs)"

lemma updates_simps [simp]:
  "updates [] vs ps = ps"
  "updates ks [] ps = ps"
  "updates (k#ks) (v#vs) ps = updates ks vs (update k v ps)"
  by (simp_all add: updates_def)

lemma updates_key_simp [simp]:
  "updates (k # ks) vs ps =
    (case vs of [] \<Rightarrow> ps | v # vs \<Rightarrow> updates ks vs (update k v ps))"
  by (cases vs) simp_all

lemma updates_conv': "map_of (updates ks vs al) = (map_of al)(ks[\<mapsto>]vs)"
proof -
  have "map_of \<circ> fold (case_prod update) (zip ks vs) =
      fold (\<lambda>(k, v) f. f(k \<mapsto> v)) (zip ks vs) \<circ> map_of"
    by (rule fold_commute) (auto simp add: fun_eq_iff update_conv')
  then show ?thesis
    by (auto simp add: updates_def fun_eq_iff map_upds_fold_map_upd foldl_conv_fold split_def)
qed

lemma updates_conv: "map_of (updates ks vs al) k = ((map_of al)(ks[\<mapsto>]vs)) k"
  by (simp add: updates_conv')

lemma distinct_updates:
  assumes "distinct (map fst al)"
  shows "distinct (map fst (updates ks vs al))"
proof -
  have "distinct (fold
       (\<lambda>(k, v) al. if k \<in> set al then al else al @ [k])
       (zip ks vs) (map fst al))"
    by (rule fold_invariant [of "zip ks vs" "\<lambda>_. True"]) (auto intro: assms)
  moreover have "map fst \<circ> fold (case_prod update) (zip ks vs) =
      fold (\<lambda>(k, v) al. if k \<in> set al then al else al @ [k]) (zip ks vs) \<circ> map fst"
    by (rule fold_commute) (simp add: update_keys split_def case_prod_beta comp_def)
  ultimately show ?thesis
    by (simp add: updates_def fun_eq_iff)
qed

lemma updates_append1[simp]: "size ks < size vs \<Longrightarrow>
    updates (ks@[k]) vs al = update k (vs!size ks) (updates ks vs al)"
  by (induct ks arbitrary: vs al) (auto split: list.splits)

lemma updates_list_update_drop[simp]:
  "size ks \<le> i \<Longrightarrow> i < size vs \<Longrightarrow>
    updates ks (vs[i:=v]) al = updates ks vs al"
  by (induct ks arbitrary: al vs i) (auto split: list.splits nat.splits)

lemma update_updates_conv_if:
  "map_of (updates xs ys (update x y al)) =
    map_of
     (if x \<in> set (take (length ys) xs)
      then updates xs ys al
      else (update x y (updates xs ys al)))"
  by (simp add: updates_conv' update_conv' map_upd_upds_conv_if)

lemma updates_twist [simp]:
  "k \<notin> set ks \<Longrightarrow>
    map_of (updates ks vs (update k v al)) = map_of (update k v (updates ks vs al))"
  by (simp add: updates_conv' update_conv')

lemma updates_apply_notin [simp]:
  "k \<notin> set ks \<Longrightarrow> map_of (updates ks vs al) k = map_of al k"
  by (simp add: updates_conv)

lemma updates_append_drop [simp]:
  "size xs = size ys \<Longrightarrow> updates (xs @ zs) ys al = updates xs ys al"
  by (induct xs arbitrary: ys al) (auto split: list.splits)

lemma updates_append2_drop [simp]:
  "size xs = size ys \<Longrightarrow> updates xs (ys @ zs) al = updates xs ys al"
  by (induct xs arbitrary: ys al) (auto split: list.splits)

subsection \<open>\<open>delete\<close>\<close>

qualified definition delete :: "'key \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where delete_eq: "delete k = filter (\<lambda>(k', _). k \<noteq> k')"

lemma delete_simps [simp]:
  "delete k [] = []"
  "delete k (p # ps) = (if fst p = k then delete k ps else p # delete k ps)"
  by (auto simp add: delete_eq)

lemma delete_conv': "map_of (delete k al) = (map_of al)(k := None)"
  by (induct al) (auto simp add: fun_eq_iff)

corollary delete_conv: "map_of (delete k al) k' = ((map_of al)(k := None)) k'"
  by (simp add: delete_conv')

lemma delete_keys: "map fst (delete k al) = removeAll k (map fst al)"
  by (simp add: delete_eq removeAll_filter_not_eq filter_map split_def comp_def)

lemma distinct_delete:
  assumes "distinct (map fst al)"
  shows "distinct (map fst (delete k al))"
  using assms by (simp add: delete_keys distinct_removeAll)

lemma delete_id [simp]: "k \<notin> fst ` set al \<Longrightarrow> delete k al = al"
  by (auto simp add: image_iff delete_eq filter_id_conv)

lemma delete_idem: "delete k (delete k al) = delete k al"
  by (simp add: delete_eq)

lemma map_of_delete [simp]: "k' \<noteq> k \<Longrightarrow> map_of (delete k al) k' = map_of al k'"
  by (simp add: delete_conv')

lemma delete_notin_dom: "k \<notin> fst ` set (delete k al)"
  by (auto simp add: delete_eq)

lemma dom_delete_subset: "fst ` set (delete k al) \<subseteq> fst ` set al"
  by (auto simp add: delete_eq)

lemma delete_update_same: "delete k (update k v al) = delete k al"
  by (induct al) simp_all

lemma delete_update: "k \<noteq> l \<Longrightarrow> delete l (update k v al) = update k v (delete l al)"
  by (induct al) simp_all

lemma delete_twist: "delete x (delete y al) = delete y (delete x al)"
  by (simp add: delete_eq conj_commute)

lemma length_delete_le: "length (delete k al) \<le> length al"
  by (simp add: delete_eq)

subsection \<open>\<open>update_with_aux\<close> and \<open>delete_aux\<close>\<close>

qualified primrec update_with_aux ::
    "'val \<Rightarrow> 'key \<Rightarrow> ('val \<Rightarrow> 'val) \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "update_with_aux v k f [] = [(k, f v)]"
  | "update_with_aux v k f (p # ps) =
      (if (fst p = k) then (k, f (snd p)) # ps else p # update_with_aux v k f ps)"

text \<open>
  The above \<^term>\<open>delete\<close> traverses all the list even if it has found the key.
  This one does not have to keep going because is assumes the invariant that keys are distinct.
\<close>
qualified fun delete_aux :: "'key \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "delete_aux k [] = []"
  | "delete_aux k ((k', v) # xs) = (if k = k' then xs else (k', v) # delete_aux k xs)"

lemma map_of_update_with_aux':
  "map_of (update_with_aux v k f ps) k' =
    ((map_of ps)(k \<mapsto> (case map_of ps k of None \<Rightarrow> f v | Some v \<Rightarrow> f v))) k'"
  by (induct ps) auto

lemma map_of_update_with_aux:
  "map_of (update_with_aux v k f ps) =
    (map_of ps)(k \<mapsto> (case map_of ps k of None \<Rightarrow> f v | Some v \<Rightarrow> f v))"
  by (simp add: fun_eq_iff map_of_update_with_aux')

lemma dom_update_with_aux: "fst ` set (update_with_aux v k f ps) = {k} \<union> fst ` set ps"
  by (induct ps) auto

lemma distinct_update_with_aux [simp]:
  "distinct (map fst (update_with_aux v k f ps)) = distinct (map fst ps)"
  by (induct ps) (auto simp add: dom_update_with_aux)

lemma set_update_with_aux:
  "distinct (map fst xs) \<Longrightarrow>
    set (update_with_aux v k f xs) =
      (set xs - {k} \<times> UNIV \<union> {(k, f (case map_of xs k of None \<Rightarrow> v | Some v \<Rightarrow> v))})"
  by (induct xs) (auto intro: rev_image_eqI)

lemma set_delete_aux: "distinct (map fst xs) \<Longrightarrow> set (delete_aux k xs) = set xs - {k} \<times> UNIV"
  apply (induct xs)
   apply simp_all
  apply clarsimp
  apply (fastforce intro: rev_image_eqI)
  done

lemma dom_delete_aux: "distinct (map fst ps) \<Longrightarrow> fst ` set (delete_aux k ps) = fst ` set ps - {k}"
  by (auto simp add: set_delete_aux)

lemma distinct_delete_aux [simp]: "distinct (map fst ps) \<Longrightarrow> distinct (map fst (delete_aux k ps))"
proof (induct ps)
  case Nil
  then show ?case by simp
next
  case (Cons a ps)
  obtain k' v where a: "a = (k', v)"
    by (cases a)
  show ?case
  proof (cases "k' = k")
    case True
    with Cons a show ?thesis by simp
  next
    case False
    with Cons a have "k' \<notin> fst ` set ps" "distinct (map fst ps)"
      by simp_all
    with False a have "k' \<notin> fst ` set (delete_aux k ps)"
      by (auto dest!: dom_delete_aux[where k=k])
    with Cons a show ?thesis
      by simp
  qed
qed

lemma map_of_delete_aux':
  "distinct (map fst xs) \<Longrightarrow> map_of (delete_aux k xs) = (map_of xs)(k := None)"
  apply (induct xs)
   apply (fastforce simp add: map_of_eq_None_iff fun_upd_twist)
  apply (auto intro!: ext)
  apply (simp add: map_of_eq_None_iff)
  done

lemma map_of_delete_aux:
  "distinct (map fst xs) \<Longrightarrow> map_of (delete_aux k xs) k' = ((map_of xs)(k := None)) k'"
  by (simp add: map_of_delete_aux')

lemma delete_aux_eq_Nil_conv: "delete_aux k ts = [] \<longleftrightarrow> ts = [] \<or> (\<exists>v. ts = [(k, v)])"
  by (cases ts) (auto split: if_split_asm)

subsection \<open>\<open>restrict\<close>\<close>

qualified definition restrict :: "'key set \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where restrict_eq: "restrict A = filter (\<lambda>(k, v). k \<in> A)"

lemma restr_simps [simp]:
  "restrict A [] = []"
  "restrict A (p#ps) = (if fst p \<in> A then p # restrict A ps else restrict A ps)"
  by (auto simp add: restrict_eq)

lemma restr_conv': "map_of (restrict A al) = ((map_of al)|` A)"
proof
  show "map_of (restrict A al) k = ((map_of al)|` A) k" for k
    apply (induct al)
     apply simp
    apply (cases "k \<in> A")
     apply auto
    done
qed

corollary restr_conv: "map_of (restrict A al) k = ((map_of al)|` A) k"
  by (simp add: restr_conv')

lemma distinct_restr: "distinct (map fst al) \<Longrightarrow> distinct (map fst (restrict A al))"
  by (induct al) (auto simp add: restrict_eq)

lemma restr_empty [simp]:
  "restrict {} al = []"
  "restrict A [] = []"
  by (induct al) (auto simp add: restrict_eq)

lemma restr_in [simp]: "x \<in> A \<Longrightarrow> map_of (restrict A al) x = map_of al x"
  by (simp add: restr_conv')

lemma restr_out [simp]: "x \<notin> A \<Longrightarrow> map_of (restrict A al) x = None"
  by (simp add: restr_conv')

lemma dom_restr [simp]: "fst ` set (restrict A al) = fst ` set al \<inter> A"
  by (induct al) (auto simp add: restrict_eq)

lemma restr_upd_same [simp]: "restrict (-{x}) (update x y al) = restrict (-{x}) al"
  by (induct al) (auto simp add: restrict_eq)

lemma restr_restr [simp]: "restrict A (restrict B al) = restrict (A\<inter>B) al"
  by (induct al) (auto simp add: restrict_eq)

lemma restr_update[simp]:
  "map_of (restrict D (update x y al)) =
    map_of ((if x \<in> D then (update x y (restrict (D-{x}) al)) else restrict D al))"
  by (simp add: restr_conv' update_conv')

lemma restr_delete [simp]:
  "delete x (restrict D al) = (if x \<in> D then restrict (D - {x}) al else restrict D al)"
  apply (simp add: delete_eq restrict_eq)
  apply (auto simp add: split_def)
proof -
  have "y \<noteq> x \<longleftrightarrow> x \<noteq> y" for y
    by auto
  then show "[p \<leftarrow> al. fst p \<in> D \<and> x \<noteq> fst p] = [p \<leftarrow> al. fst p \<in> D \<and> fst p \<noteq> x]"
    by simp
  assume "x \<notin> D"
  then have "y \<in> D \<longleftrightarrow> y \<in> D \<and> x \<noteq> y" for y
    by auto
  then show "[p \<leftarrow> al . fst p \<in> D \<and> x \<noteq> fst p] = [p \<leftarrow> al . fst p \<in> D]"
    by simp
qed

lemma update_restr:
  "map_of (update x y (restrict D al)) = map_of (update x y (restrict (D - {x}) al))"
  by (simp add: update_conv' restr_conv') (rule fun_upd_restrict)

lemma update_restr_conv [simp]:
  "x \<in> D \<Longrightarrow>
    map_of (update x y (restrict D al)) = map_of (update x y (restrict (D - {x}) al))"
  by (simp add: update_conv' restr_conv')

lemma restr_updates [simp]:
  "length xs = length ys \<Longrightarrow> set xs \<subseteq> D \<Longrightarrow>
    map_of (restrict D (updates xs ys al)) =
      map_of (updates xs ys (restrict (D - set xs) al))"
  by (simp add: updates_conv' restr_conv')

lemma restr_delete_twist: "(restrict A (delete a ps)) = delete a (restrict A ps)"
  by (induct ps) auto

subsection \<open>\<open>clearjunk\<close>\<close>

qualified function clearjunk  :: "('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "clearjunk [] = []"
  | "clearjunk (p#ps) = p # clearjunk (delete (fst p) ps)"
  by pat_completeness auto
termination
  by (relation "measure length") (simp_all add: less_Suc_eq_le length_delete_le)

lemma map_of_clearjunk: "map_of (clearjunk al) = map_of al"
  by (induct al rule: clearjunk.induct) (simp_all add: fun_eq_iff)

lemma clearjunk_keys_set: "set (map fst (clearjunk al)) = set (map fst al)"
  by (induct al rule: clearjunk.induct) (simp_all add: delete_keys)

lemma dom_clearjunk: "fst ` set (clearjunk al) = fst ` set al"
  using clearjunk_keys_set by simp

lemma distinct_clearjunk [simp]: "distinct (map fst (clearjunk al))"
  by (induct al rule: clearjunk.induct) (simp_all del: set_map add: clearjunk_keys_set delete_keys)

lemma ran_clearjunk: "ran (map_of (clearjunk al)) = ran (map_of al)"
  by (simp add: map_of_clearjunk)

lemma ran_map_of: "ran (map_of al) = snd ` set (clearjunk al)"
proof -
  have "ran (map_of al) = ran (map_of (clearjunk al))"
    by (simp add: ran_clearjunk)
  also have "\<dots> = snd ` set (clearjunk al)"
    by (simp add: ran_distinct)
  finally show ?thesis .
qed

lemma graph_map_of: "Map.graph (map_of al) = set (clearjunk al)"
  by (metis distinct_clearjunk graph_map_of_if_distinct_dom map_of_clearjunk)

lemma clearjunk_update: "clearjunk (update k v al) = update k v (clearjunk al)"
  by (induct al rule: clearjunk.induct) (simp_all add: delete_update)

lemma clearjunk_updates: "clearjunk (updates ks vs al) = updates ks vs (clearjunk al)"
proof -
  have "clearjunk \<circ> fold (case_prod update) (zip ks vs) =
      fold (case_prod update) (zip ks vs) \<circ> clearjunk"
    by (rule fold_commute) (simp add: clearjunk_update case_prod_beta o_def)
  then show ?thesis
    by (simp add: updates_def fun_eq_iff)
qed

lemma clearjunk_delete: "clearjunk (delete x al) = delete x (clearjunk al)"
  by (induct al rule: clearjunk.induct) (auto simp add: delete_idem delete_twist)

lemma clearjunk_restrict: "clearjunk (restrict A al) = restrict A (clearjunk al)"
  by (induct al rule: clearjunk.induct) (auto simp add: restr_delete_twist)

lemma distinct_clearjunk_id [simp]: "distinct (map fst al) \<Longrightarrow> clearjunk al = al"
  by (induct al rule: clearjunk.induct) auto

lemma clearjunk_idem: "clearjunk (clearjunk al) = clearjunk al"
  by simp

lemma length_clearjunk: "length (clearjunk al) \<le> length al"
proof (induct al rule: clearjunk.induct [case_names Nil Cons])
  case Nil
  then show ?case by simp
next
  case (Cons kv al)
  moreover have "length (delete (fst kv) al) \<le> length al"
    by (fact length_delete_le)
  ultimately have "length (clearjunk (delete (fst kv) al)) \<le> length al"
    by (rule order_trans)
  then show ?case
    by simp
qed

lemma delete_map:
  assumes "\<And>kv. fst (f kv) = fst kv"
  shows "delete k (map f ps) = map f (delete k ps)"
  by (simp add: delete_eq filter_map comp_def split_def assms)

lemma clearjunk_map:
  assumes "\<And>kv. fst (f kv) = fst kv"
  shows "clearjunk (map f ps) = map f (clearjunk ps)"
  by (induct ps rule: clearjunk.induct [case_names Nil Cons])
    (simp_all add: clearjunk_delete delete_map assms)

subsection \<open>\<open>map_ran\<close>\<close>

definition map_ran :: "('key \<Rightarrow> 'val1 \<Rightarrow> 'val2) \<Rightarrow> ('key \<times> 'val1) list \<Rightarrow> ('key \<times> 'val2) list"
  where "map_ran f = map (\<lambda>(k, v). (k, f k v))"

lemma map_ran_simps [simp]:
  "map_ran f [] = []"
  "map_ran f ((k, v) # ps) = (k, f k v) # map_ran f ps"
  by (simp_all add: map_ran_def)

lemma map_ran_Cons_sel: "map_ran f (p # ps) = (fst p, f (fst p) (snd p)) # map_ran f ps"
  by (simp add: map_ran_def case_prod_beta)

lemma length_map_ran[simp]: "length (map_ran f al) = length al"
  by (simp add: map_ran_def)

lemma map_fst_map_ran[simp]: "map fst (map_ran f al) = map fst al"
  by (simp add: map_ran_def case_prod_beta)

lemma dom_map_ran: "fst ` set (map_ran f al) = fst ` set al"
  by (simp add: map_ran_def image_image split_def)

lemma map_ran_conv: "map_of (map_ran f al) k = map_option (f k) (map_of al k)"
  by (induct al) auto

lemma distinct_map_ran: "distinct (map fst al) \<Longrightarrow> distinct (map fst (map_ran f al))"
  by simp

lemma map_ran_filter: "map_ran f [p\<leftarrow>ps. fst p \<noteq> a] = [p\<leftarrow>map_ran f ps. fst p \<noteq> a]"
  by (simp add: map_ran_def filter_map split_def comp_def)

lemma clearjunk_map_ran: "clearjunk (map_ran f al) = map_ran f (clearjunk al)"
  by (simp add: map_ran_def split_def clearjunk_map)

subsection \<open>\<open>merge\<close>\<close>

qualified definition merge :: "('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where "merge qs ps = foldr (\<lambda>(k, v). update k v) ps qs"

lemma merge_simps [simp]:
  "merge qs [] = qs"
  "merge qs (p#ps) = update (fst p) (snd p) (merge qs ps)"
  by (simp_all add: merge_def split_def)

lemma merge_updates: "merge qs ps = updates (rev (map fst ps)) (rev (map snd ps)) qs"
  by (simp add: merge_def updates_def foldr_conv_fold zip_rev zip_map_fst_snd)

lemma dom_merge: "fst ` set (merge xs ys) = fst ` set xs \<union> fst ` set ys"
  by (induct ys arbitrary: xs) (auto simp add: dom_update)

lemma distinct_merge: "distinct (map fst xs) \<Longrightarrow> distinct (map fst (merge xs ys))"
  by (simp add: merge_updates distinct_updates)

lemma clearjunk_merge: "clearjunk (merge xs ys) = merge (clearjunk xs) ys"
  by (simp add: merge_updates clearjunk_updates)

lemma merge_conv': "map_of (merge xs ys) = map_of xs ++ map_of ys"
proof -
  have "map_of \<circ> fold (case_prod update) (rev ys) =
      fold (\<lambda>(k, v) m. m(k \<mapsto> v)) (rev ys) \<circ> map_of"
    by (rule fold_commute) (simp add: update_conv' case_prod_beta split_def fun_eq_iff)
  then show ?thesis
    by (simp add: merge_def map_add_map_of_foldr foldr_conv_fold fun_eq_iff)
qed

corollary merge_conv: "map_of (merge xs ys) k = (map_of xs ++ map_of ys) k"
  by (simp add: merge_conv')

lemma merge_empty: "map_of (merge [] ys) = map_of ys"
  by (simp add: merge_conv')

lemma merge_assoc [simp]: "map_of (merge m1 (merge m2 m3)) = map_of (merge (merge m1 m2) m3)"
  by (simp add: merge_conv')

lemma merge_Some_iff:
  "map_of (merge m n) k = Some x \<longleftrightarrow>
    map_of n k = Some x \<or> map_of n k = None \<and> map_of m k = Some x"
  by (simp add: merge_conv' map_add_Some_iff)

lemmas merge_SomeD [dest!] = merge_Some_iff [THEN iffD1]

lemma merge_find_right [simp]: "map_of n k = Some v \<Longrightarrow> map_of (merge m n) k = Some v"
  by (simp add: merge_conv')

lemma merge_None [iff]: "(map_of (merge m n) k = None) = (map_of n k = None \<and> map_of m k = None)"
  by (simp add: merge_conv')

lemma merge_upd [simp]: "map_of (merge m (update k v n)) = map_of (update k v (merge m n))"
  by (simp add: update_conv' merge_conv')

lemma merge_updatess [simp]:
  "map_of (merge m (updates xs ys n)) = map_of (updates xs ys (merge m n))"
  by (simp add: updates_conv' merge_conv')

lemma merge_append: "map_of (xs @ ys) = map_of (merge ys xs)"
  by (simp add: merge_conv')

subsection \<open>\<open>compose\<close>\<close>

qualified function compose :: "('key \<times> 'a) list \<Rightarrow> ('a \<times> 'b) list \<Rightarrow> ('key \<times> 'b) list"
  where
    "compose [] ys = []"
  | "compose (x # xs) ys =
      (case map_of ys (snd x) of
        None \<Rightarrow> compose (delete (fst x) xs) ys
      | Some v \<Rightarrow> (fst x, v) # compose xs ys)"
  by pat_completeness auto
termination
  by (relation "measure (length \<circ> fst)") (simp_all add: less_Suc_eq_le length_delete_le)

lemma compose_first_None [simp]: "map_of xs k = None \<Longrightarrow> map_of (compose xs ys) k = None"
  by (induct xs ys rule: compose.induct) (auto split: option.splits if_split_asm)

lemma compose_conv: "map_of (compose xs ys) k = (map_of ys \<circ>\<^sub>m map_of xs) k"
proof (induct xs ys rule: compose.induct)
  case 1
  then show ?case by simp
next
  case (2 x xs ys)
  show ?case
  proof (cases "map_of ys (snd x)")
    case None
    with 2 have hyp: "map_of (compose (delete (fst x) xs) ys) k =
        (map_of ys \<circ>\<^sub>m map_of (delete (fst x) xs)) k"
      by simp
    show ?thesis
    proof (cases "fst x = k")
      case True
      from True delete_notin_dom [of k xs]
      have "map_of (delete (fst x) xs) k = None"
        by (simp add: map_of_eq_None_iff)
      with hyp show ?thesis
        using True None
        by simp
    next
      case False
      from False have "map_of (delete (fst x) xs) k = map_of xs k"
        by simp
      with hyp show ?thesis
        using False None by (simp add: map_comp_def)
    qed
  next
    case (Some v)
    with 2
    have "map_of (compose xs ys) k = (map_of ys \<circ>\<^sub>m map_of xs) k"
      by simp
    with Some show ?thesis
      by (auto simp add: map_comp_def)
  qed
qed

lemma compose_conv': "map_of (compose xs ys) = (map_of ys \<circ>\<^sub>m map_of xs)"
  by (rule ext) (rule compose_conv)

lemma compose_first_Some [simp]: "map_of xs k = Some v \<Longrightarrow> map_of (compose xs ys) k = map_of ys v"
  by (simp add: compose_conv)

lemma dom_compose: "fst ` set (compose xs ys) \<subseteq> fst ` set xs"
proof (induct xs ys rule: compose.induct)
  case 1
  then show ?case by simp
next
  case (2 x xs ys)
  show ?case
  proof (cases "map_of ys (snd x)")
    case None
    with "2.hyps" have "fst ` set (compose (delete (fst x) xs) ys) \<subseteq> fst ` set (delete (fst x) xs)"
      by simp
    also have "\<dots> \<subseteq> fst ` set xs"
      by (rule dom_delete_subset)
    finally show ?thesis
      using None by auto
  next
    case (Some v)
    with "2.hyps" have "fst ` set (compose xs ys) \<subseteq> fst ` set xs"
      by simp
    with Some show ?thesis
      by auto
  qed
qed

lemma distinct_compose:
  assumes "distinct (map fst xs)"
  shows "distinct (map fst (compose xs ys))"
  using assms
proof (induct xs ys rule: compose.induct)
  case 1
  then show ?case by simp
next
  case (2 x xs ys)
  show ?case
  proof (cases "map_of ys (snd x)")
    case None
    with 2 show ?thesis by simp
  next
    case (Some v)
    with 2 dom_compose [of xs ys] show ?thesis
      by auto
  qed
qed

lemma compose_delete_twist: "compose (delete k xs) ys = delete k (compose xs ys)"
proof (induct xs ys rule: compose.induct)
  case 1
  then show ?case by simp
next
  case (2 x xs ys)
  show ?case
  proof (cases "map_of ys (snd x)")
    case None
    with 2 have hyp: "compose (delete k (delete (fst x) xs)) ys =
        delete k (compose (delete (fst x) xs) ys)"
      by simp
    show ?thesis
    proof (cases "fst x = k")
      case True
      with None hyp show ?thesis
        by (simp add: delete_idem)
    next
      case False
      from None False hyp show ?thesis
        by (simp add: delete_twist)
    qed
  next
    case (Some v)
    with 2 have hyp: "compose (delete k xs) ys = delete k (compose xs ys)"
      by simp
    with Some show ?thesis
      by simp
  qed
qed

lemma compose_clearjunk: "compose xs (clearjunk ys) = compose xs ys"
  by (induct xs ys rule: compose.induct)
    (auto simp add: map_of_clearjunk split: option.splits)

lemma clearjunk_compose: "clearjunk (compose xs ys) = compose (clearjunk xs) ys"
  by (induct xs rule: clearjunk.induct)
    (auto split: option.splits simp add: clearjunk_delete delete_idem compose_delete_twist)

lemma compose_empty [simp]: "compose xs [] = []"
  by (induct xs) (auto simp add: compose_delete_twist)

lemma compose_Some_iff:
  "(map_of (compose xs ys) k = Some v) \<longleftrightarrow>
    (\<exists>k'. map_of xs k = Some k' \<and> map_of ys k' = Some v)"
  by (simp add: compose_conv map_comp_Some_iff)

lemma map_comp_None_iff:
  "map_of (compose xs ys) k = None \<longleftrightarrow>
    (map_of xs k = None \<or> (\<exists>k'. map_of xs k = Some k' \<and> map_of ys k' = None))"
  by (simp add: compose_conv map_comp_None_iff)

subsection \<open>\<open>map_entry\<close>\<close>

qualified fun map_entry :: "'key \<Rightarrow> ('val \<Rightarrow> 'val) \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "map_entry k f [] = []"
  | "map_entry k f (p # ps) =
      (if fst p = k then (k, f (snd p)) # ps else p # map_entry k f ps)"

lemma map_of_map_entry:
  "map_of (map_entry k f xs) =
    (map_of xs)(k := case map_of xs k of None \<Rightarrow> None | Some v' \<Rightarrow> Some (f v'))"
  by (induct xs) auto

lemma dom_map_entry: "fst ` set (map_entry k f xs) = fst ` set xs"
  by (induct xs) auto

lemma distinct_map_entry:
  assumes "distinct (map fst xs)"
  shows "distinct (map fst (map_entry k f xs))"
  using assms by (induct xs) (auto simp add: dom_map_entry)

subsection \<open>\<open>map_default\<close>\<close>

fun map_default :: "'key \<Rightarrow> 'val \<Rightarrow> ('val \<Rightarrow> 'val) \<Rightarrow> ('key \<times> 'val) list \<Rightarrow> ('key \<times> 'val) list"
  where
    "map_default k v f [] = [(k, v)]"
  | "map_default k v f (p # ps) =
      (if fst p = k then (k, f (snd p)) # ps else p # map_default k v f ps)"

lemma map_of_map_default:
  "map_of (map_default k v f xs) =
    (map_of xs)(k := case map_of xs k of None \<Rightarrow> Some v | Some v' \<Rightarrow> Some (f v'))"
  by (induct xs) auto

lemma dom_map_default: "fst ` set (map_default k v f xs) = insert k (fst ` set xs)"
  by (induct xs) auto

lemma distinct_map_default:
  assumes "distinct (map fst xs)"
  shows "distinct (map fst (map_default k v f xs))"
  using assms by (induct xs) (auto simp add: dom_map_default)

end

end


(*  Title:      HOL/ex/Hex_Bin_Examples.thy
    Author:     Gerwin Klein, NICTA
*)

section {* Examples for hexadecimal and binary numerals *}

theory Hex_Bin_Examples imports Main
begin

text "Hex and bin numerals can be used like normal decimal numerals in input"
lemma "0xFF = 255" by (rule refl)
lemma "0xF = 0b1111" by (rule refl)

text {*
  Just like decimal numeral they are polymorphic, for arithmetic
  they need to be constrained
*}
lemma "0x0A + 0x10 = (0x1A :: nat)" by simp

text "The number of leading zeros is irrelevant"
lemma "0b00010000 = 0x10" by (rule refl)

text "Unary minus works as for decimal numerals"
lemma "- 0x0A = - 10" by (rule refl)

text {*
  Hex and bin numerals are printed as decimal: @{term "0b10"}
*}
term "0b10"
term "0x0A"

text {*
  The numerals 0 and 1 are syntactically different from the
  constants 0 and 1. For the usual numeric types, their values
  are the same, though.
*}
lemma "0x01 = 1" oops
lemma "0x00 = 0" oops

lemma "0x01 = (1::nat)" by simp
lemma "0b0000 = (0::int)" by simp

end