theory Classes
  imports Main HOL.Real
begin

subsection \<open>definition of classes\<close>
class additive =
  fixes add :: "'a \<Rightarrow> 'a \<Rightarrow> 'a" (infixl "\<oplus>" 70)
  assumes assoc : "(x \<oplus> y) \<oplus> z = x \<oplus> (y \<oplus> z)"

print_locale additive

instantiation int :: additive
begin

definition add_int_def : "x \<oplus> y = (x::int) + y"

instance proof
  fix x y z :: int
  show "x \<oplus> y \<oplus> z = x \<oplus> (y \<oplus> z)" unfolding add_int_def by simp
qed

end

value "(2::int) \<oplus> 12"

instantiation bool :: additive
begin
definition add_bool_def : "x \<oplus> y = ((x::bool) \<or> y)"

instance proof
  fix x y z :: bool
  show "x \<oplus> y \<oplus> z = x \<oplus> (y \<oplus> z)" unfolding add_bool_def by simp
qed

end

value "True \<oplus> False"

datatype Number = Natural nat | Integer int | Real real

instantiation Number :: additive
begin

definition addnum :: "Number \<Rightarrow> Number \<Rightarrow> Number"
  where "addnum x y \<equiv> Real ((case x of Real r \<Rightarrow> r | Natural n \<Rightarrow> of_nat n | Integer i \<Rightarrow> of_int i)
                           + (case y of Real r \<Rightarrow> r | Natural n \<Rightarrow> of_nat n | Integer i \<Rightarrow> of_int i))"

definition add_Number_def : "x \<oplus> y = addnum x y"

instance proof
  fix x y z :: Number
  show "x \<oplus> y \<oplus> z = x \<oplus> (y \<oplus> z)"
    unfolding add_Number_def addnum_def
    by auto
qed

end

value "(Real 2.0) \<oplus> (Integer 3)"

interpretation list_add : additive append
proof
  fix x y z :: "'a list"
  show "(x @ y) @ z = x @ y @ z" by auto
qed

subsection \<open>parametric polymorphism and ad-hoc polymorphism (overloading)\<close>

subsection \<open>semigroup, monoid\<close>
class semigroup =
  fixes mult :: "'a \<Rightarrow> 'a \<Rightarrow> 'a" (infixl "\<otimes>" 70)
  assumes assoc : "(x \<otimes> y) \<otimes> z = x \<otimes> (y \<otimes> z)"

print_locale semigroup

instantiation int :: semigroup
begin
definition multi_int_def : "i \<otimes> j = i + (j::int)"

instance proof
  fix x y z :: int have "(x + y) + z = x + (y + z)" by simp
  then show "x \<otimes> y \<otimes> z = x \<otimes> (y \<otimes> z)" unfolding multi_int_def by simp
qed
end

thm multi_int_def

instantiation nat :: semigroup
begin
definition multi_nat_def : "i \<otimes> j = i + (j::nat)"

instance proof
  fix x y z :: nat have "(x + y) + z = x + (y + z)" by simp
  then show "x \<otimes> y \<otimes> z = x \<otimes> (y \<otimes> z)" unfolding multi_nat_def by simp
qed
end

instantiation prod :: (semigroup, semigroup) semigroup
begin
definition
mult_prod_def : "p1 \<otimes> p2 = (fst p1 \<otimes> fst p2, snd p1 \<otimes> snd p2)"

instance proof
  fix p1 p2 p3 :: "('a :: semigroup) \<times> ('b :: semigroup)"
  show "(p1 \<otimes> p2) \<otimes> p3 = p1 \<otimes> (p2 \<otimes> p3)"
  unfolding mult_prod_def by (simp add: assoc)
qed
end

class monoidl = semigroup +
  fixes neutral :: 'a ("\<one>")
  assumes neutl : "\<one> \<otimes> x = x"

print_locale semigroup
print_locale monoidl
thm monoidl_axioms
thm semigroup_axioms

subclass (in monoidl) semigroup
  using semigroup_axioms by simp

instantiation nat and int :: monoidl
begin

definition neutral_nat_def : "\<one> = (0::nat)"

definition neutral_int_def : "\<one> = (0::int)"

instance proof
  fix n::nat
  show "\<one> \<otimes> n = n" unfolding neutral_nat_def multi_nat_def by simp
next
  fix n::int
  show "\<one> \<otimes> n = n" unfolding neutral_int_def multi_int_def by simp
qed
end

instantiation prod :: (monoidl,monoidl) monoidl
begin

definition neutral_prod_def: "\<one> = (\<one>,\<one>)"

instance proof
  fix p :: "'a :: monoidl \<times> 'b :: monoidl"
  show "\<one> \<otimes> p = p"
    unfolding neutral_prod_def mult_prod_def by (simp add:neutl)
qed
end

class monoid = monoidl +
  assumes neutr: "x \<otimes> \<one> = x"

instantiation int and nat :: monoid
begin

instance proof
  fix x :: int
  show "x \<otimes> \<one> = x"
    by (simp add: multi_int_def neutral_int_def)
next
  fix x :: nat
  show "x \<otimes> \<one> = x"
    by (simp add: multi_nat_def neutral_nat_def)
qed
end

instantiation prod :: (monoid, monoid) monoid
begin
instance proof
  fix p :: "('a :: monoid) \<times> ('b :: monoid)"
  show "p \<otimes> \<one> = p"
    by (simp add: mult_prod_def neutr neutral_prod_def)
qed

end

class group = monoidl +
  fixes inverse :: "'a \<Rightarrow> 'a" ("\<ominus>_" [1000] 900)
  assumes invl : "\<ominus>x \<otimes> x = \<one>"

lemma (in group) left_cancel: "x \<otimes> y = x \<otimes> z \<longleftrightarrow> y = z"
proof
assume "x \<otimes> y = x \<otimes> z"
then have "\<ominus>x \<otimes> (x \<otimes> y) = \<ominus>x \<otimes> (x \<otimes> z)" by simp
then have "(\<ominus>x \<otimes> x) \<otimes> y = (\<ominus>x \<otimes> x) \<otimes> z" using assoc by simp
then show "y = z" using neutl and invl by simp
next
assume "y = z"
then show "x \<otimes> y = x \<otimes> z" by simp
qed

subclass (in group) monoidl
  using monoidl_axioms by auto

subclass (in group) monoid
proof
  fix x
  from invl have "\<ominus>x \<otimes> x = \<one>" by simp
  with assoc [symmetric] neutl invl have "\<ominus>x \<otimes> (x \<otimes> \<one>) = \<ominus>x \<otimes> x" by simp
  with left_cancel show "x \<otimes> \<one> = x" by simp
qed

instantiation int :: group
begin
definition inverse_int_def : "\<ominus>x = - (x::int)"

instance proof
  fix x :: int
  show "\<ominus>x \<otimes> x = \<one>"
    by (simp add: inverse_int_def multi_int_def neutral_int_def)
qed
end

lemma "(x::int) \<otimes> y \<otimes> z = (x \<otimes> y) \<otimes> z"
  by simp

interpretation list_monoid: monoid append "[]"
proof
  fix x y z :: "'a list"
  show "(x @ y) @ z = x @ y @ z" by auto
next
  fix x :: "'a list"
  show "[] @ x = x" by auto
next
  fix x :: "'a list"
  show "x @ [] = x" by auto
qed

lemma "(xs @ ys) @ zs = xs @ (ys @ zs)"
  using list_monoid.assoc by auto

interpretation fun_monoid: monoid comp id
proof
  fix x y z :: "'a \<Rightarrow> 'a"
  show "x \<circ> y \<circ> z = x \<circ> (y \<circ> z)" by auto
next
  fix x :: "'a \<Rightarrow> 'a"
  show "id \<circ> x = x" by auto
next
  fix x :: "'a \<Rightarrow> 'a"
  show "x \<circ> id = x" by auto
qed

lemma "(f \<circ> g) \<circ> h = f \<circ> (g \<circ> h)"
  using fun_monoid.assoc by auto

class eqclass =
  fixes eq :: "'a \<Rightarrow> 'a \<Rightarrow> bool" (infixl "\<asymp>" 70)
  assumes refl : "a \<asymp> a"
    and   sym : "a \<asymp> b \<longleftrightarrow> b \<asymp> a"
    and   trans : "a \<asymp> b \<and> b \<asymp> c \<longrightarrow> a \<asymp> c"

instantiation nat :: eqclass
begin

definition eq_nat_def : "x \<asymp> y = (x = (y :: nat))"

instance proof
  fix a :: nat
  show "a \<asymp> a" using eq_nat_def by simp
next
  fix a b :: nat
  show "a \<asymp> b = b \<asymp> a" using eq_nat_def by auto
next
  fix a b c :: nat
  show "a \<asymp> b \<and> b \<asymp> c \<longrightarrow> a \<asymp> c" using eq_nat_def by auto
qed

end

instantiation int :: eqclass
begin

definition eq_int_def : "x \<asymp> y = (x = (y :: int))"

instance proof
  fix a :: int
  show "a \<asymp> a" using eq_int_def by simp
next
  fix a b :: int
  show "a \<asymp> b = b \<asymp> a" using eq_int_def by auto
next
  fix a b c :: int
  show "a \<asymp> b \<and> b \<asymp> c \<longrightarrow> a \<asymp> c" using eq_int_def by auto
qed

end

instantiation prod :: (eqclass, eqclass) eqclass
begin

definition eq_prod_def : "x \<asymp> y = (fst x = fst y \<and> snd x = snd y)"

instance proof
  fix p :: "'a :: eqclass \<times> 'b :: eqclass"
  show "p \<asymp> p" using eq_prod_def by simp
next
  fix a b :: "'a :: eqclass \<times> 'b :: eqclass"
  show "a \<asymp> b = b \<asymp> a" using eq_prod_def by auto
next
  fix a b c :: "'a :: eqclass \<times> 'b :: eqclass"
  show "a \<asymp> b \<and> b \<asymp> c \<longrightarrow> a \<asymp> c" using eq_prod_def by auto
qed

end

lemma "((1::nat) + 2) \<asymp> 3"
  by (simp add: eq_nat_def)

lemma "((1::int) + 2) \<asymp> 3"
  by (simp add: eq_int_def)

lemma "(a::int) \<asymp> c \<and> (b::nat) \<asymp> d \<Longrightarrow> (a,b) \<asymp> (c,d)"
  unfolding eq_prod_def eq_int_def eq_nat_def by simp

instantiation list :: (type) eqclass
begin

definition eq_list_def : "xs \<asymp> ys = ((xs::'a list) = ys)"

instance proof
  fix a :: "'a list"
  show "a \<asymp> a" by (simp add: eq_list_def)
next
  fix a b :: "'a list"
  show "a \<asymp> b = b \<asymp> a" using eq_list_def by auto
next
  fix a b c :: "'a list"
  show "a \<asymp> b \<and> b \<asymp> c \<longrightarrow> a \<asymp> c" using eq_list_def by auto
qed

end

end


(* Property from Productive Use of Failure in Inductive Proof,
   Andrew Ireland and Alan Bundy, JAR 1996.
   This Isabelle theory is produced using the TIP tool offered at the following website:
     https://github.com/tip-org/tools
   This file was originally provided as part of TIP benchmark at the following website:
     https://github.com/tip-org/benchmarks
   Yutaka Nagashima at CIIRC, CTU changed the TIP output theory file slightly
   to make it compatible with Isabelle2017.
   Some proofs were added by Yutaka Nagashima.*)
  theory TIP_prop_16
  imports "../../Test_Base"
begin

datatype Nat = Z | S "Nat"

fun even :: "Nat => bool" where
"even (Z) = True"
| "even (S (Z)) = False"
| "even (S (S z)) = even z"

fun t2 :: "Nat => Nat => Nat" where
"t2 (Z) y = y"
| "t2 (S z) y = S (t2 z y)"

lemma t2_succ: "t2 n (S m) = t2 (S n) m"
  by(induct n, auto)

theorem property0 :
  "even (t2 x x)"
  apply(induct x rule: even.induct , auto)
  apply(simp add:t2_succ)
  done

end


section \<open>Algebra-only Theorems\<close>

text \<open>This section verifies the linear algebraic counter-parts of the graph-theoretic theorems
about Random walks. The graph-theoretic results are then derived in Section~\ref{sec:random_walks}.\<close>

theory Expander_Graphs_Algebra
  imports
    "HOL-Library.Monad_Syntax"
    Expander_Graphs_TTS
begin

lemma pythagoras:
  fixes v w :: "'a::real_inner"
  assumes "v \<bullet> w  = 0"
  shows "norm (v+w)^2 = norm v^2 + norm w^2"
  using assms by (simp add:power2_norm_eq_inner algebra_simps inner_commute)

definition diag :: "('a :: zero)^'n \<Rightarrow> 'a^'n^'n"
  where "diag v = (\<chi> i j. if i = j then (v $ i) else 0)"

definition ind_vec :: "'n set \<Rightarrow> real^'n"
  where "ind_vec S = (\<chi> i. of_bool( i \<in> S))"

lemma diag_mult_eq: "diag x ** diag y = diag (x * y)"
  unfolding diag_def
  by (vector matrix_matrix_mult_def)
   (auto simp add:if_distrib if_distribR sum.If_cases)

lemma diag_vec_mult_eq: "diag x *v y = x * y"
  unfolding diag_def matrix_vector_mult_def
  by (simp add:if_distrib if_distribR sum.If_cases times_vec_def)

definition matrix_norm_bound :: "real^'n^'m \<Rightarrow> real \<Rightarrow> bool"
  where "matrix_norm_bound A l = (\<forall>x. norm (A *v x) \<le> l * norm x)"

lemma  matrix_norm_boundI:
  assumes "\<And>x. norm (A *v x) \<le> l * norm x"
  shows "matrix_norm_bound A l"
  using assms unfolding matrix_norm_bound_def by simp

lemma matrix_norm_boundD:
  assumes "matrix_norm_bound A l"
  shows "norm (A *v x) \<le> l * norm x"
  using assms unfolding matrix_norm_bound_def by simp

lemma matrix_norm_bound_nonneg:
  fixes A :: "real^'n^'m"
  assumes "matrix_norm_bound A l"
  shows "l \<ge> 0"
proof -
  have "0 \<le> norm (A *v 1)" by simp
  also have "... \<le> l * norm (1::real^'n)"
    using assms(1) unfolding matrix_norm_bound_def by simp
  finally have "0 \<le> l  * norm (1::real^'n)"
    by simp
  moreover have "norm (1::real^'n) > 0"
    by simp
  ultimately show ?thesis
    by (simp add: zero_le_mult_iff)
qed

lemma  matrix_norm_bound_0:
  assumes "matrix_norm_bound A 0"
  shows "A = (0::real^'n^'m)"
proof (intro iffD2[OF matrix_eq] allI)
  fix x :: "real^'n"
  have "norm (A *v x) = 0"
    using assms unfolding matrix_norm_bound_def by simp
  thus "A *v x = 0 *v x"
    by simp
qed

lemma matrix_norm_bound_diag:
  fixes x :: "real^'n"
  assumes "\<And>i. \<bar>x $ i\<bar> \<le> l"
  shows "matrix_norm_bound (diag x) l"
proof (rule matrix_norm_boundI)
  fix y :: "real^'n"

  have l_ge_0: "l \<ge> 0" using assms by fastforce

  have a: "\<bar>x $ i * v\<bar> \<le> \<bar>l * v\<bar>" for v i
    using l_ge_0 assms by (simp add:abs_mult mult_right_mono)

  have "norm (diag x *v y) = sqrt (\<Sum>i \<in> UNIV. (x $ i * y $ i)^2)"
    unfolding matrix_vector_mult_def diag_def norm_vec_def L2_set_def
    by (auto simp add:if_distrib if_distribR sum.If_cases)
  also have "... \<le> sqrt (\<Sum>i \<in> UNIV. (l * y $ i)^2)"
    by (intro real_sqrt_le_mono sum_mono iffD1[OF abs_le_square_iff] a)
  also have "... = l * norm y"
    using l_ge_0 by (simp add:norm_vec_def L2_set_def algebra_simps
        sum_distrib_left[symmetric] real_sqrt_mult)
  finally show "norm (diag x *v y) \<le> l * norm y" by simp
qed

lemma vector_scaleR_matrix_ac_2: "b *\<^sub>R (A::real^'n^'m) *v x = b *\<^sub>R (A *v x)"
  unfolding vector_transpose_matrix[symmetric]  transpose_scalar
  by (intro vector_scaleR_matrix_ac)

lemma  matrix_norm_bound_scale:
  assumes "matrix_norm_bound A l"
  shows "matrix_norm_bound (b *\<^sub>R A) (\<bar>b\<bar> * l)"
proof (intro matrix_norm_boundI)
  fix x
  have "norm (b *\<^sub>R A *v x) = norm (b *\<^sub>R (A *v x))"
    by (metis transpose_scalar vector_scaleR_matrix_ac vector_transpose_matrix)
  also have "... = \<bar>b\<bar> * norm (A *v x)"
    by simp
  also have "... \<le> \<bar>b\<bar> * (l * norm x)"
    using assms matrix_norm_bound_def by (intro mult_left_mono) auto
  also have "... \<le> (\<bar>b\<bar> * l) * norm x" by simp
  finally show "norm (b *\<^sub>R A *v x) \<le> (\<bar>b\<bar> * l) * norm x" by simp
qed

definition nonneg_mat :: "real^'n^'m \<Rightarrow> bool"
  where "nonneg_mat A = (\<forall>i j. A $ i $ j \<ge> 0)"

lemma nonneg_mat_1:
  shows "nonneg_mat (mat 1)"
  unfolding nonneg_mat_def mat_def by auto

lemma nonneg_mat_prod:
  assumes "nonneg_mat A" "nonneg_mat B"
  shows "nonneg_mat (A ** B)"
  using assms unfolding nonneg_mat_def matrix_matrix_mult_def
  by (auto intro:sum_nonneg)

lemma nonneg_mat_transpose:
  "nonneg_mat (transpose A) = nonneg_mat A"
  unfolding nonneg_mat_def transpose_def
  by auto

definition spec_bound :: "real^'n^'n \<Rightarrow> real \<Rightarrow> bool"
  where "spec_bound M l = (l \<ge> 0 \<and> (\<forall>v. v \<bullet> 1 = 0 \<longrightarrow> norm (M *v v) \<le> l * norm v))"

lemma spec_boundD1:
  assumes "spec_bound M l"
  shows "0 \<le> l"
  using assms unfolding spec_bound_def by simp

lemma spec_boundD2:
  assumes "spec_bound M l"
  assumes "v \<bullet> 1 = 0 "
  shows "norm (M *v v) \<le> l * norm v"
  using assms unfolding spec_bound_def by simp

lemma spec_bound_mono:
  assumes "spec_bound M \<alpha>" "\<alpha> \<le> \<beta>"
  shows "spec_bound M \<beta>"
proof -
  have "norm (M *v v) \<le> \<beta> * norm v" if "inner v 1 = 0"  for v
  proof -
    have "norm (M *v v) \<le> \<alpha> * norm v"
      by (intro spec_boundD2[OF assms(1)] that)
    also have "... \<le> \<beta> * norm v"
      by (intro mult_right_mono assms(2)) auto
    finally show ?thesis by simp
  qed
  moreover have "\<beta> \<ge> 0"
    using assms(2) spec_boundD1[OF assms(1)] by simp
  ultimately show ?thesis
    unfolding spec_bound_def by simp
qed

definition markov :: "real^'n^'n \<Rightarrow> bool"
  where "markov M = (nonneg_mat M \<and> M *v 1  = 1 \<and> 1 v* M = 1)"

lemma markov_symI:
  assumes "nonneg_mat A" "transpose A = A" "A *v 1 = 1"
  shows "markov A"
proof -
  have "1 v* A = transpose A *v 1"
    unfolding vector_transpose_matrix[symmetric] by simp
  also have "... = 1" unfolding assms(2,3) by simp
  finally have "1 v* A = 1" by simp
  thus ?thesis
    unfolding markov_def using assms by auto
qed

lemma markov_apply:
  assumes "markov M"
  shows "M *v 1 = 1" "1 v* M = 1"
  using assms unfolding markov_def by auto

lemma markov_transpose:
  "markov A = markov (transpose A)"
  unfolding markov_def nonneg_mat_transpose by auto
fun matrix_pow where
  "matrix_pow M 0 = mat 1" |
  "matrix_pow M (Suc n) = M ** (matrix_pow M n)"

lemma markov_orth_inv:
  assumes "markov A"
  shows "inner (A *v x) 1 = inner x 1"
proof -
  have "inner (A *v x) 1 = inner x (1 v* A)"
    using dot_lmul_matrix inner_commute by metis
  also have "... = inner x 1"
    using markov_apply[OF assms(1)] by simp
  finally show ?thesis by simp
qed

lemma markov_id:
  "markov (mat 1)"
  unfolding markov_def using nonneg_mat_1 by simp

lemma markov_mult:
  assumes "markov A" "markov B"
  shows "markov (A ** B)"
proof -
  have "nonneg_mat (A ** B)"
    using assms unfolding markov_def by (intro nonneg_mat_prod) auto
  moreover have "(A ** B) *v 1 = 1"
    using assms unfolding markov_def
    unfolding matrix_vector_mul_assoc[symmetric] by simp
  moreover have "1 v* (A ** B) = 1"
    using assms unfolding markov_def
    unfolding vector_matrix_mul_assoc[symmetric] by simp
  ultimately show ?thesis
    unfolding markov_def by simp
qed

lemma markov_matrix_pow:
  assumes "markov A"
  shows "markov (matrix_pow A k)"
  using markov_id assms markov_mult
  by (induction k, auto)

lemma spec_bound_prod:
  assumes "markov A" "markov B"
  assumes "spec_bound A la" "spec_bound B lb"
  shows "spec_bound (A ** B) (la*lb)"
proof -
  have la_ge_0: "la \<ge> 0" using spec_boundD1[OF assms(3)] by simp

  have "norm ((A ** B) *v x) \<le> (la * lb) * norm x" if "inner x 1 = 0" for x
  proof -
    have "norm ((A ** B) *v x) = norm (A *v (B *v x))"
      by (simp add:matrix_vector_mul_assoc)
    also have "... \<le> la * norm (B *v x)"
      by (intro spec_boundD2[OF assms(3)]) (simp add:markov_orth_inv that assms(2))
    also have "... \<le> la * (lb * norm x)"
      by (intro spec_boundD2[OF assms(4)] mult_left_mono that la_ge_0)
    finally show ?thesis by simp
  qed
  moreover have "la * lb \<ge> 0"
    using la_ge_0 spec_boundD1[OF assms(4)] by simp
  ultimately show ?thesis
    using spec_bound_def by auto
qed

lemma spec_bound_pow:
  assumes "markov A"
  assumes "spec_bound A l"
  shows "spec_bound (matrix_pow A k) (l^k)"
proof (induction k)
  case 0
  then show ?case unfolding spec_bound_def by simp
next
  case (Suc k)
  have "spec_bound (A ** matrix_pow A k) (l * l ^ k)"
    by (intro spec_bound_prod assms Suc markov_matrix_pow)
  thus ?case by simp
qed

fun intersperse :: "'a \<Rightarrow> 'a list \<Rightarrow> 'a list"
  where
    "intersperse x [] = []" |
    "intersperse x (y#[]) = y#[]" |
    "intersperse x (y#z#zs) = y#x#intersperse x (z#zs)"

lemma intersperse_snoc:
  assumes "xs \<noteq> []"
  shows "intersperse z (xs@[y]) = intersperse z xs@[z,y]"
  using assms
proof (induction xs rule:list_nonempty_induct)
  case (single x)
  then show ?case by simp
next
  case (cons x xs)
  then obtain xsh xst where t:"xs = xsh#xst"
    by (metis neq_Nil_conv)
  have "intersperse z ((x # xs) @ [y]) = x#z#intersperse z (xs@[y])"
    unfolding t by simp
  also have "... = x#z#intersperse z xs@[z,y]"
    using cons by simp
  also have "... = intersperse z (x#xs)@[z,y]"
    unfolding t by simp
  finally show ?case by simp
qed

lemma foldl_intersperse:
  assumes "xs \<noteq> []"
  shows "foldl f a ((intersperse x xs)@[x]) = foldl (\<lambda>y z. f (f y z) x) a xs"
  using assms by (induction xs rule:rev_nonempty_induct) (auto simp add:intersperse_snoc)

lemma foldl_intersperse_2:
  shows "foldl f a (intersperse y (x#xs)) = foldl (\<lambda>x z. f (f x y) z) (f a x) xs"
proof (induction xs rule:rev_induct)
  case Nil
  then show ?case by simp
next
  case (snoc xst xs)
  have "foldl f a (intersperse y ((x # xs) @ [xst])) = foldl (\<lambda>x. f (f x y)) (f a x) (xs @ [xst])"
    by (subst intersperse_snoc, auto simp add:snoc)
  then show ?case  by simp
qed

context regular_graph_tts
begin

definition stat :: "real^'n"
  where "stat = (1 / real CARD('n)) *\<^sub>R 1"

definition J :: "('c :: field)^'n^'n"
  where "J = (\<chi> i j. of_nat 1 / of_nat CARD('n))"

lemma inner_1_1: "1 \<bullet> (1::real^'n) = CARD('n)"
  unfolding inner_vec_def by simp

definition proj_unit :: "real^'n \<Rightarrow> real^'n"
  where "proj_unit v = (1 \<bullet> v) *\<^sub>R stat"

definition proj_rem :: "real^'n \<Rightarrow> real^'n"
  where "proj_rem v = v - proj_unit v"

lemma proj_rem_orth: "1 \<bullet> (proj_rem v) = 0"
  unfolding proj_rem_def proj_unit_def inner_diff_right stat_def
  by (simp add:inner_1_1)

lemma split_vec: "v = proj_unit v + proj_rem v"
  unfolding proj_rem_def by simp

lemma apply_J: "J *v x = proj_unit x"
proof (intro iffD2[OF vec_eq_iff] allI)
  fix i
  have "(J *v x) $ i = inner (\<chi> j. 1 / real CARD('n)) x"
    unfolding matrix_vector_mul_component J_def by simp
  also have "... = inner stat x"
    unfolding stat_def scaleR_vec_def by auto
  also have "... = (proj_unit x) $ i"
    unfolding proj_unit_def stat_def by simp
  finally show "(J *v x) $ i = (proj_unit x) $ i" by simp
qed

lemma spec_bound_J: "spec_bound (J :: real^'n^'n) 0"
proof -
  have "norm (J *v v) = 0" if "inner v 1 = 0" for v :: "real^'n"
  proof -
    have "inner (proj_unit v + proj_rem v) 1 = 0"
      using that by (subst (asm) split_vec[of "v"], simp)
    hence "inner (proj_unit v) 1 = 0"
      using proj_rem_orth inner_commute unfolding inner_add_left
      by (metis add_cancel_left_right)
    hence "proj_unit v = 0"
      unfolding proj_unit_def stat_def by simp
    hence "J *v v = 0"
      unfolding apply_J by simp
    thus ?thesis by simp
  qed
  thus ?thesis
    unfolding spec_bound_def by simp
qed

lemma matrix_decomposition_lemma_aux:
  fixes A :: "real^'n^'n"
  assumes "markov A"
  shows "spec_bound A l \<longleftrightarrow> matrix_norm_bound (A - (1-l) *\<^sub>R J) l" (is "?L \<longleftrightarrow> ?R")
proof
  assume a:"?L"
  hence l_ge_0: "l \<ge> 0" using spec_boundD1 by auto
  show "?R"
  proof (rule matrix_norm_boundI)
    fix x :: "real^'n"
    have "(A - (1-l) *\<^sub>R J) *v x = A *v x - (1-l) *\<^sub>R (proj_unit x)"
      by (simp add:algebra_simps vector_scaleR_matrix_ac_2 apply_J)
    also have "... = A *v proj_unit x + A *v proj_rem x - (1-l) *\<^sub>R (proj_unit x)"
      by (subst split_vec[of "x"], simp add:algebra_simps)
    also have "... = proj_unit x + A *v proj_rem x - (1-l) *\<^sub>R (proj_unit x)"
      using markov_apply[OF assms(1)]
      unfolding proj_unit_def stat_def by (simp add:algebra_simps)
    also have "... = A *v proj_rem x + l *\<^sub>R proj_unit x" (is "_ = ?R1")
      by (simp add:algebra_simps)
    finally have d:"(A - (1-l) *\<^sub>R J) *v x = ?R1" by simp

    have "inner (l *\<^sub>R proj_unit x) (A *v proj_rem x) =
      inner ((l * inner 1 x / real CARD('n)) *\<^sub>R 1 v* A) (proj_rem x)"
      by (subst dot_lmul_matrix[symmetric]) (simp add:proj_unit_def stat_def)
    also have "... = (l * inner 1 x / real CARD('n)) * inner 1 (proj_rem x)"
      unfolding scaleR_vector_matrix_assoc markov_apply[OF assms] by simp
    also have "... = 0"
      unfolding proj_rem_orth by simp
    finally have b:"inner (l *\<^sub>R proj_unit x) (A *v proj_rem x) = 0" by simp

    have c: "inner (proj_rem x) (proj_unit x) = 0"
      using proj_rem_orth[of "x"]
      unfolding proj_unit_def stat_def by (simp add:inner_commute)

    have "norm (?R1)^2 = norm (A *v proj_rem x)^2 + norm (l *\<^sub>R proj_unit x)^2"
      using b by (intro pythagoras) (simp add:inner_commute)
    also have "... \<le> (l * norm (proj_rem x))^2 + norm (l *\<^sub>R proj_unit x)^2"
      using proj_rem_orth[of "x"]
      by (intro add_mono power_mono spec_boundD2 a) (auto simp add:inner_commute)
    also have "... = l^2 * (norm (proj_rem x)^2 + norm (proj_unit x)^2)"
      by (simp add:algebra_simps)
    also have "... = l^2 * (norm (proj_rem x + proj_unit x)^2)"
      using c by (subst pythagoras) auto
    also have "... = l^2 * norm x^2"
      by (subst (3) split_vec[of "x"]) (simp add:algebra_simps)
    also have "... = (l * norm x)^2"
      by (simp add:algebra_simps)
    finally have "norm (?R1)^2 \<le> (l * norm x)^2" by simp
    hence "norm (?R1) \<le> l * norm x"
      using l_ge_0 by (subst (asm) power_mono_iff) auto

    thus "norm ((A - (1-l) *\<^sub>R J) *v x) \<le> l * norm x"
      unfolding d by simp
  qed
next
  assume a:"?R"
  have "norm (A *v x) \<le> l * norm x" if "inner x 1 = 0" for x
  proof -
    have "(1 - l) *\<^sub>R J *v x = (1 - l) *\<^sub>R (proj_unit x)"
      by (simp add:vector_scaleR_matrix_ac_2 apply_J)
    also have "... = 0"
      unfolding proj_unit_def using that by (simp add:inner_commute)
    finally have b: "(1 - l) *\<^sub>R J *v x = 0" by simp

    have "norm (A *v x) = norm ((A - (1-l) *\<^sub>R J) *v x  + ((1-l) *\<^sub>R J) *v x)"
      by (simp add:algebra_simps)
    also have "... \<le> norm ((A - (1-l) *\<^sub>R J) *v x) + norm (((1-l) *\<^sub>R J) *v x)"
      by (intro norm_triangle_ineq)
    also have "... \<le> l * norm x + 0"
      using a b unfolding  matrix_norm_bound_def by (intro add_mono, auto)
    also have "... = l * norm x"
      by simp
    finally show ?thesis by simp
  qed

  moreover have "l \<ge> 0"
    using a matrix_norm_bound_nonneg by blast

  ultimately show "?L"
    unfolding spec_bound_def by simp
qed

lemma matrix_decomposition_lemma:
  fixes A :: "real^'n^'n"
  assumes "markov A"
  shows "spec_bound A l \<longleftrightarrow> (\<exists>E. A = (1-l) *\<^sub>R J + l *\<^sub>R E \<and> matrix_norm_bound E 1 \<and> l \<ge> 0)"
    (is "?L \<longleftrightarrow> ?R")
proof -
  have "?L \<longleftrightarrow> matrix_norm_bound (A - (1-l) *\<^sub>R J) l"
    using matrix_decomposition_lemma_aux[OF assms] by simp
  also have "... \<longleftrightarrow> ?R"
  proof
    assume a:"matrix_norm_bound (A - (1 - l) *\<^sub>R J) l"
    hence l_ge_0: "l \<ge> 0" using matrix_norm_bound_nonneg by auto
    define E where "E = (1/l) *\<^sub>R (A - (1-l) *\<^sub>R J)"
    have "A = J" if "l = 0"
    proof -
      have "matrix_norm_bound (A - J) 0"
        using a that by simp
      hence "A - J = 0" using matrix_norm_bound_0 by blast
      thus "A = J" by simp
    qed
    hence "A = (1-l) *\<^sub>R J + l *\<^sub>R E"
      unfolding E_def by simp
    moreover have "matrix_norm_bound E 1"
    proof (cases "l = 0")
      case True
      hence "E = 0" if "l = 0"
        unfolding E_def by simp
      thus "matrix_norm_bound E 1" if "l = 0"
        using that unfolding matrix_norm_bound_def by auto
    next
      case False
      hence "l > 0" using l_ge_0 by simp
      moreover have "matrix_norm_bound E (\<bar>1 / l\<bar>* l)"
        unfolding E_def
        by (intro matrix_norm_bound_scale a)
      ultimately show ?thesis by auto
    qed
    ultimately show ?R using l_ge_0 by auto
  next
    assume a:?R
    then obtain E where E_def: "A = (1 - l) *\<^sub>R J + l *\<^sub>R E"  "matrix_norm_bound E 1" "l \<ge> 0"
      by auto
    have "matrix_norm_bound (l *\<^sub>R E) (abs l*1)"
      by (intro matrix_norm_bound_scale E_def(2))
    moreover have "l \<ge> 0" using E_def by simp
    moreover have " l *\<^sub>R E = (A - (1 - l) *\<^sub>R J)"
      using E_def(1) by simp
    ultimately show "matrix_norm_bound (A - (1 - l) *\<^sub>R J) l"
      by simp
  qed
  finally show ?thesis by simp
qed

lemma hitting_property_alg:
  fixes S :: "('n :: finite) set"
  assumes l_range: "l \<in> {0..1}"
  defines "P \<equiv> diag (ind_vec S)"
  defines "\<mu> \<equiv> card S / CARD('n)"
  assumes "\<And>M. M \<in> set Ms \<Longrightarrow> spec_bound M l \<and> markov M"
  shows "foldl (\<lambda>x M. P *v (M *v x)) (P *v stat) Ms \<bullet> 1 \<le> (\<mu> + l * (1-\<mu>))^(length Ms+1)"
proof -
  define t :: "real^'n" where "t = (\<chi> i. of_bool (i \<in> S))"
  define r where "r = foldl (\<lambda>x M. P *v (M *v x)) (P *v stat) Ms"
  have P_proj: "P ** P = P"
    unfolding P_def diag_mult_eq ind_vec_def by (intro arg_cong[where f="diag"]) (vector)

  have P_1_left: "1 v* P = t"
    unfolding P_def diag_def ind_vec_def vector_matrix_mult_def t_def by simp

  have P_1_right: "P *v 1 = t"
    unfolding P_def diag_def ind_vec_def matrix_vector_mult_def t_def by simp

  have P_norm :"matrix_norm_bound P 1"
    unfolding P_def ind_vec_def by (intro matrix_norm_bound_diag) simp

  have norm_t: "norm t = sqrt (real (card S))"
    unfolding t_def norm_vec_def L2_set_def of_bool_def
    by (simp add:sum.If_cases if_distrib if_distribR)

  have \<mu>_range: "\<mu> \<ge> 0" "\<mu> \<le> 1"
    unfolding \<mu>_def by (auto simp add:card_mono)

  define condition :: "real^'n \<Rightarrow> nat \<Rightarrow> bool"
    where "condition = (\<lambda>x n. norm x \<le> (\<mu> + l * (1-\<mu>))^n * sqrt (card S)/CARD('n) \<and> P *v x = x)"

  have a:"condition r (length Ms)"
    unfolding r_def using assms(4)
  proof (induction Ms rule:rev_induct)
    case Nil
    have "norm (P *v stat) = (1 / real CARD('n)) * norm t"
      unfolding stat_def matrix_vector_mult_scaleR P_1_right by simp
    also have "... \<le>  (1 / real CARD('n)) * sqrt (real (card S))"
      using  norm_t by (intro mult_left_mono) auto
    also have "... = sqrt (card S)/CARD('n)" by simp
    finally have "norm (P *v stat) \<le> sqrt (card S)/CARD('n)" by simp
    moreover have "P *v (P *v stat) = P *v stat"
      unfolding matrix_vector_mul_assoc P_proj by simp
    ultimately show ?case unfolding condition_def by simp
  next
    case (snoc M xs)
    hence "spec_bound M l \<and> markov M"
        using snoc(2) by simp
    then obtain E where E_def: "M = (1-l) *\<^sub>R J + l *\<^sub>R E" "matrix_norm_bound E 1"
      using iffD1[OF matrix_decomposition_lemma] by auto

    define y where "y = foldl (\<lambda>x M. P *v (M *v x)) (P *v stat) xs"
    have b:"condition y (length xs)"
      using snoc unfolding y_def by simp
    hence a:"P *v y = y" using condition_def by simp

    have "norm (P *v (M *v y)) = norm (P *v ((1-l)*\<^sub>R J *v y) + P *v (l *\<^sub>R E *v y))"
      by (simp add:E_def algebra_simps)
    also have "... \<le> norm (P *v ((1-l)*\<^sub>R J *v y)) + norm (P *v (l *\<^sub>R E *v y)) "
      by (intro norm_triangle_ineq)
    also have "... = (1 - l) * norm (P *v (J *v y)) + l * norm (P *v (E *v y))"
      using l_range
      by (simp add:vector_scaleR_matrix_ac_2 matrix_vector_mult_scaleR)
    also have "... = (1-l) * \<bar>1 \<bullet> (P *v y)/real CARD('n)\<bar> * norm t + l * norm (P *v (E *v y))"
      by (subst a[symmetric])
        (simp add:apply_J proj_unit_def stat_def P_1_right matrix_vector_mult_scaleR)
    also have "... = (1-l) * \<bar>t \<bullet> y\<bar>/real CARD('n) * norm t + l * norm (P *v (E *v y))"
      by (subst dot_lmul_matrix[symmetric]) (simp add:P_1_left)
    also have "... \<le> (1-l) * (norm t * norm y) / real CARD('n) * norm t + l * (1 * norm (E *v y))"
      using P_norm Cauchy_Schwarz_ineq2 l_range
      by (intro add_mono mult_right_mono mult_left_mono divide_right_mono matrix_norm_boundD) auto
    also have "... = (1-l) * \<mu> * norm y + l * norm (E *v y)"
      unfolding \<mu>_def norm_t by simp
    also have "... \<le> (1-l) * \<mu> * norm y + l * (1 * norm y)"
      using \<mu>_range l_range
      by (intro add_mono matrix_norm_boundD mult_left_mono E_def) auto
    also have "... = (\<mu> + l * (1-\<mu>)) * norm y"
      by (simp add:algebra_simps)
    also have "... \<le> (\<mu> + l * (1-\<mu>)) * ((\<mu> + l * (1-\<mu>))^length xs * sqrt (card S)/CARD('n))"
      using b \<mu>_range l_range unfolding condition_def
      by (intro mult_left_mono) auto
    also have "... = (\<mu> + l * (1-\<mu>))^(length xs +1) * sqrt (card S)/CARD('n)"
      by simp
    finally have "norm (P *v (M *v y)) \<le> (\<mu> + l * (1-\<mu>))^(length xs +1) * sqrt (card S)/CARD('n)"
      by simp

    moreover have "P *v (P *v (M *v y)) = P *v (M *v y)"
      unfolding matrix_vector_mul_assoc matrix_mul_assoc P_proj
      by simp

    ultimately have "condition (P *v (M *v y)) (length (xs@[M]))"
      unfolding condition_def by simp

    then show ?case
      unfolding y_def by simp
  qed

  have "inner r 1 = inner (P *v r) 1"
    using a condition_def by simp
  also have "... = inner (1 v* P) r"
    unfolding dot_lmul_matrix by (simp add:inner_commute)
  also have "... = inner t r"
    unfolding P_1_left by simp
  also have "... \<le> norm t * norm r"
    by (intro norm_cauchy_schwarz)
  also have "... \<le> sqrt (card S) * ((\<mu> + l * (1-\<mu>))^(length Ms) * sqrt(card S)/CARD('n))"
    using a unfolding condition_def norm_t
    by (intro mult_mono) auto
  also have "... = (\<mu> + 0) * ((\<mu> + l * (1-\<mu>))^(length Ms))"
    by (simp add:\<mu>_def)
  also have "... \<le> (\<mu> + l * (1-\<mu>)) * (\<mu> + l * (1-\<mu>))^(length Ms)"
    using \<mu>_range l_range
    by (intro mult_right_mono zero_le_power add_mono) auto
  also have "... = (\<mu> + l * (1-\<mu>))^(length Ms+1)" by simp
  finally show ?thesis
    unfolding r_def by simp
qed

lemma upto_append:
  assumes "i \<le> j" "j \<le> k"
  shows  "[i..<j]@[j..<k] = [i..<k]"
  using assms by (metis less_eqE upt_add_eq_append)

definition bool_list_split :: "bool list \<Rightarrow> (nat list \<times> nat)"
  where "bool_list_split xs = foldl (\<lambda>(ys,z) x. (if x then (ys@[z],0) else (ys,z+1))) ([],0) xs"

lemma bool_list_split:
  assumes "bool_list_split xs = (ys,z)"
  shows "xs = concat (map (\<lambda>k. replicate k False@[True]) ys)@replicate z False"
  using assms
proof (induction xs arbitrary: ys z rule:rev_induct)
  case Nil
  then show ?case unfolding bool_list_split_def by simp
next
  case (snoc x xs)
  obtain u v where uv_def: "bool_list_split xs = (u,v)"
    by (metis surj_pair)

  show ?case
  proof (cases x)
    case True
    have a:"ys = u@[v]" "z = 0"
      using snoc(2) True uv_def unfolding bool_list_split_def by auto
    have "xs@[x] = concat (map (\<lambda>k. replicate k False@[True]) u)@replicate v False@[True]"
      using snoc(1)[OF uv_def] True by simp
    also have "... = concat (map (\<lambda>k. replicate k False@[True]) (u@[v]))@replicate 0 False"
      by simp
    also have "... = concat (map (\<lambda>k. replicate k False@[True]) (ys))@replicate z False"
      using a by simp
    finally show ?thesis by simp
  next
    case False
    have a:"ys = u" "z = v+1"
      using snoc(2) False uv_def unfolding bool_list_split_def by auto
    have "xs@[x] = concat (map (\<lambda>k. replicate k False@[True]) u)@replicate (v+1) False"
      using snoc(1)[OF uv_def] False unfolding replicate_add by simp
    also have "... = concat (map (\<lambda>k. replicate k False@[True]) (ys))@replicate z False"
      using a by simp
    finally show ?thesis by simp
  qed
qed

lemma bool_list_split_count:
  assumes "bool_list_split xs = (ys,z)"
  shows "length (filter id xs) = length ys"
  unfolding bool_list_split[OF assms(1)] by (simp add:filter_concat comp_def)

lemma foldl_concat:
  "foldl f a (concat xss) = foldl (\<lambda>y xs. foldl f y xs) a xss"
  by (induction xss rule:rev_induct, auto)

lemma hitting_property_alg_2:
  fixes S :: "('n :: finite) set" and l :: nat
  fixes M :: "real^'n^'n"
  assumes \<alpha>_range: "\<alpha> \<in> {0..1}"
  assumes "I \<subseteq> {..<l}"
  defines "P i \<equiv> (if i \<in> I then diag (ind_vec S) else mat 1)"
  defines "\<mu> \<equiv> real (card S) / real (CARD('n))"
  assumes "spec_bound M \<alpha>" "markov M"
  shows
    "foldl (\<lambda>x M. M *v x) stat (intersperse M (map P [0..<l])) \<bullet> 1 \<le> (\<mu>+\<alpha>*(1-\<mu>))^card I"
    (is "?L \<le> ?R")
proof (cases "I \<noteq> {}")
  case True
  define xs where "xs = map (\<lambda>i. i \<in> I) [0..<l]"
  define Q where "Q = diag (ind_vec S)"
  define P' where "P' = (\<lambda>x. if x then Q else mat 1)"

  let ?rep = "(\<lambda>x. replicate x (mat 1))"

  have P_eq: "P i = P' (i \<in> I)" for i
    unfolding P_def P'_def Q_def by simp

  have "l > 0"
    using True assms(2) by auto
  hence xs_ne: "xs \<noteq> []"
    unfolding xs_def by simp

  obtain ys z where ys_z: "bool_list_split xs = (ys,z)"
    by (metis surj_pair)

  have "length ys = length (filter id xs)"
    using bool_list_split_count[OF ys_z] by simp
  also have "... = card (I \<inter> {0..<l})"
    unfolding xs_def filter_map by (simp add:comp_def distinct_length_filter)
  also have "... = card I"
    using Int_absorb2[OF assms(2)] unfolding atLeast0LessThan by simp
  finally have  len_ys: "length ys = card I" by simp

  hence "length ys > 0"
    using True assms(2) by (metis card_gt_0_iff finite_nat_iff_bounded)
  then obtain yh yt where ys_split: "ys = yh#yt"
    by (metis length_greater_0_conv neq_Nil_conv)

  have a:"foldl (\<lambda>x N. M *v (N *v x)) x (?rep z) \<bullet> 1 = x \<bullet> 1" for x
  proof (induction z)
    case 0
    then show ?case by simp
  next
    case (Suc z)
    have "foldl (\<lambda>x N. M *v (N *v x)) x (?rep (z+1)) \<bullet> 1 = x \<bullet> 1"
      unfolding replicate_add using Suc
      by (simp add:markov_orth_inv[OF assms(6)])
    then show ?case by simp
  qed

  have "M *v stat = stat"
    using assms(6) unfolding stat_def matrix_vector_mult_scaleR markov_def by simp
  hence b: "foldl (\<lambda>x N. M *v (N *v x)) stat (?rep yh) = stat"
    by (induction yh, auto)

  have "foldl (\<lambda>x N. N *v (M *v x)) a (?rep x) = matrix_pow M x *v a" for x a
  proof (induction x)
    case 0
    then show ?case by simp
  next
    case (Suc x)
    have "foldl (\<lambda>x N. N *v (M *v x)) a (?rep (x+1)) =  matrix_pow M (x+1) *v a"
      unfolding replicate_add using Suc by (simp add: matrix_vector_mul_assoc)
    then show ?case by simp
  qed
  hence c: "foldl (\<lambda>x N. N *v (M *v x)) a (?rep x @ [Q]) = Q *v (matrix_pow M (x+1) *v a)" for x a
    by (simp add:matrix_vector_mul_assoc matrix_mul_assoc)

  have d: "spec_bound N \<alpha> \<and> markov N" if t1: "N \<in> set (map (\<lambda>x. matrix_pow M (x + 1)) yt)" for N
  proof -
    obtain y where N_def: "N = matrix_pow M (y+1)"
      using t1 by auto
    hence d1: "spec_bound N (\<alpha>^(y+1))"
      unfolding N_def using spec_bound_pow assms(5,6) by blast
    have "spec_bound N (\<alpha>^1)"
      using \<alpha>_range by (intro spec_bound_mono[OF d1] power_decreasing) auto
    moreover have "markov N"
      unfolding N_def by (intro markov_matrix_pow assms(6))
    ultimately show ?thesis by simp
  qed

  have "?L = foldl (\<lambda>x M. M *v x) stat (intersperse M (map P' xs)) \<bullet> 1"
    unfolding P_eq xs_def map_map by (simp add:comp_def)
  also have "... = foldl (\<lambda>x M. M *v x) stat (intersperse M (map P' xs)@[M]) \<bullet> 1"
    by (simp add:markov_orth_inv[OF assms(6)])
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P' xs) \<bullet> 1"
    using xs_ne by (subst foldl_intersperse) auto
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat ((ys \<bind> (\<lambda>x. ?rep x @ [Q])) @ ?rep z) \<bullet> 1"
    unfolding bool_list_split[OF ys_z] P'_def List.bind_def by (simp add: comp_def map_concat)
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (ys \<bind> (\<lambda>x. ?rep x @ [Q])) \<bullet> 1"
    by (simp add: a)
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (?rep yh @[Q]@(yt \<bind>(\<lambda>x. ?rep x @ [Q]))) \<bullet> 1"
    unfolding ys_split by simp
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat ([Q]@(yt \<bind>(\<lambda>x. ?rep x @ [Q]))) \<bullet> 1"
    by (simp add:b)
  also have "... = foldl (\<lambda>x N. N *v x) stat (intersperse M (Q#(yt \<bind>(\<lambda>x.?rep x@[Q])))@[M])\<bullet>1"
    by (subst foldl_intersperse, auto)
  also have "... = foldl (\<lambda>x N. N *v x) stat (intersperse M (Q#(yt \<bind>(\<lambda>x.?rep x@[Q])))) \<bullet> 1"
    by (simp add:markov_orth_inv[OF assms(6)])
  also have "... = foldl (\<lambda>x N. N *v (M *v x)) (Q *v stat) (yt \<bind>(\<lambda>x.?rep x@[Q])) \<bullet> 1"
    by (subst foldl_intersperse_2, simp)
  also have "... = foldl (\<lambda>a x. foldl (\<lambda>x N. N *v (M *v x)) a (?rep x @ [Q])) (Q *v stat) yt \<bullet> 1"
    unfolding List.bind_def foldl_concat foldl_map by simp
  also have "... = foldl (\<lambda>a x. Q *v (matrix_pow M (x+1) *v a)) (Q *v stat) yt \<bullet> 1"
    unfolding c by simp
  also have "... = foldl (\<lambda>a N. Q *v (N *v a)) (Q *v stat) (map (\<lambda>x. matrix_pow M (x+1)) yt) \<bullet> 1"
    by (simp add:foldl_map)
  also have "... \<le> (\<mu> + \<alpha>*(1-\<mu>))^(length (map (\<lambda>x. matrix_pow M (x+1)) yt)+1)"
    unfolding \<mu>_def Q_def by (intro hitting_property_alg \<alpha>_range d) simp
  also have "... = (\<mu> + \<alpha>*(1-\<mu>))^(length ys)"
    unfolding ys_split by simp
  also have "... = ?R" unfolding len_ys by simp
  finally show ?thesis by simp
next
  case False
  hence I_empty: "I = {}" by simp

  have "?L = stat \<bullet> (1 :: real^'n)"
  proof (cases "l > 0")
    case True
    have "?L = foldl (\<lambda>x M. M *v x) stat ((intersperse M (map P [0..<l]))@[M]) \<bullet> 1"
      by (simp add:markov_orth_inv[OF assms(6)])
    also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P [0..<l])  \<bullet> 1"
      using True by (subst foldl_intersperse, auto)
    also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map (\<lambda>_. mat 1) [0..<l])  \<bullet> 1"
      unfolding  P_def using I_empty by simp
    also have "... = foldl (\<lambda>x _. M *v x) stat [0..<l] \<bullet> 1"
      unfolding foldl_map by simp
    also have "... = stat \<bullet> (1 :: real^'n)"
      by (induction l, auto simp add:markov_orth_inv[OF assms(6)])
    finally show ?thesis by simp
  next
    case False
    then show ?thesis by simp
  qed
  also have "... = 1"
    unfolding stat_def by (simp add:inner_vec_def)
  also have "... \<le> ?R" unfolding I_empty by simp
  finally show ?thesis by simp
qed

lemma uniform_property_alg:
  fixes x :: "('n :: finite)" and l :: nat
  assumes "i < l"
  defines "P j \<equiv> (if j = i then diag (ind_vec {x}) else mat 1)"
  assumes "markov M"
  shows "foldl (\<lambda>x M. M *v x) stat (intersperse M (map P [0..<l])) \<bullet> 1 = 1 / CARD('n)"
    (is "?L = ?R")
proof -
  have a:"l > 0" using assms(1) by simp

  have 0: "foldl (\<lambda>x N. M *v (N *v x)) y (xs) \<bullet> 1 = y  \<bullet> 1" if "set xs \<subseteq> {mat 1}" for xs y
    using that
  proof (induction xs rule:rev_induct)
    case Nil
    then show ?case by simp
  next
    case (snoc x xs)
    have "x = mat 1"
      using snoc(2) by simp
    hence "foldl (\<lambda>x N. M *v (N *v x)) y (xs @ [x]) \<bullet> 1 = foldl (\<lambda>x N. M *v (N *v x)) y xs \<bullet> 1"
      by (simp add:markov_orth_inv[OF assms(3)])
    also have "... = y \<bullet> 1"
      using snoc(2) by (intro snoc(1)) auto
    finally show ?case by simp
  qed

  have M_stat: "M *v stat = stat"
    using assms(3) unfolding stat_def matrix_vector_mult_scaleR markov_def by simp

  hence 1: "(foldl (\<lambda>x N. M *v (N *v x)) stat xs) = stat" if "set xs \<subseteq> {mat 1}" for xs
    using that by (induction xs, auto)

  have "?L = foldl (\<lambda>x M. M *v x) stat ((intersperse M (map P [0..<l]))@[M]) \<bullet> 1"
    by (simp add:markov_orth_inv[OF assms(3)])
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P [0..<l]) \<bullet> 1"
    using a by (subst foldl_intersperse) auto
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P ([0..<i+1]@[i+1..<l])) \<bullet> 1"
    using assms(1) by (subst upto_append) auto
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P [0..<i + 1]) \<bullet> 1"
    unfolding map_append foldl_append  P_def by (subst 0) auto
  also have "... = foldl (\<lambda>x N. M *v (N *v x)) stat (map P ([0..<i]@[i])) \<bullet> 1"
    by simp
  also have "... = (M *v (diag (ind_vec {x}) *v stat)) \<bullet> 1"
    unfolding map_append foldl_append P_def by (subst 1) auto
  also have "... = (diag (ind_vec {x}) *v stat) \<bullet> 1"
    by (simp add:markov_orth_inv[OF assms(3)])
  also have "... = ((1/CARD('n)) *\<^sub>R ind_vec {x}) \<bullet> 1"
    unfolding diag_def ind_vec_def  stat_def matrix_vector_mult_def
    by (intro arg_cong2[where f="(\<bullet>)"] refl)
      (vector of_bool_def sum.If_cases if_distrib if_distribR)
  also have "... = (1/CARD('n)) * (ind_vec {x} \<bullet> 1)"
    by simp
  also have "... = (1/CARD('n)) * 1"
    unfolding inner_vec_def ind_vec_def of_bool_def
    by (intro arg_cong2[where f="(*)"] refl) (simp)
  finally show ?thesis by simp
qed

end

lemma foldl_matrix_mult_expand:
  fixes Ms :: "(('r::{semiring_1,comm_monoid_mult})^'a^'a) list"
  shows "(foldl (\<lambda>x M. M *v x) a Ms) $ k = (\<Sum>x | length x = length Ms+1 \<and> x! length Ms = k.
  (\<Prod> i< length Ms. (Ms ! i) $ (x ! (i+1)) $ (x ! i)) * a $ (x ! 0))"
proof (induction Ms arbitrary: k rule:rev_induct)
  case Nil
  have "length x = Suc 0 \<Longrightarrow> x = [x!0]" for x :: "'a list"
    by (cases x, auto)
  hence "{x. length x = Suc 0 \<and> x ! 0 = k} = {[k]}"
    by auto
  thus ?case by auto
next
  case (snoc M Ms)
  let ?l = "length Ms"

  have 0: "finite {w. length w = Suc (length Ms) \<and> w ! length Ms = i}" for i :: 'a
    using finite_lists_length_eq[where A="UNIV::'a set" and n="?l +1"] by simp

  have "take (?l+1) x @ [x ! (?l+1)] = x" if "length x = ?l+2" for x :: "'a list"
  proof -
    have "take (?l+1) x @ [x ! (?l+1)] = take (Suc (?l+1)) x"
      using that by (intro take_Suc_conv_app_nth[symmetric], simp)
    also have "... = x"
      using that by simp
    finally show ?thesis by simp
  qed
  hence 1: "bij_betw  (take (?l+1)) {w. length w=?l+2 \<and> w!(?l+1) =k} {w. length w = ?l+1}"
    by (intro bij_betwI[where g="\<lambda>x. x@[k]"]) (auto simp add:nth_append)

  have "foldl (\<lambda>x M. M *v x) a (Ms @ [M]) $ k = (\<Sum>j\<in>UNIV. M$k$j *(foldl (\<lambda>x M. M *v x) a Ms $ j))"
    by (simp add:matrix_vector_mult_def)
  also have "... =
    (\<Sum>j\<in>UNIV. M$k$j * (\<Sum>w|length w=?l+1\<and>w!?l=j. (\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i) * a $ w!0))"
    unfolding snoc by simp
  also have "... =
    (\<Sum>j\<in>UNIV. (\<Sum>w|length w=?l+1\<and>w!?l=j. M$k$w!?l * (\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i) * a $ w!0))"
    by (intro sum.cong refl) (simp add: sum_distrib_left algebra_simps)
  also have "... = (\<Sum>w\<in> (\<Union>j \<in> UNIV. {w. length w=?l+1 \<and> w!?l =j}).
    M$k$w!?l*(\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i) * a $ w!0)"
    using 0 by (subst sum.UNION_disjoint, simp, simp) auto
  also have "... = (\<Sum>w | length w=?l+1. M$k$(w!?l)*(\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i) * a $ w!0)"
    by (intro sum.cong arg_cong2[where f="(*)"] refl) auto
  also have "... = (\<Sum>w \<in> take (?l+1) ` {w. length w=?l+2 \<and> w!(?l+1) =k}.
    M$k$w!?l*(\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i) * a $ w!0)"
    using 1 unfolding bij_betw_def by (intro sum.cong refl, auto)
  also have "... = (\<Sum>w|length w=?l+2\<and>w!(?l+1)=k. M$k$w!?l*(\<Prod>i<?l. Ms!i $ w!(i+1) $ w!i)* a$w!0)"
    using 1 unfolding bij_betw_def by (subst sum.reindex, auto)
  also have "... = (\<Sum>w|length w=?l+2\<and>w!(?l+1)=k.
    (Ms@[M])!?l$k$w!?l*(\<Prod>i<?l. (Ms@[M])!i $ w!(i+1) $ w!i)* a$w!0)"
    by (intro sum.cong arg_cong2[where f="(*)"] prod.cong refl) (auto simp add:nth_append)
  also have "... = (\<Sum>w|length w=?l+2\<and>w!(?l+1)=k. (\<Prod>i<(?l+1). (Ms@[M])!i $ w!(i+1) $ w!i)* a$w!0)"
    by (intro sum.cong, auto simp add:algebra_simps)
  finally have "foldl (\<lambda>x M. M *v x) a (Ms @ [M]) $ k =
    (\<Sum> w | length w = ?l+2 \<and> w ! (?l+1) = k. (\<Prod>i<(?l+1). (Ms@[M])!i $ w!(i+1) $ w!i)* a$w!0)"
    by simp
  then show ?case by simp
qed

lemma foldl_matrix_mult_expand_2:
  fixes Ms :: "(real^'a^'a) list"
  shows "(foldl (\<lambda>x M. M *v x) a Ms) \<bullet> 1 = (\<Sum>x | length x = length Ms+1.
          (\<Prod> i< length Ms. (Ms ! i) $ (x ! (i+1)) $ (x ! i)) * a $ (x ! 0))"
  (is "?L = ?R")
proof -
  let ?l = "length Ms"
  have "?L = (\<Sum>j \<in> UNIV. (foldl (\<lambda>x M. M *v x) a Ms) $ j)"
    by (simp add:inner_vec_def)
  also have "... = (\<Sum>j\<in>UNIV. \<Sum>x|length x=?l+1 \<and> x!?l=j.(\<Prod>i<?l. Ms!i $ x!(i+1) $ x!i) * a $ x!0)"
    unfolding foldl_matrix_mult_expand by simp
  also have "... = (\<Sum>x \<in> (\<Union>j\<in> UNIV.{w. length w = length Ms+1 \<and> w ! length Ms = j}).
          (\<Prod> i< length Ms. (Ms ! i) $ (x ! (i+1)) $ (x ! i)) * a $ (x ! 0))"
    using finite_lists_length_eq[where A="UNIV::'a set" and n="?l +1"]
    by (intro sum.UNION_disjoint[symmetric]) auto
  also have "... = ?R"
    by (intro sum.cong, auto)
  finally show ?thesis by simp
qed

end


(*  Title:      HOL/Map.thy
    Author:     Tobias Nipkow, based on a theory by David von Oheimb
    Copyright   1997-2003 TU Muenchen

The datatype of "maps"; strongly resembles maps in VDM.
*)

section \<open>Maps\<close>

theory Map
  imports List
  abbrevs "(=" = "\<subseteq>\<^sub>m"
begin

type_synonym ('a, 'b) "map" = "'a \<Rightarrow> 'b option" (infixr "\<rightharpoonup>" 0)

abbreviation (input)
  empty :: "'a \<rightharpoonup> 'b" where
  "empty \<equiv> \<lambda>x. None"

definition
  map_comp :: "('b \<rightharpoonup> 'c) \<Rightarrow> ('a \<rightharpoonup> 'b) \<Rightarrow> ('a \<rightharpoonup> 'c)"  (infixl "\<circ>\<^sub>m" 55) where
  "f \<circ>\<^sub>m g = (\<lambda>k. case g k of None \<Rightarrow> None | Some v \<Rightarrow> f v)"

definition
  map_add :: "('a \<rightharpoonup> 'b) \<Rightarrow> ('a \<rightharpoonup> 'b) \<Rightarrow> ('a \<rightharpoonup> 'b)"  (infixl "++" 100) where
  "m1 ++ m2 = (\<lambda>x. case m2 x of None \<Rightarrow> m1 x | Some y \<Rightarrow> Some y)"

definition
  restrict_map :: "('a \<rightharpoonup> 'b) \<Rightarrow> 'a set \<Rightarrow> ('a \<rightharpoonup> 'b)"  (infixl "|`"  110) where
  "m|`A = (\<lambda>x. if x \<in> A then m x else None)"

notation (latex output)
  restrict_map  ("_\<restriction>\<^bsub>_\<^esub>" [111,110] 110)

definition
  dom :: "('a \<rightharpoonup> 'b) \<Rightarrow> 'a set" where
  "dom m = {a. m a \<noteq> None}"

definition
  ran :: "('a \<rightharpoonup> 'b) \<Rightarrow> 'b set" where
  "ran m = {b. \<exists>a. m a = Some b}"

definition
  graph :: "('a \<rightharpoonup> 'b) \<Rightarrow> ('a \<times> 'b) set" where
  "graph m = {(a, b) | a b. m a = Some b}"

definition
  map_le :: "('a \<rightharpoonup> 'b) \<Rightarrow> ('a \<rightharpoonup> 'b) \<Rightarrow> bool"  (infix "\<subseteq>\<^sub>m" 50) where
  "(m\<^sub>1 \<subseteq>\<^sub>m m\<^sub>2) \<longleftrightarrow> (\<forall>a \<in> dom m\<^sub>1. m\<^sub>1 a = m\<^sub>2 a)"

text \<open>Function update syntax \<open>f(x := y, \<dots>)\<close> is extended with \<open>x \<mapsto> y\<close>, which is short for
\<open>x := Some y\<close>. \<open>:=\<close> and \<open>\<mapsto>\<close> can be mixed freely.
The syntax \<open>[x \<mapsto> y, \<dots>]\<close> is short for \<open>Map.empty(x \<mapsto> y, \<dots>)\<close>
but must only contain \<open>\<mapsto>\<close>, not \<open>:=\<close>, because \<open>[x:=y]\<close> clashes with the list update syntax \<open>xs[i:=x]\<close>.\<close>

nonterminal maplet and maplets

syntax
  "_maplet"  :: "['a, 'a] \<Rightarrow> maplet"             ("_ /\<mapsto>/ _")
  ""         :: "maplet \<Rightarrow> updbind"              ("_")
  ""         :: "maplet \<Rightarrow> maplets"             ("_")
  "_Maplets" :: "[maplet, maplets] \<Rightarrow> maplets" ("_,/ _")
  "_Map"     :: "maplets \<Rightarrow> 'a \<rightharpoonup> 'b"           ("(1[_])")
(* Syntax forbids \<open>[\<dots>, x := y, \<dots>]\<close> by introducing \<open>maplets\<close> in addition to \<open>updbinds\<close> *)

syntax (ASCII)
  "_maplet"  :: "['a, 'a] \<Rightarrow> maplet"             ("_ /|->/ _")

translations
  "_Update f (_maplet x y)" \<rightleftharpoons> "f(x := CONST Some y)"
  "_Maplets m ms" \<rightharpoonup> "_updbinds m ms"
  "_Map ms" \<rightharpoonup> "_Update (CONST empty) ms"

(* Printing must create \<open>_Map\<close> only for \<open>_maplet\<close> *)
  "_Map (_maplet x y)"  \<leftharpoondown> "_Update (\<lambda>u. CONST None) (_maplet x y)"
  "_Map (_updbinds m (_maplet x y))"  \<leftharpoondown> "_Update (_Map m) (_maplet x y)"

text \<open>Updating with lists:\<close>

primrec map_of :: "('a \<times> 'b) list \<Rightarrow> 'a \<rightharpoonup> 'b" where
  "map_of [] = empty"
| "map_of (p # ps) = (map_of ps)(fst p \<mapsto> snd p)"

lemma map_of_Cons_code [code]:
  "map_of [] k = None"
  "map_of ((l, v) # ps) k = (if l = k then Some v else map_of ps k)"
  by simp_all

definition map_upds :: "('a \<rightharpoonup> 'b) \<Rightarrow> 'a list \<Rightarrow> 'b list \<Rightarrow> 'a \<rightharpoonup> 'b" where
"map_upds m xs ys = m ++ map_of (rev (zip xs ys))"

text \<open>There is also the more specialized update syntax \<open>xs [\<mapsto>] ys\<close> for lists \<open>xs\<close> and \<open>ys\<close>.\<close>

syntax
  "_maplets"  :: "['a, 'a] \<Rightarrow> maplet"             ("_ /[\<mapsto>]/ _")

syntax (ASCII)
  "_maplets" :: "['a, 'a] \<Rightarrow> maplet"             ("_ /[|->]/ _")

translations
  "_Update m (_maplets xs ys)" \<rightleftharpoons> "CONST map_upds m xs ys"

  "_Map (_maplets xs ys)"  \<leftharpoondown> "_Update (\<lambda>u. CONST None) (_maplets xs ys)"
  "_Map (_updbinds m (_maplets xs ys))"  \<leftharpoondown> "_Update (_Map m) (_maplets xs ys)"

subsection \<open>@{term [source] empty}\<close>

lemma empty_upd_none [simp]: "empty(x := None) = empty"
  by (rule ext) simp

subsection \<open>@{term [source] map_upd}\<close>

lemma map_upd_triv: "t k = Some x \<Longrightarrow> t(k\<mapsto>x) = t"
  by (rule ext) simp

lemma map_upd_nonempty [simp]: "t(k\<mapsto>x) \<noteq> empty"
proof
  assume "t(k \<mapsto> x) = empty"
  then have "(t(k \<mapsto> x)) k = None" by simp
  then show False by simp
qed

lemma map_upd_eqD1:
  assumes "m(a\<mapsto>x) = n(a\<mapsto>y)"
  shows "x = y"
proof -
  from assms have "(m(a\<mapsto>x)) a = (n(a\<mapsto>y)) a" by simp
  then show ?thesis by simp
qed

lemma map_upd_Some_unfold:
  "((m(a\<mapsto>b)) x = Some y) = (x = a \<and> b = y \<or> x \<noteq> a \<and> m x = Some y)"
  by auto

lemma image_map_upd [simp]: "x \<notin> A \<Longrightarrow> m(x \<mapsto> y) ` A = m ` A"
  by auto

lemma finite_range_updI:
  assumes "finite (range f)" shows "finite (range (f(a\<mapsto>b)))"
proof -
  have "range (f(a\<mapsto>b)) \<subseteq> insert (Some b) (range f)"
    by auto
  then show ?thesis
    by (rule finite_subset) (use assms in auto)
qed

subsection \<open>@{term [source] map_of}\<close>

lemma map_of_eq_empty_iff [simp]:
  "map_of xys = empty \<longleftrightarrow> xys = []"
proof
  show "map_of xys = empty \<Longrightarrow> xys = []"
    by (induction xys) simp_all
qed simp

lemma empty_eq_map_of_iff [simp]:
  "empty = map_of xys \<longleftrightarrow> xys = []"
by(subst eq_commute) simp

lemma map_of_eq_None_iff:
  "(map_of xys x = None) = (x \<notin> fst ` (set xys))"
by (induct xys) simp_all

lemma map_of_eq_Some_iff [simp]:
  "distinct(map fst xys) \<Longrightarrow> (map_of xys x = Some y) = ((x,y) \<in> set xys)"
proof (induct xys)
  case (Cons xy xys)
  then show ?case
    by (cases xy) (auto simp flip: map_of_eq_None_iff)
qed auto

lemma Some_eq_map_of_iff [simp]:
  "distinct(map fst xys) \<Longrightarrow> (Some y = map_of xys x) = ((x,y) \<in> set xys)"
by (auto simp del: map_of_eq_Some_iff simp: map_of_eq_Some_iff [symmetric])

lemma map_of_is_SomeI [simp]:
  "\<lbrakk>distinct(map fst xys); (x,y) \<in> set xys\<rbrakk> \<Longrightarrow> map_of xys x = Some y"
  by simp

lemma map_of_zip_is_None [simp]:
  "length xs = length ys \<Longrightarrow> (map_of (zip xs ys) x = None) = (x \<notin> set xs)"
by (induct rule: list_induct2) simp_all

lemma map_of_zip_is_Some:
  assumes "length xs = length ys"
  shows "x \<in> set xs \<longleftrightarrow> (\<exists>y. map_of (zip xs ys) x = Some y)"
using assms by (induct rule: list_induct2) simp_all

lemma map_of_zip_upd:
  fixes x :: 'a and xs :: "'a list" and ys zs :: "'b list"
  assumes "length ys = length xs"
    and "length zs = length xs"
    and "x \<notin> set xs"
    and "(map_of (zip xs ys))(x \<mapsto> y) = (map_of (zip xs zs))(x \<mapsto> z)"
  shows "map_of (zip xs ys) = map_of (zip xs zs)"
proof
  fix x' :: 'a
  show "map_of (zip xs ys) x' = map_of (zip xs zs) x'"
  proof (cases "x = x'")
    case True
    from assms True map_of_zip_is_None [of xs ys x']
      have "map_of (zip xs ys) x' = None" by simp
    moreover from assms True map_of_zip_is_None [of xs zs x']
      have "map_of (zip xs zs) x' = None" by simp
    ultimately show ?thesis by simp
  next
    case False from assms
      have "((map_of (zip xs ys))(x \<mapsto> y)) x' = ((map_of (zip xs zs))(x \<mapsto> z)) x'" by auto
    with False show ?thesis by simp
  qed
qed

lemma map_of_zip_inject:
  assumes "length ys = length xs"
    and "length zs = length xs"
    and dist: "distinct xs"
    and map_of: "map_of (zip xs ys) = map_of (zip xs zs)"
  shows "ys = zs"
  using assms(1) assms(2)[symmetric]
  using dist map_of
proof (induct ys xs zs rule: list_induct3)
  case Nil show ?case by simp
next
  case (Cons y ys x xs z zs)
  from \<open>map_of (zip (x#xs) (y#ys)) = map_of (zip (x#xs) (z#zs))\<close>
    have map_of: "(map_of (zip xs ys))(x \<mapsto> y) = (map_of (zip xs zs))(x \<mapsto> z)" by simp
  from Cons have "length ys = length xs" and "length zs = length xs"
    and "x \<notin> set xs" by simp_all
  then have "map_of (zip xs ys) = map_of (zip xs zs)" using map_of by (rule map_of_zip_upd)
  with Cons.hyps \<open>distinct (x # xs)\<close> have "ys = zs" by simp
  moreover from map_of have "y = z" by (rule map_upd_eqD1)
  ultimately show ?case by simp
qed

lemma map_of_zip_nth:
  assumes "length xs = length ys"
  assumes "distinct xs"
  assumes "i < length ys"
  shows "map_of (zip xs ys) (xs ! i) = Some (ys ! i)"
using assms proof (induct arbitrary: i rule: list_induct2)
  case Nil
  then show ?case by simp
next
  case (Cons x xs y ys)
  then show ?case
    using less_Suc_eq_0_disj by auto
qed

lemma map_of_zip_map:
  "map_of (zip xs (map f xs)) = (\<lambda>x. if x \<in> set xs then Some (f x) else None)"
  by (induct xs) (simp_all add: fun_eq_iff)

lemma finite_range_map_of: "finite (range (map_of xys))"
proof (induct xys)
  case (Cons a xys)
  then show ?case
    using finite_range_updI by fastforce
qed auto

lemma map_of_SomeD: "map_of xs k = Some y \<Longrightarrow> (k, y) \<in> set xs"
  by (induct xs) (auto split: if_splits)

lemma map_of_mapk_SomeI:
  "inj f \<Longrightarrow> map_of t k = Some x \<Longrightarrow>
   map_of (map (case_prod (\<lambda>k. Pair (f k))) t) (f k) = Some x"
by (induct t) (auto simp: inj_eq)

lemma weak_map_of_SomeI: "(k, x) \<in> set l \<Longrightarrow> \<exists>x. map_of l k = Some x"
by (induct l) auto

lemma map_of_filter_in:
  "map_of xs k = Some z \<Longrightarrow> P k z \<Longrightarrow> map_of (filter (case_prod P) xs) k = Some z"
by (induct xs) auto

lemma map_of_map:
  "map_of (map (\<lambda>(k, v). (k, f v)) xs) = map_option f \<circ> map_of xs"
  by (induct xs) (auto simp: fun_eq_iff)

lemma dom_map_option:
  "dom (\<lambda>k. map_option (f k) (m k)) = dom m"
  by (simp add: dom_def)

lemma dom_map_option_comp [simp]:
  "dom (map_option g \<circ> m) = dom m"
  using dom_map_option [of "\<lambda>_. g" m] by (simp add: comp_def)

subsection \<open>\<^const>\<open>map_option\<close> related\<close>

lemma map_option_o_empty [simp]: "map_option f \<circ> empty = empty"
by (rule ext) simp

lemma map_option_o_map_upd [simp]:
  "map_option f \<circ> m(a\<mapsto>b) = (map_option f \<circ> m)(a\<mapsto>f b)"
by (rule ext) simp

subsection \<open>@{term [source] map_comp} related\<close>

lemma map_comp_empty [simp]:
  "m \<circ>\<^sub>m empty = empty"
  "empty \<circ>\<^sub>m m = empty"
by (auto simp: map_comp_def split: option.splits)

lemma map_comp_simps [simp]:
  "m2 k = None \<Longrightarrow> (m1 \<circ>\<^sub>m m2) k = None"
  "m2 k = Some k' \<Longrightarrow> (m1 \<circ>\<^sub>m m2) k = m1 k'"
by (auto simp: map_comp_def)

lemma map_comp_Some_iff:
  "((m1 \<circ>\<^sub>m m2) k = Some v) = (\<exists>k'. m2 k = Some k' \<and> m1 k' = Some v)"
by (auto simp: map_comp_def split: option.splits)

lemma map_comp_None_iff:
  "((m1 \<circ>\<^sub>m m2) k = None) = (m2 k = None \<or> (\<exists>k'. m2 k = Some k' \<and> m1 k' = None)) "
by (auto simp: map_comp_def split: option.splits)

subsection \<open>\<open>++\<close>\<close>

lemma map_add_empty[simp]: "m ++ empty = m"
by(simp add: map_add_def)

lemma empty_map_add[simp]: "empty ++ m = m"
by (rule ext) (simp add: map_add_def split: option.split)

lemma map_add_assoc[simp]: "m1 ++ (m2 ++ m3) = (m1 ++ m2) ++ m3"
by (rule ext) (simp add: map_add_def split: option.split)

lemma map_add_Some_iff:
  "((m ++ n) k = Some x) = (n k = Some x \<or> n k = None \<and> m k = Some x)"
by (simp add: map_add_def split: option.split)

lemma map_add_SomeD [dest!]:
  "(m ++ n) k = Some x \<Longrightarrow> n k = Some x \<or> n k = None \<and> m k = Some x"
by (rule map_add_Some_iff [THEN iffD1])

lemma map_add_find_right [simp]: "n k = Some xx \<Longrightarrow> (m ++ n) k = Some xx"
by (subst map_add_Some_iff) fast

lemma map_add_None [iff]: "((m ++ n) k = None) = (n k = None \<and> m k = None)"
by (simp add: map_add_def split: option.split)

lemma map_add_upd[simp]: "f ++ g(x\<mapsto>y) = (f ++ g)(x\<mapsto>y)"
by (rule ext) (simp add: map_add_def)

lemma map_add_upds[simp]: "m1 ++ (m2(xs[\<mapsto>]ys)) = (m1++m2)(xs[\<mapsto>]ys)"
by (simp add: map_upds_def)

lemma map_add_upd_left: "m\<notin>dom e2 \<Longrightarrow> e1(m \<mapsto> u1) ++ e2 = (e1 ++ e2)(m \<mapsto> u1)"
by (rule ext) (auto simp: map_add_def dom_def split: option.split)

lemma map_of_append[simp]: "map_of (xs @ ys) = map_of ys ++ map_of xs"
  unfolding map_add_def
proof (induct xs)
  case (Cons a xs)
  then show ?case
    by (force split: option.split)
qed auto

lemma finite_range_map_of_map_add:
  "finite (range f) \<Longrightarrow> finite (range (f ++ map_of l))"
proof (induct l)
case (Cons a l)
  then show ?case
    by (metis finite_range_updI map_add_upd map_of.simps(2))
qed auto

lemma inj_on_map_add_dom [iff]:
  "inj_on (m ++ m') (dom m') = inj_on m' (dom m')"
  by (fastforce simp: map_add_def dom_def inj_on_def split: option.splits)

lemma map_upds_fold_map_upd:
  "m(ks[\<mapsto>]vs) = foldl (\<lambda>m (k, v). m(k \<mapsto> v)) m (zip ks vs)"
unfolding map_upds_def proof (rule sym, rule zip_obtain_same_length)
  fix ks :: "'a list" and vs :: "'b list"
  assume "length ks = length vs"
  then show "foldl (\<lambda>m (k, v). m(k\<mapsto>v)) m (zip ks vs) = m ++ map_of (rev (zip ks vs))"
    by(induct arbitrary: m rule: list_induct2) simp_all
qed

lemma map_add_map_of_foldr:
  "m ++ map_of ps = foldr (\<lambda>(k, v) m. m(k \<mapsto> v)) ps m"
  by (induct ps) (auto simp: fun_eq_iff map_add_def)

subsection \<open>@{term [source] restrict_map}\<close>

lemma restrict_map_to_empty [simp]: "m|`{} = empty"
  by (simp add: restrict_map_def)

lemma restrict_map_insert: "f |` (insert a A) = (f |` A)(a := f a)"
  by (auto simp: restrict_map_def)

lemma restrict_map_empty [simp]: "empty|`D = empty"
  by (simp add: restrict_map_def)

lemma restrict_in [simp]: "x \<in> A \<Longrightarrow> (m|`A) x = m x"
  by (simp add: restrict_map_def)

lemma restrict_out [simp]: "x \<notin> A \<Longrightarrow> (m|`A) x = None"
  by (simp add: restrict_map_def)

lemma ran_restrictD: "y \<in> ran (m|`A) \<Longrightarrow> \<exists>x\<in>A. m x = Some y"
  by (auto simp: restrict_map_def ran_def split: if_split_asm)

lemma dom_restrict [simp]: "dom (m|`A) = dom m \<inter> A"
  by (auto simp: restrict_map_def dom_def split: if_split_asm)

lemma restrict_upd_same [simp]: "m(x\<mapsto>y)|`(-{x}) = m|`(-{x})"
  by (rule ext) (auto simp: restrict_map_def)

lemma restrict_restrict [simp]: "m|`A|`B = m|`(A\<inter>B)"
  by (rule ext) (auto simp: restrict_map_def)

lemma restrict_fun_upd [simp]:
  "m(x := y)|`D = (if x \<in> D then (m|`(D-{x}))(x := y) else m|`D)"
  by (simp add: restrict_map_def fun_eq_iff)

lemma fun_upd_None_restrict [simp]:
  "(m|`D)(x := None) = (if x \<in> D then m|`(D - {x}) else m|`D)"
  by (simp add: restrict_map_def fun_eq_iff)

lemma fun_upd_restrict: "(m|`D)(x := y) = (m|`(D-{x}))(x := y)"
  by (simp add: restrict_map_def fun_eq_iff)

lemma fun_upd_restrict_conv [simp]:
  "x \<in> D \<Longrightarrow> (m|`D)(x := y) = (m|`(D-{x}))(x := y)"
  by (rule fun_upd_restrict)

lemma map_of_map_restrict:
  "map_of (map (\<lambda>k. (k, f k)) ks) = (Some \<circ> f) |` set ks"
  by (induct ks) (simp_all add: fun_eq_iff restrict_map_insert)

lemma restrict_complement_singleton_eq:
  "f |` (- {x}) = f(x := None)"
  by auto

subsection \<open>@{term [source] map_upds}\<close>

lemma map_upds_Nil1 [simp]: "m([] [\<mapsto>] bs) = m"
  by (simp add: map_upds_def)

lemma map_upds_Nil2 [simp]: "m(as [\<mapsto>] []) = m"
  by (simp add:map_upds_def)

lemma map_upds_Cons [simp]: "m(a#as [\<mapsto>] b#bs) = (m(a\<mapsto>b))(as[\<mapsto>]bs)"
  by (simp add:map_upds_def)

lemma map_upds_append1 [simp]:
  "size xs < size ys \<Longrightarrow> m(xs@[x] [\<mapsto>] ys) = m(xs [\<mapsto>] ys, x \<mapsto> ys!size xs)"
proof (induct xs arbitrary: ys m)
  case Nil
  then show ?case
    by (auto simp: neq_Nil_conv)
next
  case (Cons a xs)
  then show ?case
    by (cases ys) auto
qed

lemma map_upds_list_update2_drop [simp]:
  "size xs \<le> i \<Longrightarrow> m(xs[\<mapsto>]ys[i:=y]) = m(xs[\<mapsto>]ys)"
proof (induct xs arbitrary: m ys i)
  case Nil
  then show ?case
    by auto
next
  case (Cons a xs)
  then show ?case
    by (cases ys) (use Cons in \<open>auto split: nat.split\<close>)
qed

text \<open>Something weirdly sensitive about this proof, which needs only four lines in apply style\<close>
lemma map_upd_upds_conv_if:
  "(f(x\<mapsto>y))(xs [\<mapsto>] ys) =
   (if x \<in> set(take (length ys) xs) then f(xs [\<mapsto>] ys)
                                    else (f(xs [\<mapsto>] ys))(x\<mapsto>y))"
proof (induct xs arbitrary: x y ys f)
  case (Cons a xs)
  show ?case
  proof (cases ys)
    case (Cons z zs)
    then show ?thesis
      using Cons.hyps
      apply (auto split: if_split simp: fun_upd_twist)
      using Cons.hyps apply fastforce+
      done
  qed auto
qed auto

lemma map_upds_twist [simp]:
  "a \<notin> set as \<Longrightarrow> m(a\<mapsto>b, as[\<mapsto>]bs) = m(as[\<mapsto>]bs, a\<mapsto>b)"
using set_take_subset by (fastforce simp add: map_upd_upds_conv_if)

lemma map_upds_apply_nontin [simp]:
  "x \<notin> set xs \<Longrightarrow> (f(xs[\<mapsto>]ys)) x = f x"
proof (induct xs arbitrary: ys)
  case (Cons a xs)
  then show ?case
    by (cases ys) (auto simp: map_upd_upds_conv_if)
qed auto

lemma fun_upds_append_drop [simp]:
  "size xs = size ys \<Longrightarrow> m(xs@zs[\<mapsto>]ys) = m(xs[\<mapsto>]ys)"
proof (induct xs arbitrary: ys)
  case (Cons a xs)
  then show ?case
    by (cases ys) (auto simp: map_upd_upds_conv_if)
qed auto

lemma fun_upds_append2_drop [simp]:
  "size xs = size ys \<Longrightarrow> m(xs[\<mapsto>]ys@zs) = m(xs[\<mapsto>]ys)"
proof (induct xs arbitrary: ys)
  case (Cons a xs)
  then show ?case
    by (cases ys) (auto simp: map_upd_upds_conv_if)
qed auto

lemma restrict_map_upds[simp]:
  "\<lbrakk> length xs = length ys; set xs \<subseteq> D \<rbrakk>
    \<Longrightarrow> m(xs [\<mapsto>] ys)|`D = (m|`(D - set xs))(xs [\<mapsto>] ys)"
proof (induct xs arbitrary: m ys)
  case (Cons a xs)
  then show ?case
  proof (cases ys)
    case (Cons z zs)
    with Cons.hyps Cons.prems show ?thesis
      apply (simp add: insert_absorb flip: Diff_insert)
      apply (auto simp add: map_upd_upds_conv_if)
      done
  qed auto
qed auto

subsection \<open>@{term [source] dom}\<close>

lemma dom_eq_empty_conv [simp]: "dom f = {} \<longleftrightarrow> f = empty"
  by (auto simp: dom_def)

lemma domI: "m a = Some b \<Longrightarrow> a \<in> dom m"
  by (simp add: dom_def)
(* declare domI [intro]? *)

lemma domD: "a \<in> dom m \<Longrightarrow> \<exists>b. m a = Some b"
  by (cases "m a") (auto simp add: dom_def)

lemma domIff [iff, simp del, code_unfold]: "a \<in> dom m \<longleftrightarrow> m a \<noteq> None"
  by (simp add: dom_def)

lemma dom_empty [simp]: "dom empty = {}"
  by (simp add: dom_def)

lemma dom_fun_upd [simp]:
  "dom(f(x := y)) = (if y = None then dom f - {x} else insert x (dom f))"
  by (auto simp: dom_def)

lemma dom_if:
  "dom (\<lambda>x. if P x then f x else g x) = dom f \<inter> {x. P x} \<union> dom g \<inter> {x. \<not> P x}"
  by (auto split: if_splits)

lemma dom_map_of_conv_image_fst:
  "dom (map_of xys) = fst ` set xys"
  by (induct xys) (auto simp add: dom_if)

lemma dom_map_of_zip [simp]: "length xs = length ys \<Longrightarrow> dom (map_of (zip xs ys)) = set xs"
  by (induct rule: list_induct2) (auto simp: dom_if)

lemma finite_dom_map_of: "finite (dom (map_of l))"
  by (induct l) (auto simp: dom_def insert_Collect [symmetric])

lemma dom_map_upds [simp]:
  "dom(m(xs[\<mapsto>]ys)) = set(take (length ys) xs) \<union> dom m"
proof (induct xs arbitrary: ys)
  case (Cons a xs)
  then show ?case
    by (cases ys) (auto simp: map_upd_upds_conv_if)
qed auto

lemma dom_map_add [simp]: "dom (m ++ n) = dom n \<union> dom m"
  by (auto simp: dom_def)

lemma dom_override_on [simp]:
  "dom (override_on f g A) =
    (dom f  - {a. a \<in> A - dom g}) \<union> {a. a \<in> A \<inter> dom g}"
  by (auto simp: dom_def override_on_def)

lemma map_add_dom_app_simps:
  "m \<in> dom l2 \<Longrightarrow> (l1 ++ l2) m = l2 m"
  "m \<notin> dom l1 \<Longrightarrow> (l1 ++ l2) m = l2 m"
  "m \<notin> dom l2 \<Longrightarrow> (l1 ++ l2) m = l1 m"
  by (auto simp add: map_add_def split: option.split_asm)

lemma dom_const [simp]:
  "dom (\<lambda>x. Some (f x)) = UNIV"
  by auto

(* Due to John Matthews - could be rephrased with dom *)
lemma finite_map_freshness:
  "finite (dom (f :: 'a \<rightharpoonup> 'b)) \<Longrightarrow> \<not> finite (UNIV :: 'a set) \<Longrightarrow>
   \<exists>x. f x = None"
  by (bestsimp dest: ex_new_if_finite)

lemma dom_minus:
  "f x = None \<Longrightarrow> dom f - insert x A = dom f - A"
  unfolding dom_def by simp

lemma insert_dom:
  "f x = Some y \<Longrightarrow> insert x (dom f) = dom f"
  unfolding dom_def by auto

lemma map_of_map_keys:
  "set xs = dom m \<Longrightarrow> map_of (map (\<lambda>k. (k, the (m k))) xs) = m"
  by (rule ext) (auto simp add: map_of_map_restrict restrict_map_def)

lemma map_of_eqI:
  assumes set_eq: "set (map fst xs) = set (map fst ys)"
  assumes map_eq: "\<forall>k\<in>set (map fst xs). map_of xs k = map_of ys k"
  shows "map_of xs = map_of ys"
proof (rule ext)
  fix k show "map_of xs k = map_of ys k"
  proof (cases "map_of xs k")
    case None
    then have "k \<notin> set (map fst xs)" by (simp add: map_of_eq_None_iff)
    with set_eq have "k \<notin> set (map fst ys)" by simp
    then have "map_of ys k = None" by (simp add: map_of_eq_None_iff)
    with None show ?thesis by simp
  next
    case (Some v)
    then have "k \<in> set (map fst xs)" by (auto simp add: dom_map_of_conv_image_fst [symmetric])
    with map_eq show ?thesis by auto
  qed
qed

lemma map_of_eq_dom:
  assumes "map_of xs = map_of ys"
  shows "fst ` set xs = fst ` set ys"
proof -
  from assms have "dom (map_of xs) = dom (map_of ys)" by simp
  then show ?thesis by (simp add: dom_map_of_conv_image_fst)
qed

lemma finite_set_of_finite_maps:
  assumes "finite A" "finite B"
  shows "finite {m. dom m = A \<and> ran m \<subseteq> B}" (is "finite ?S")
proof -
  let ?S' = "{m. \<forall>x. (x \<in> A \<longrightarrow> m x \<in> Some ` B) \<and> (x \<notin> A \<longrightarrow> m x = None)}"
  have "?S = ?S'"
  proof
    show "?S \<subseteq> ?S'" by (auto simp: dom_def ran_def image_def)
    show "?S' \<subseteq> ?S"
    proof
      fix m assume "m \<in> ?S'"
      hence 1: "dom m = A" by force
      hence 2: "ran m \<subseteq> B" using \<open>m \<in> ?S'\<close> by (auto simp: dom_def ran_def)
      from 1 2 show "m \<in> ?S" by blast
    qed
  qed
  with assms show ?thesis by(simp add: finite_set_of_finite_funs)
qed

subsection \<open>@{term [source] ran}\<close>

lemma ranI: "m a = Some b \<Longrightarrow> b \<in> ran m"
  by (auto simp: ran_def)
(* declare ranI [intro]? *)

lemma ran_empty [simp]: "ran empty = {}"
  by (auto simp: ran_def)

lemma ran_map_upd [simp]:  "m a = None \<Longrightarrow> ran(m(a\<mapsto>b)) = insert b (ran m)"
  unfolding ran_def
  by force

lemma fun_upd_None_if_notin_dom[simp]: "k \<notin> dom m \<Longrightarrow> m(k := None) = m"
  by auto

lemma ran_map_upd_Some:
  "\<lbrakk> m x = Some y; inj_on m (dom m); z \<notin> ran m \<rbrakk> \<Longrightarrow> ran(m(x := Some z)) = ran m - {y} \<union> {z}"
by(force simp add: ran_def domI inj_onD)

lemma ran_map_add:
  assumes "dom m1 \<inter> dom m2 = {}"
  shows "ran (m1 ++ m2) = ran m1 \<union> ran m2"
proof
  show "ran (m1 ++ m2) \<subseteq> ran m1 \<union> ran m2"
    unfolding ran_def by auto
next
  show "ran m1 \<union> ran m2 \<subseteq> ran (m1 ++ m2)"
  proof -
    have "(m1 ++ m2) x = Some y" if "m1 x = Some y" for x y
      using assms map_add_comm that by fastforce
    moreover have "(m1 ++ m2) x = Some y" if "m2 x = Some y" for x y
      using assms that by auto
    ultimately show ?thesis
      unfolding ran_def by blast
  qed
qed

lemma finite_ran:
  assumes "finite (dom p)"
  shows "finite (ran p)"
proof -
  have "ran p = (\<lambda>x. the (p x)) ` dom p"
    unfolding ran_def by force
  from this \<open>finite (dom p)\<close> show ?thesis by auto
qed

lemma ran_distinct:
  assumes dist: "distinct (map fst al)"
  shows "ran (map_of al) = snd ` set al"
  using assms
proof (induct al)
  case Nil
  then show ?case by simp
next
  case (Cons kv al)
  then have "ran (map_of al) = snd ` set al" by simp
  moreover from Cons.prems have "map_of al (fst kv) = None"
    by (simp add: map_of_eq_None_iff)
  ultimately show ?case by (simp only: map_of.simps ran_map_upd) simp
qed

lemma ran_map_of_zip:
  assumes "length xs = length ys" "distinct xs"
  shows "ran (map_of (zip xs ys)) = set ys"
using assms by (simp add: ran_distinct set_map[symmetric])

lemma ran_map_option: "ran (\<lambda>x. map_option f (m x)) = f ` ran m"
  by (auto simp add: ran_def)

subsection \<open>@{term [source] graph}\<close>

lemma in_graphI: "m k = Some v \<Longrightarrow> (k, v) \<in> graph m"
  unfolding graph_def by blast

lemma in_graphD: "(k, v) \<in> graph m \<Longrightarrow> m k = Some v"
  unfolding graph_def by blast

lemma graph_map_upd[simp]: "graph (m(k \<mapsto> v)) = insert (k, v) (graph (m(k := None)))"
  unfolding graph_def by (auto split: if_splits)

lemma graph_fun_upd_None: "graph (m(k := None)) = {e \<in> graph m. fst e \<noteq> k}"
  unfolding graph_def by (auto split: if_splits)

lemma graph_restrictD:
  assumes "(k, v) \<in> graph (m |` A)"
  shows "k \<in> A" and "m k = Some v"
  using assms unfolding graph_def
  by (auto simp: restrict_map_def split: if_splits)

lemma graph_map_comp[simp]: "graph (m1 \<circ>\<^sub>m m2) = graph m2 O graph m1"
  unfolding graph_def by (auto simp: map_comp_Some_iff relcomp_unfold)

lemma graph_map_add: "dom m1 \<inter> dom m2 = {} \<Longrightarrow> graph (m1 ++ m2) = graph m1 \<union> graph m2"
  unfolding graph_def using map_add_comm by force

lemma graph_eq_to_snd_dom: "graph m = (\<lambda>x. (x, the (m x))) ` dom m"
  unfolding graph_def dom_def by force

lemma fst_graph_eq_dom: "fst ` graph m = dom m"
  unfolding graph_eq_to_snd_dom by force

lemma graph_domD: "x \<in> graph m \<Longrightarrow> fst x \<in> dom m"
  using fst_graph_eq_dom by (metis imageI)

lemma snd_graph_ran: "snd ` graph m = ran m"
  unfolding graph_def ran_def by force

lemma graph_ranD: "x \<in> graph m \<Longrightarrow> snd x \<in> ran m"
  using snd_graph_ran by (metis imageI)

lemma finite_graph_map_of: "finite (graph (map_of al))"
  unfolding graph_eq_to_snd_dom finite_dom_map_of
  using finite_dom_map_of by blast

lemma graph_map_of_if_distinct_dom: "distinct (map fst al) \<Longrightarrow> graph (map_of al) = set al"
  unfolding graph_def by auto

lemma finite_graph_iff_finite_dom[simp]: "finite (graph m) = finite (dom m)"
  by (metis graph_eq_to_snd_dom finite_imageI fst_graph_eq_dom)

lemma inj_on_fst_graph: "inj_on fst (graph m)"
  unfolding graph_def inj_on_def by force

subsection \<open>\<open>map_le\<close>\<close>

lemma map_le_empty [simp]: "empty \<subseteq>\<^sub>m g"
  by (simp add: map_le_def)

lemma upd_None_map_le [simp]: "f(x := None) \<subseteq>\<^sub>m f"
  by (force simp add: map_le_def)

lemma map_le_upd[simp]: "f \<subseteq>\<^sub>m g ==> f(a := b) \<subseteq>\<^sub>m g(a := b)"
  by (fastforce simp add: map_le_def)

lemma map_le_imp_upd_le [simp]: "m1 \<subseteq>\<^sub>m m2 \<Longrightarrow> m1(x := None) \<subseteq>\<^sub>m m2(x \<mapsto> y)"
  by (force simp add: map_le_def)

lemma map_le_upds [simp]:
  "f \<subseteq>\<^sub>m g \<Longrightarrow> f(as [\<mapsto>] bs) \<subseteq>\<^sub>m g(as [\<mapsto>] bs)"
proof (induct as arbitrary: f g bs)
  case (Cons a as)
  then show ?case
    by (cases bs) (use Cons in auto)
qed auto

lemma map_le_implies_dom_le: "(f \<subseteq>\<^sub>m g) \<Longrightarrow> (dom f \<subseteq> dom g)"
  by (fastforce simp add: map_le_def dom_def)

lemma map_le_refl [simp]: "f \<subseteq>\<^sub>m f"
  by (simp add: map_le_def)

lemma map_le_trans[trans]: "\<lbrakk> m1 \<subseteq>\<^sub>m m2; m2 \<subseteq>\<^sub>m m3\<rbrakk> \<Longrightarrow> m1 \<subseteq>\<^sub>m m3"
  by (auto simp add: map_le_def dom_def)

lemma map_le_antisym: "\<lbrakk> f \<subseteq>\<^sub>m g; g \<subseteq>\<^sub>m f \<rbrakk> \<Longrightarrow> f = g"
  unfolding map_le_def
  by (metis ext domIff)

lemma map_le_map_add [simp]: "f \<subseteq>\<^sub>m g ++ f"
  by (fastforce simp: map_le_def)

lemma map_le_iff_map_add_commute: "f \<subseteq>\<^sub>m f ++ g \<longleftrightarrow> f ++ g = g ++ f"
  by (fastforce simp: map_add_def map_le_def fun_eq_iff split: option.splits)

lemma map_add_le_mapE: "f ++ g \<subseteq>\<^sub>m h \<Longrightarrow> g \<subseteq>\<^sub>m h"
  by (fastforce simp: map_le_def map_add_def dom_def)

lemma map_add_le_mapI: "\<lbrakk> f \<subseteq>\<^sub>m h; g \<subseteq>\<^sub>m h \<rbrakk> \<Longrightarrow> f ++ g \<subseteq>\<^sub>m h"
  by (auto simp: map_le_def map_add_def dom_def split: option.splits)

lemma map_add_subsumed1: "f \<subseteq>\<^sub>m g \<Longrightarrow> f++g = g"
by (simp add: map_add_le_mapI map_le_antisym)

lemma map_add_subsumed2: "f \<subseteq>\<^sub>m g \<Longrightarrow> g++f = g"
by (metis map_add_subsumed1 map_le_iff_map_add_commute)

lemma dom_eq_singleton_conv: "dom f = {x} \<longleftrightarrow> (\<exists>v. f = [x \<mapsto> v])"
  (is "?lhs \<longleftrightarrow> ?rhs")
proof
  assume ?rhs
  then show ?lhs by (auto split: if_split_asm)
next
  assume ?lhs
  then obtain v where v: "f x = Some v" by auto
  show ?rhs
  proof
    show "f = [x \<mapsto> v]"
    proof (rule map_le_antisym)
      show "[x \<mapsto> v] \<subseteq>\<^sub>m f"
        using v by (auto simp add: map_le_def)
      show "f \<subseteq>\<^sub>m [x \<mapsto> v]"
        using \<open>dom f = {x}\<close> \<open>f x = Some v\<close> by (auto simp add: map_le_def)
    qed
  qed
qed

lemma map_add_eq_empty_iff[simp]:
  "(f++g = empty) \<longleftrightarrow> f = empty \<and> g = empty"
by (metis map_add_None)

lemma empty_eq_map_add_iff[simp]:
  "(empty = f++g) \<longleftrightarrow> f = empty \<and> g = empty"
by(subst map_add_eq_empty_iff[symmetric])(rule eq_commute)

subsection \<open>Various\<close>

lemma set_map_of_compr:
  assumes distinct: "distinct (map fst xs)"
  shows "set xs = {(k, v). map_of xs k = Some v}"
  using assms
proof (induct xs)
  case Nil
  then show ?case by simp
next
  case (Cons x xs)
  obtain k v where "x = (k, v)" by (cases x) blast
  with Cons.prems have "k \<notin> dom (map_of xs)"
    by (simp add: dom_map_of_conv_image_fst)
  then have *: "insert (k, v) {(k, v). map_of xs k = Some v} =
    {(k', v'). ((map_of xs)(k \<mapsto> v)) k' = Some v'}"
    by (auto split: if_splits)
  from Cons have "set xs = {(k, v). map_of xs k = Some v}" by simp
  with * \<open>x = (k, v)\<close> show ?case by simp
qed

lemma eq_key_imp_eq_value:
  "v1 = v2"
  if "distinct (map fst xs)" "(k, v1) \<in> set xs" "(k, v2) \<in> set xs"
proof -
  from that have "inj_on fst (set xs)"
    by (simp add: distinct_map)
  moreover have "fst (k, v1) = fst (k, v2)"
    by simp
  ultimately have "(k, v1) = (k, v2)"
    by (rule inj_onD) (fact that)+
  then show ?thesis
    by simp
qed

lemma map_of_inject_set:
  assumes distinct: "distinct (map fst xs)" "distinct (map fst ys)"
  shows "map_of xs = map_of ys \<longleftrightarrow> set xs = set ys" (is "?lhs \<longleftrightarrow> ?rhs")
proof
  assume ?lhs
  moreover from \<open>distinct (map fst xs)\<close> have "set xs = {(k, v). map_of xs k = Some v}"
    by (rule set_map_of_compr)
  moreover from \<open>distinct (map fst ys)\<close> have "set ys = {(k, v). map_of ys k = Some v}"
    by (rule set_map_of_compr)
  ultimately show ?rhs by simp
next
  assume ?rhs show ?lhs
  proof
    fix k
    show "map_of xs k = map_of ys k"
    proof (cases "map_of xs k")
      case None
      with \<open>?rhs\<close> have "map_of ys k = None"
        by (simp add: map_of_eq_None_iff)
      with None show ?thesis by simp
    next
      case (Some v)
      with distinct \<open>?rhs\<close> have "map_of ys k = Some v"
        by simp
      with Some show ?thesis by simp
    qed
  qed
qed

lemma finite_Map_induct[consumes 1, case_names empty update]:
  assumes "finite (dom m)"
  assumes "P Map.empty"
  assumes "\<And>k v m. finite (dom m) \<Longrightarrow> k \<notin> dom m \<Longrightarrow> P m \<Longrightarrow> P (m(k \<mapsto> v))"
  shows "P m"
  using assms(1)
proof(induction "dom m" arbitrary: m rule: finite_induct)
  case empty
  then show ?case using assms(2) unfolding dom_def by simp
next
  case (insert x F)
  then have "finite (dom (m(x:=None)))" "x \<notin> dom (m(x:=None))" "P (m(x:=None))"
    by (metis Diff_insert_absorb dom_fun_upd)+
  with assms(3)[OF this] show ?case
    by (metis fun_upd_triv fun_upd_upd option.exhaust)
qed

hide_const (open) Map.empty Map.graph

end


(*  Title:      HOL/HOLCF/Cont.thy
    Author:     Franz Regensburger
    Author:     Brian Huffman
*)

section \<open>Continuity and monotonicity\<close>

theory Cont
imports Pcpo
begin

text \<open>
   Now we change the default class! Form now on all untyped type variables are
   of default class po
\<close>

default_sort po

subsection \<open>Definitions\<close>

definition
  monofun :: "('a \<Rightarrow> 'b) \<Rightarrow> bool"  \<comment> "monotonicity"  where
  "monofun f = (\<forall>x y. x \<sqsubseteq> y \<longrightarrow> f x \<sqsubseteq> f y)"

definition
  cont :: "('a::cpo \<Rightarrow> 'b::cpo) \<Rightarrow> bool"
where
  "cont f = (\<forall>Y. chain Y \<longrightarrow> range (\<lambda>i. f (Y i)) <<| f (\<Squnion>i. Y i))"

lemma contI:
  "\<lbrakk>\<And>Y. chain Y \<Longrightarrow> range (\<lambda>i. f (Y i)) <<| f (\<Squnion>i. Y i)\<rbrakk> \<Longrightarrow> cont f"
by (simp add: cont_def)

lemma contE:
  "\<lbrakk>cont f; chain Y\<rbrakk> \<Longrightarrow> range (\<lambda>i. f (Y i)) <<| f (\<Squnion>i. Y i)"
by (simp add: cont_def)

lemma monofunI:
  "\<lbrakk>\<And>x y. x \<sqsubseteq> y \<Longrightarrow> f x \<sqsubseteq> f y\<rbrakk> \<Longrightarrow> monofun f"
by (simp add: monofun_def)

lemma monofunE:
  "\<lbrakk>monofun f; x \<sqsubseteq> y\<rbrakk> \<Longrightarrow> f x \<sqsubseteq> f y"
by (simp add: monofun_def)

subsection \<open>Equivalence of alternate definition\<close>

text \<open>monotone functions map chains to chains\<close>

lemma ch2ch_monofun: "\<lbrakk>monofun f; chain Y\<rbrakk> \<Longrightarrow> chain (\<lambda>i. f (Y i))"
apply (rule chainI)
apply (erule monofunE)
apply (erule chainE)
done

text \<open>monotone functions map upper bound to upper bounds\<close>

lemma ub2ub_monofun:
  "\<lbrakk>monofun f; range Y <| u\<rbrakk> \<Longrightarrow> range (\<lambda>i. f (Y i)) <| f u"
apply (rule ub_rangeI)
apply (erule monofunE)
apply (erule ub_rangeD)
done

text \<open>a lemma about binary chains\<close>

lemma binchain_cont:
  "\<lbrakk>cont f; x \<sqsubseteq> y\<rbrakk> \<Longrightarrow> range (\<lambda>i::nat. f (if i = 0 then x else y)) <<| f y"
apply (subgoal_tac "f (\<Squnion>i::nat. if i = 0 then x else y) = f y")
apply (erule subst)
apply (erule contE)
apply (erule bin_chain)
apply (rule_tac f=f in arg_cong)
apply (erule is_lub_bin_chain [THEN lub_eqI])
done

text \<open>continuity implies monotonicity\<close>

lemma cont2mono: "cont f \<Longrightarrow> monofun f"
apply (rule monofunI)
apply (drule (1) binchain_cont)
apply (drule_tac i=0 in is_lub_rangeD1)
apply simp
done

lemmas cont2monofunE = cont2mono [THEN monofunE]

lemmas ch2ch_cont = cont2mono [THEN ch2ch_monofun]

text \<open>continuity implies preservation of lubs\<close>

lemma cont2contlubE:
  "\<lbrakk>cont f; chain Y\<rbrakk> \<Longrightarrow> f (\<Squnion>i. Y i) = (\<Squnion>i. f (Y i))"
apply (rule lub_eqI [symmetric])
apply (erule (1) contE)
done

lemma contI2:
  fixes f :: "'a::cpo \<Rightarrow> 'b::cpo"
  assumes mono: "monofun f"
  assumes below: "\<And>Y. \<lbrakk>chain Y; chain (\<lambda>i. f (Y i))\<rbrakk>
     \<Longrightarrow> f (\<Squnion>i. Y i) \<sqsubseteq> (\<Squnion>i. f (Y i))"
  shows "cont f"
proof (rule contI)
  fix Y :: "nat \<Rightarrow> 'a"
  assume Y: "chain Y"
  with mono have fY: "chain (\<lambda>i. f (Y i))"
    by (rule ch2ch_monofun)
  have "(\<Squnion>i. f (Y i)) = f (\<Squnion>i. Y i)"
    apply (rule below_antisym)
    apply (rule lub_below [OF fY])
    apply (rule monofunE [OF mono])
    apply (rule is_ub_thelub [OF Y])
    apply (rule below [OF Y fY])
    done
  with fY show "range (\<lambda>i. f (Y i)) <<| f (\<Squnion>i. Y i)"
    by (rule thelubE)
qed

subsection \<open>Collection of continuity rules\<close>

named_theorems cont2cont "continuity intro rule"

subsection \<open>Continuity of basic functions\<close>

text \<open>The identity function is continuous\<close>

lemma cont_id [simp, cont2cont]: "cont (\<lambda>x. x)"
apply (rule contI)
apply (erule cpo_lubI)
done

text \<open>constant functions are continuous\<close>

lemma cont_const [simp, cont2cont]: "cont (\<lambda>x. c)"
  using is_lub_const by (rule contI)

text \<open>application of functions is continuous\<close>

lemma cont_apply:
  fixes f :: "'a::cpo \<Rightarrow> 'b::cpo \<Rightarrow> 'c::cpo" and t :: "'a \<Rightarrow> 'b"
  assumes 1: "cont (\<lambda>x. t x)"
  assumes 2: "\<And>x. cont (\<lambda>y. f x y)"
  assumes 3: "\<And>y. cont (\<lambda>x. f x y)"
  shows "cont (\<lambda>x. (f x) (t x))"
proof (rule contI2 [OF monofunI])
  fix x y :: "'a" assume "x \<sqsubseteq> y"
  then show "f x (t x) \<sqsubseteq> f y (t y)"
    by (auto intro: cont2monofunE [OF 1]
                    cont2monofunE [OF 2]
                    cont2monofunE [OF 3]
                    below_trans)
next
  fix Y :: "nat \<Rightarrow> 'a" assume "chain Y"
  then show "f (\<Squnion>i. Y i) (t (\<Squnion>i. Y i)) \<sqsubseteq> (\<Squnion>i. f (Y i) (t (Y i)))"
    by (simp only: cont2contlubE [OF 1] ch2ch_cont [OF 1]
                   cont2contlubE [OF 2] ch2ch_cont [OF 2]
                   cont2contlubE [OF 3] ch2ch_cont [OF 3]
                   diag_lub below_refl)
qed

lemma cont_compose:
  "\<lbrakk>cont c; cont (\<lambda>x. f x)\<rbrakk> \<Longrightarrow> cont (\<lambda>x. c (f x))"
by (rule cont_apply [OF _ _ cont_const])

text \<open>Least upper bounds preserve continuity\<close>

lemma cont2cont_lub [simp]:
  assumes chain: "\<And>x. chain (\<lambda>i. F i x)" and cont: "\<And>i. cont (\<lambda>x. F i x)"
  shows "cont (\<lambda>x. \<Squnion>i. F i x)"
apply (rule contI2)
apply (simp add: monofunI cont2monofunE [OF cont] lub_mono chain)
apply (simp add: cont2contlubE [OF cont])
apply (simp add: diag_lub ch2ch_cont [OF cont] chain)
done

text \<open>if-then-else is continuous\<close>

lemma cont_if [simp, cont2cont]:
  "\<lbrakk>cont f; cont g\<rbrakk> \<Longrightarrow> cont (\<lambda>x. if b then f x else g x)"
by (induct b) simp_all

subsection \<open>Finite chains and flat pcpos\<close>

text \<open>Monotone functions map finite chains to finite chains.\<close>

lemma monofun_finch2finch:
  "\<lbrakk>monofun f; finite_chain Y\<rbrakk> \<Longrightarrow> finite_chain (\<lambda>n. f (Y n))"
apply (unfold finite_chain_def)
apply (simp add: ch2ch_monofun)
apply (force simp add: max_in_chain_def)
done

text \<open>The same holds for continuous functions.\<close>

lemma cont_finch2finch:
  "\<lbrakk>cont f; finite_chain Y\<rbrakk> \<Longrightarrow> finite_chain (\<lambda>n. f (Y n))"
by (rule cont2mono [THEN monofun_finch2finch])

text \<open>All monotone functions with chain-finite domain are continuous.\<close>

lemma chfindom_monofun2cont: "monofun f \<Longrightarrow> cont (f::'a::chfin \<Rightarrow> 'b::cpo)"
apply (erule contI2)
apply (frule chfin2finch)
apply (clarsimp simp add: finite_chain_def)
apply (subgoal_tac "max_in_chain i (\<lambda>i. f (Y i))")
apply (simp add: maxinch_is_thelub ch2ch_monofun)
apply (force simp add: max_in_chain_def)
done

text \<open>All strict functions with flat domain are continuous.\<close>

lemma flatdom_strict2mono: "f \<bottom> = \<bottom> \<Longrightarrow> monofun (f::'a::flat \<Rightarrow> 'b::pcpo)"
apply (rule monofunI)
apply (drule ax_flat)
apply auto
done

lemma flatdom_strict2cont: "f \<bottom> = \<bottom> \<Longrightarrow> cont (f::'a::flat \<Rightarrow> 'b::pcpo)"
by (rule flatdom_strict2mono [THEN chfindom_monofun2cont])

text \<open>All functions with discrete domain are continuous.\<close>

lemma cont_discrete_cpo [simp, cont2cont]: "cont (f::'a::discrete_cpo \<Rightarrow> 'b::cpo)"
apply (rule contI)
apply (drule discrete_chain_const, clarify)
apply (simp add: is_lub_const)
done

end