(*  Title:      HOL/Library/Liminf_Limsup.thy
    Author:     Johannes Hölzl, TU München
    Author:     Manuel Eberl, TU München
*)

section \<open>Liminf and Limsup on conditionally complete lattices\<close>

theory Liminf_Limsup
imports Complex_Main
begin

lemma (in conditionally_complete_linorder) le_cSup_iff:
  assumes "A \<noteq> {}" "bdd_above A"
  shows "x \<le> Sup A \<longleftrightarrow> (\<forall>y<x. \<exists>a\<in>A. y < a)"
proof safe
  fix y assume "x \<le> Sup A" "y < x"
  then have "y < Sup A" by auto
  then show "\<exists>a\<in>A. y < a"
    unfolding less_cSup_iff[OF assms] .
qed (auto elim!: allE[of _ "Sup A"] simp add: not_le[symmetric] cSup_upper assms)

lemma (in conditionally_complete_linorder) le_cSUP_iff:
  "A \<noteq> {} \<Longrightarrow> bdd_above (f`A) \<Longrightarrow> x \<le> Sup (f ` A) \<longleftrightarrow> (\<forall>y<x. \<exists>i\<in>A. y < f i)"
  using le_cSup_iff [of "f ` A"] by simp

lemma le_cSup_iff_less:
  fixes x :: "'a :: {conditionally_complete_linorder, dense_linorder}"
  shows "A \<noteq> {} \<Longrightarrow> bdd_above (f`A) \<Longrightarrow> x \<le> (SUP i\<in>A. f i) \<longleftrightarrow> (\<forall>y<x. \<exists>i\<in>A. y \<le> f i)"
  by (simp add: le_cSUP_iff)
     (blast intro: less_imp_le less_trans less_le_trans dest: dense)

lemma le_Sup_iff_less:
  fixes x :: "'a :: {complete_linorder, dense_linorder}"
  shows "x \<le> (SUP i\<in>A. f i) \<longleftrightarrow> (\<forall>y<x. \<exists>i\<in>A. y \<le> f i)" (is "?lhs = ?rhs")
  unfolding le_SUP_iff
  by (blast intro: less_imp_le less_trans less_le_trans dest: dense)

lemma (in conditionally_complete_linorder) cInf_le_iff:
  assumes "A \<noteq> {}" "bdd_below A"
  shows "Inf A \<le> x \<longleftrightarrow> (\<forall>y>x. \<exists>a\<in>A. y > a)"
proof safe
  fix y assume "x \<ge> Inf A" "y > x"
  then have "y > Inf A" by auto
  then show "\<exists>a\<in>A. y > a"
    unfolding cInf_less_iff[OF assms] .
qed (auto elim!: allE[of _ "Inf A"] simp add: not_le[symmetric] cInf_lower assms)

lemma (in conditionally_complete_linorder) cINF_le_iff:
  "A \<noteq> {} \<Longrightarrow> bdd_below (f`A) \<Longrightarrow> Inf (f ` A) \<le> x \<longleftrightarrow> (\<forall>y>x. \<exists>i\<in>A. y > f i)"
  using cInf_le_iff [of "f ` A"] by simp

lemma cInf_le_iff_less:
  fixes x :: "'a :: {conditionally_complete_linorder, dense_linorder}"
  shows "A \<noteq> {} \<Longrightarrow> bdd_below (f`A) \<Longrightarrow> (INF i\<in>A. f i) \<le> x \<longleftrightarrow> (\<forall>y>x. \<exists>i\<in>A. f i \<le> y)"
  by (simp add: cINF_le_iff)
     (blast intro: less_imp_le less_trans le_less_trans dest: dense)

lemma Inf_le_iff_less:
  fixes x :: "'a :: {complete_linorder, dense_linorder}"
  shows "(INF i\<in>A. f i) \<le> x \<longleftrightarrow> (\<forall>y>x. \<exists>i\<in>A. f i \<le> y)"
  unfolding INF_le_iff
  by (blast intro: less_imp_le less_trans le_less_trans dest: dense)

lemma SUP_pair:
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: complete_lattice"
  shows "(SUP i \<in> A. SUP j \<in> B. f i j) = (SUP p \<in> A \<times> B. f (fst p) (snd p))"
  by (rule antisym) (auto intro!: SUP_least SUP_upper2)

lemma INF_pair:
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: complete_lattice"
  shows "(INF i \<in> A. INF j \<in> B. f i j) = (INF p \<in> A \<times> B. f (fst p) (snd p))"
  by (rule antisym) (auto intro!: INF_greatest INF_lower2)

lemma INF_Sigma:
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: complete_lattice"
  shows "(INF i \<in> A. INF j \<in> B i. f i j) = (INF p \<in> Sigma A B. f (fst p) (snd p))"
  by (rule antisym) (auto intro!: INF_greatest INF_lower2)

subsubsection \<open>\<open>Liminf\<close> and \<open>Limsup\<close>\<close>

definition Liminf :: "'a filter \<Rightarrow> ('a \<Rightarrow> 'b) \<Rightarrow> 'b :: complete_lattice" where
  "Liminf F f = (SUP P\<in>{P. eventually P F}. INF x\<in>{x. P x}. f x)"

definition Limsup :: "'a filter \<Rightarrow> ('a \<Rightarrow> 'b) \<Rightarrow> 'b :: complete_lattice" where
  "Limsup F f = (INF P\<in>{P. eventually P F}. SUP x\<in>{x. P x}. f x)"

abbreviation "liminf \<equiv> Liminf sequentially"

abbreviation "limsup \<equiv> Limsup sequentially"

lemma Liminf_eqI:
  "(\<And>P. eventually P F \<Longrightarrow> Inf (f ` (Collect P)) \<le> x) \<Longrightarrow>
    (\<And>y. (\<And>P. eventually P F \<Longrightarrow> Inf (f ` (Collect P)) \<le> y) \<Longrightarrow> x \<le> y) \<Longrightarrow> Liminf F f = x"
  unfolding Liminf_def by (auto intro!: SUP_eqI)

lemma Limsup_eqI:
  "(\<And>P. eventually P F \<Longrightarrow> x \<le> Sup (f ` (Collect P))) \<Longrightarrow>
    (\<And>y. (\<And>P. eventually P F \<Longrightarrow> y \<le> Sup (f ` (Collect P))) \<Longrightarrow> y \<le> x) \<Longrightarrow> Limsup F f = x"
  unfolding Limsup_def by (auto intro!: INF_eqI)

lemma liminf_SUP_INF: "liminf f = (SUP n. INF m\<in>{n..}. f m)"
  unfolding Liminf_def eventually_sequentially
  by (rule SUP_eq) (auto simp: atLeast_def intro!: INF_mono)

lemma limsup_INF_SUP: "limsup f = (INF n. SUP m\<in>{n..}. f m)"
  unfolding Limsup_def eventually_sequentially
  by (rule INF_eq) (auto simp: atLeast_def intro!: SUP_mono)

lemma mem_limsup_iff: "x \<in> limsup A \<longleftrightarrow> (\<exists>\<^sub>F n in sequentially. x \<in> A n)"
  by (simp add: Limsup_def) (metis (mono_tags) eventually_mono not_frequently)

lemma mem_liminf_iff: "x \<in> liminf A \<longleftrightarrow> (\<forall>\<^sub>F n in sequentially. x \<in> A n)"
  by (simp add: Liminf_def) (metis (mono_tags) eventually_mono)

lemma Limsup_const:
  assumes ntriv: "\<not> trivial_limit F"
  shows "Limsup F (\<lambda>x. c) = c"
proof -
  have *: "\<And>P. Ex P \<longleftrightarrow> P \<noteq> (\<lambda>x. False)" by auto
  have "\<And>P. eventually P F \<Longrightarrow> (SUP x \<in> {x. P x}. c) = c"
    using ntriv by (intro SUP_const) (auto simp: eventually_False *)
  then show ?thesis
    apply (auto simp add: Limsup_def)
    apply (rule INF_const)
    apply auto
    using eventually_True apply blast
    done
qed

lemma Liminf_const:
  assumes ntriv: "\<not> trivial_limit F"
  shows "Liminf F (\<lambda>x. c) = c"
proof -
  have *: "\<And>P. Ex P \<longleftrightarrow> P \<noteq> (\<lambda>x. False)" by auto
  have "\<And>P. eventually P F \<Longrightarrow> (INF x \<in> {x. P x}. c) = c"
    using ntriv by (intro INF_const) (auto simp: eventually_False *)
  then show ?thesis
    apply (auto simp add: Liminf_def)
    apply (rule SUP_const)
    apply auto
    using eventually_True apply blast
    done
qed

lemma Liminf_mono:
  assumes ev: "eventually (\<lambda>x. f x \<le> g x) F"
  shows "Liminf F f \<le> Liminf F g"
  unfolding Liminf_def
proof (safe intro!: SUP_mono)
  fix P assume "eventually P F"
  with ev have "eventually (\<lambda>x. f x \<le> g x \<and> P x) F" (is "eventually ?Q F") by (rule eventually_conj)
  then show "\<exists>Q\<in>{P. eventually P F}. Inf (f ` (Collect P)) \<le> Inf (g ` (Collect Q))"
    by (intro bexI[of _ ?Q]) (auto intro!: INF_mono)
qed

lemma Liminf_eq:
  assumes "eventually (\<lambda>x. f x = g x) F"
  shows "Liminf F f = Liminf F g"
  by (intro antisym Liminf_mono eventually_mono[OF assms]) auto

lemma Limsup_mono:
  assumes ev: "eventually (\<lambda>x. f x \<le> g x) F"
  shows "Limsup F f \<le> Limsup F g"
  unfolding Limsup_def
proof (safe intro!: INF_mono)
  fix P assume "eventually P F"
  with ev have "eventually (\<lambda>x. f x \<le> g x \<and> P x) F" (is "eventually ?Q F") by (rule eventually_conj)
  then show "\<exists>Q\<in>{P. eventually P F}. Sup (f ` (Collect Q)) \<le> Sup (g ` (Collect P))"
    by (intro bexI[of _ ?Q]) (auto intro!: SUP_mono)
qed

lemma Limsup_eq:
  assumes "eventually (\<lambda>x. f x = g x) net"
  shows "Limsup net f = Limsup net g"
  by (intro antisym Limsup_mono eventually_mono[OF assms]) auto

lemma Liminf_bot[simp]: "Liminf bot f = top"
  unfolding Liminf_def top_unique[symmetric]
  by (rule SUP_upper2[where i="\<lambda>x. False"]) simp_all

lemma Limsup_bot[simp]: "Limsup bot f = bot"
  unfolding Limsup_def bot_unique[symmetric]
  by (rule INF_lower2[where i="\<lambda>x. False"]) simp_all

lemma Liminf_le_Limsup:
  assumes ntriv: "\<not> trivial_limit F"
  shows "Liminf F f \<le> Limsup F f"
  unfolding Limsup_def Liminf_def
  apply (rule SUP_least)
  apply (rule INF_greatest)
proof safe
  fix P Q assume "eventually P F" "eventually Q F"
  then have "eventually (\<lambda>x. P x \<and> Q x) F" (is "eventually ?C F") by (rule eventually_conj)
  then have not_False: "(\<lambda>x. P x \<and> Q x) \<noteq> (\<lambda>x. False)"
    using ntriv by (auto simp add: eventually_False)
  have "Inf (f ` (Collect P)) \<le> Inf (f ` (Collect ?C))"
    by (rule INF_mono) auto
  also have "\<dots> \<le> Sup (f ` (Collect ?C))"
    using not_False by (intro INF_le_SUP) auto
  also have "\<dots> \<le> Sup (f ` (Collect Q))"
    by (rule SUP_mono) auto
  finally show "Inf (f ` (Collect P)) \<le> Sup (f ` (Collect Q))" .
qed

lemma Liminf_bounded:
  assumes le: "eventually (\<lambda>n. C \<le> X n) F"
  shows "C \<le> Liminf F X"
  using Liminf_mono[OF le] Liminf_const[of F C]
  by (cases "F = bot") simp_all

lemma Limsup_bounded:
  assumes le: "eventually (\<lambda>n. X n \<le> C) F"
  shows "Limsup F X \<le> C"
  using Limsup_mono[OF le] Limsup_const[of F C]
  by (cases "F = bot") simp_all

lemma le_Limsup:
  assumes F: "F \<noteq> bot" and x: "\<forall>\<^sub>F x in F. l \<le> f x"
  shows "l \<le> Limsup F f"
  using F Liminf_bounded[of l f F] Liminf_le_Limsup[of F f] order.trans x by blast

lemma Liminf_le:
  assumes F: "F \<noteq> bot" and x: "\<forall>\<^sub>F x in F. f x \<le> l"
  shows "Liminf F f \<le> l"
  using F Liminf_le_Limsup Limsup_bounded order.trans x by blast

lemma le_Liminf_iff:
  fixes X :: "_ \<Rightarrow> _ :: complete_linorder"
  shows "C \<le> Liminf F X \<longleftrightarrow> (\<forall>y<C. eventually (\<lambda>x. y < X x) F)"
proof -
  have "eventually (\<lambda>x. y < X x) F"
    if "eventually P F" "y < Inf (X ` (Collect P))" for y P
    using that by (auto elim!: eventually_mono dest: less_INF_D)
  moreover
  have "\<exists>P. eventually P F \<and> y < Inf (X ` (Collect P))"
    if "y < C" and y: "\<forall>y<C. eventually (\<lambda>x. y < X x) F" for y P
  proof (cases "\<exists>z. y < z \<and> z < C")
    case True
    then obtain z where z: "y < z \<and> z < C" ..
    moreover from z have "z \<le> Inf (X ` {x. z < X x})"
      by (auto intro!: INF_greatest)
    ultimately show ?thesis
      using y by (intro exI[of _ "\<lambda>x. z < X x"]) auto
  next
    case False
    then have "C \<le> Inf (X ` {x. y < X x})"
      by (intro INF_greatest) auto
    with \<open>y < C\<close> show ?thesis
      using y by (intro exI[of _ "\<lambda>x. y < X x"]) auto
  qed
  ultimately show ?thesis
    unfolding Liminf_def le_SUP_iff by auto
qed

lemma Limsup_le_iff:
  fixes X :: "_ \<Rightarrow> _ :: complete_linorder"
  shows "C \<ge> Limsup F X \<longleftrightarrow> (\<forall>y>C. eventually (\<lambda>x. y > X x) F)"
proof -
  { fix y P assume "eventually P F" "y > Sup (X ` (Collect P))"
    then have "eventually (\<lambda>x. y > X x) F"
      by (auto elim!: eventually_mono dest: SUP_lessD) }
  moreover
  { fix y P assume "y > C" and y: "\<forall>y>C. eventually (\<lambda>x. y > X x) F"
    have "\<exists>P. eventually P F \<and> y > Sup (X ` (Collect P))"
    proof (cases "\<exists>z. C < z \<and> z < y")
      case True
      then obtain z where z: "C < z \<and> z < y" ..
      moreover from z have "z \<ge> Sup (X ` {x. X x < z})"
        by (auto intro!: SUP_least)
      ultimately show ?thesis
        using y by (intro exI[of _ "\<lambda>x. z > X x"]) auto
    next
      case False
      then have "C \<ge> Sup (X ` {x. X x < y})"
        by (intro SUP_least) (auto simp: not_less)
      with \<open>y > C\<close> show ?thesis
        using y by (intro exI[of _ "\<lambda>x. y > X x"]) auto
    qed }
  ultimately show ?thesis
    unfolding Limsup_def INF_le_iff by auto
qed

lemma less_LiminfD:
  "y < Liminf F (f :: _ \<Rightarrow> 'a :: complete_linorder) \<Longrightarrow> eventually (\<lambda>x. f x > y) F"
  using le_Liminf_iff[of "Liminf F f" F f] by simp

lemma Limsup_lessD:
  "y > Limsup F (f :: _ \<Rightarrow> 'a :: complete_linorder) \<Longrightarrow> eventually (\<lambda>x. f x < y) F"
  using Limsup_le_iff[of F f "Limsup F f"] by simp

lemma lim_imp_Liminf:
  fixes f :: "'a \<Rightarrow> _ :: {complete_linorder,linorder_topology}"
  assumes ntriv: "\<not> trivial_limit F"
  assumes lim: "(f \<longlongrightarrow> f0) F"
  shows "Liminf F f = f0"
proof (intro Liminf_eqI)
  fix P assume P: "eventually P F"
  then have "eventually (\<lambda>x. Inf (f ` (Collect P)) \<le> f x) F"
    by eventually_elim (auto intro!: INF_lower)
  then show "Inf (f ` (Collect P)) \<le> f0"
    by (rule tendsto_le[OF ntriv lim tendsto_const])
next
  fix y assume upper: "\<And>P. eventually P F \<Longrightarrow> Inf (f ` (Collect P)) \<le> y"
  show "f0 \<le> y"
  proof cases
    assume "\<exists>z. y < z \<and> z < f0"
    then obtain z where "y < z \<and> z < f0" ..
    moreover have "z \<le> Inf (f ` {x. z < f x})"
      by (rule INF_greatest) simp
    ultimately show ?thesis
      using lim[THEN topological_tendstoD, THEN upper, of "{z <..}"] by auto
  next
    assume discrete: "\<not> (\<exists>z. y < z \<and> z < f0)"
    show ?thesis
    proof (rule classical)
      assume "\<not> f0 \<le> y"
      then have "eventually (\<lambda>x. y < f x) F"
        using lim[THEN topological_tendstoD, of "{y <..}"] by auto
      then have "eventually (\<lambda>x. f0 \<le> f x) F"
        using discrete by (auto elim!: eventually_mono)
      then have "Inf (f ` {x. f0 \<le> f x}) \<le> y"
        by (rule upper)
      moreover have "f0 \<le> Inf (f ` {x. f0 \<le> f x})"
        by (intro INF_greatest) simp
      ultimately show "f0 \<le> y" by simp
    qed
  qed
qed

lemma lim_imp_Limsup:
  fixes f :: "'a \<Rightarrow> _ :: {complete_linorder,linorder_topology}"
  assumes ntriv: "\<not> trivial_limit F"
  assumes lim: "(f \<longlongrightarrow> f0) F"
  shows "Limsup F f = f0"
proof (intro Limsup_eqI)
  fix P assume P: "eventually P F"
  then have "eventually (\<lambda>x. f x \<le> Sup (f ` (Collect P))) F"
    by eventually_elim (auto intro!: SUP_upper)
  then show "f0 \<le> Sup (f ` (Collect P))"
    by (rule tendsto_le[OF ntriv tendsto_const lim])
next
  fix y assume lower: "\<And>P. eventually P F \<Longrightarrow> y \<le> Sup (f ` (Collect P))"
  show "y \<le> f0"
  proof (cases "\<exists>z. f0 < z \<and> z < y")
    case True
    then obtain z where "f0 < z \<and> z < y" ..
    moreover have "Sup (f ` {x. f x < z}) \<le> z"
      by (rule SUP_least) simp
    ultimately show ?thesis
      using lim[THEN topological_tendstoD, THEN lower, of "{..< z}"] by auto
  next
    case False
    show ?thesis
    proof (rule classical)
      assume "\<not> y \<le> f0"
      then have "eventually (\<lambda>x. f x < y) F"
        using lim[THEN topological_tendstoD, of "{..< y}"] by auto
      then have "eventually (\<lambda>x. f x \<le> f0) F"
        using False by (auto elim!: eventually_mono simp: not_less)
      then have "y \<le> Sup (f ` {x. f x \<le> f0})"
        by (rule lower)
      moreover have "Sup (f ` {x. f x \<le> f0}) \<le> f0"
        by (intro SUP_least) simp
      ultimately show "y \<le> f0" by simp
    qed
  qed
qed

lemma Liminf_eq_Limsup:
  fixes f0 :: "'a :: {complete_linorder,linorder_topology}"
  assumes ntriv: "\<not> trivial_limit F"
    and lim: "Liminf F f = f0" "Limsup F f = f0"
  shows "(f \<longlongrightarrow> f0) F"
proof (rule order_tendstoI)
  fix a assume "f0 < a"
  with assms have "Limsup F f < a" by simp
  then obtain P where "eventually P F" "Sup (f ` (Collect P)) < a"
    unfolding Limsup_def INF_less_iff by auto
  then show "eventually (\<lambda>x. f x < a) F"
    by (auto elim!: eventually_mono dest: SUP_lessD)
next
  fix a assume "a < f0"
  with assms have "a < Liminf F f" by simp
  then obtain P where "eventually P F" "a < Inf (f ` (Collect P))"
    unfolding Liminf_def less_SUP_iff by auto
  then show "eventually (\<lambda>x. a < f x) F"
    by (auto elim!: eventually_mono dest: less_INF_D)
qed

lemma tendsto_iff_Liminf_eq_Limsup:
  fixes f0 :: "'a :: {complete_linorder,linorder_topology}"
  shows "\<not> trivial_limit F \<Longrightarrow> (f \<longlongrightarrow> f0) F \<longleftrightarrow> (Liminf F f = f0 \<and> Limsup F f = f0)"
  by (metis Liminf_eq_Limsup lim_imp_Limsup lim_imp_Liminf)

lemma liminf_subseq_mono:
  fixes X :: "nat \<Rightarrow> 'a :: complete_linorder"
  assumes "strict_mono r"
  shows "liminf X \<le> liminf (X \<circ> r) "
proof-
  have "\<And>n. (INF m\<in>{n..}. X m) \<le> (INF m\<in>{n..}. (X \<circ> r) m)"
  proof (safe intro!: INF_mono)
    fix n m :: nat assume "n \<le> m" then show "\<exists>ma\<in>{n..}. X ma \<le> (X \<circ> r) m"
      using seq_suble[OF \<open>strict_mono r\<close>, of m] by (intro bexI[of _ "r m"]) auto
  qed
  then show ?thesis by (auto intro!: SUP_mono simp: liminf_SUP_INF comp_def)
qed

lemma limsup_subseq_mono:
  fixes X :: "nat \<Rightarrow> 'a :: complete_linorder"
  assumes "strict_mono r"
  shows "limsup (X \<circ> r) \<le> limsup X"
proof-
  have "(SUP m\<in>{n..}. (X \<circ> r) m) \<le> (SUP m\<in>{n..}. X m)" for n
  proof (safe intro!: SUP_mono)
    fix m :: nat
    assume "n \<le> m"
    then show "\<exists>ma\<in>{n..}. (X \<circ> r) m \<le> X ma"
      using seq_suble[OF \<open>strict_mono r\<close>, of m] by (intro bexI[of _ "r m"]) auto
  qed
  then show ?thesis
    by (auto intro!: INF_mono simp: limsup_INF_SUP comp_def)
qed

lemma continuous_on_imp_continuous_within:
  "continuous_on s f \<Longrightarrow> t \<subseteq> s \<Longrightarrow> x \<in> s \<Longrightarrow> continuous (at x within t) f"
  unfolding continuous_on_eq_continuous_within
  by (auto simp: continuous_within intro: tendsto_within_subset)

lemma Liminf_compose_continuous_mono:
  fixes f :: "'a::{complete_linorder, linorder_topology} \<Rightarrow> 'b::{complete_linorder, linorder_topology}"
  assumes c: "continuous_on UNIV f" and am: "mono f" and F: "F \<noteq> bot"
  shows "Liminf F (\<lambda>n. f (g n)) = f (Liminf F g)"
proof -
  { fix P assume "eventually P F"
    have "\<exists>x. P x"
    proof (rule ccontr)
      assume "\<not> (\<exists>x. P x)" then have "P = (\<lambda>x. False)"
        by auto
      with \<open>eventually P F\<close> F show False
        by auto
    qed }
  note * = this

  have "f (SUP P\<in>{P. eventually P F}. Inf (g ` Collect P)) =
    Sup (f ` (\<lambda>P. Inf (g ` Collect P)) ` {P. eventually P F})"
    using am continuous_on_imp_continuous_within [OF c]
    by (rule continuous_at_Sup_mono) (auto intro: eventually_True)
  then have "f (Liminf F g) = (SUP P \<in> {P. eventually P F}. f (Inf (g ` Collect P)))"
    by (simp add: Liminf_def image_comp)
  also have "\<dots> = (SUP P \<in> {P. eventually P F}. Inf (f ` (g ` Collect P)))"
    using * continuous_at_Inf_mono [OF am continuous_on_imp_continuous_within [OF c]]
    by auto
  finally show ?thesis by (auto simp: Liminf_def image_comp)
qed

lemma Limsup_compose_continuous_mono:
  fixes f :: "'a::{complete_linorder, linorder_topology} \<Rightarrow> 'b::{complete_linorder, linorder_topology}"
  assumes c: "continuous_on UNIV f" and am: "mono f" and F: "F \<noteq> bot"
  shows "Limsup F (\<lambda>n. f (g n)) = f (Limsup F g)"
proof -
  { fix P assume "eventually P F"
    have "\<exists>x. P x"
    proof (rule ccontr)
      assume "\<not> (\<exists>x. P x)" then have "P = (\<lambda>x. False)"
        by auto
      with \<open>eventually P F\<close> F show False
        by auto
    qed }
  note * = this

  have "f (INF P\<in>{P. eventually P F}. Sup (g ` Collect P)) =
    Inf (f ` (\<lambda>P. Sup (g ` Collect P)) ` {P. eventually P F})"
    using am continuous_on_imp_continuous_within [OF c]
    by (rule continuous_at_Inf_mono) (auto intro: eventually_True)
  then have "f (Limsup F g) = (INF P \<in> {P. eventually P F}. f (Sup (g ` Collect P)))"
    by (simp add: Limsup_def image_comp)
  also have "\<dots> = (INF P \<in> {P. eventually P F}. Sup (f ` (g ` Collect P)))"
    using * continuous_at_Sup_mono [OF am continuous_on_imp_continuous_within [OF c]]
    by auto
  finally show ?thesis by (auto simp: Limsup_def image_comp)
qed

lemma Liminf_compose_continuous_antimono:
  fixes f :: "'a::{complete_linorder,linorder_topology} \<Rightarrow> 'b::{complete_linorder,linorder_topology}"
  assumes c: "continuous_on UNIV f"
    and am: "antimono f"
    and F: "F \<noteq> bot"
  shows "Liminf F (\<lambda>n. f (g n)) = f (Limsup F g)"
proof -
  have *: "\<exists>x. P x" if "eventually P F" for P
  proof (rule ccontr)
    assume "\<not> (\<exists>x. P x)" then have "P = (\<lambda>x. False)"
      by auto
    with \<open>eventually P F\<close> F show False
      by auto
  qed

  have "f (INF P\<in>{P. eventually P F}. Sup (g ` Collect P)) =
    Sup (f ` (\<lambda>P. Sup (g ` Collect P)) ` {P. eventually P F})"
    using am continuous_on_imp_continuous_within [OF c]
    by (rule continuous_at_Inf_antimono) (auto intro: eventually_True)
  then have "f (Limsup F g) = (SUP P \<in> {P. eventually P F}. f (Sup (g ` Collect P)))"
    by (simp add: Limsup_def image_comp)
  also have "\<dots> = (SUP P \<in> {P. eventually P F}. Inf (f ` (g ` Collect P)))"
    using * continuous_at_Sup_antimono [OF am continuous_on_imp_continuous_within [OF c]]
    by auto
  finally show ?thesis
    by (auto simp: Liminf_def image_comp)
qed

lemma Limsup_compose_continuous_antimono:
  fixes f :: "'a::{complete_linorder, linorder_topology} \<Rightarrow> 'b::{complete_linorder, linorder_topology}"
  assumes c: "continuous_on UNIV f" and am: "antimono f" and F: "F \<noteq> bot"
  shows "Limsup F (\<lambda>n. f (g n)) = f (Liminf F g)"
proof -
  { fix P assume "eventually P F"
    have "\<exists>x. P x"
    proof (rule ccontr)
      assume "\<not> (\<exists>x. P x)" then have "P = (\<lambda>x. False)"
        by auto
      with \<open>eventually P F\<close> F show False
        by auto
    qed }
  note * = this

  have "f (SUP P\<in>{P. eventually P F}. Inf (g ` Collect P)) =
    Inf (f ` (\<lambda>P. Inf (g ` Collect P)) ` {P. eventually P F})"
    using am continuous_on_imp_continuous_within [OF c]
    by (rule continuous_at_Sup_antimono) (auto intro: eventually_True)
  then have "f (Liminf F g) = (INF P \<in> {P. eventually P F}. f (Inf (g ` Collect P)))"
    by (simp add: Liminf_def image_comp)
  also have "\<dots> = (INF P \<in> {P. eventually P F}. Sup (f ` (g ` Collect P)))"
    using * continuous_at_Inf_antimono [OF am continuous_on_imp_continuous_within [OF c]]
    by auto
  finally show ?thesis
    by (auto simp: Limsup_def image_comp)
qed

lemma Liminf_filtermap_le: "Liminf (filtermap f F) g \<le> Liminf F (\<lambda>x. g (f x))"
  apply (cases "F = bot", simp)
  by (subst Liminf_def)
    (auto simp add: INF_lower Liminf_bounded eventually_filtermap eventually_mono intro!: SUP_least)

lemma Limsup_filtermap_ge: "Limsup (filtermap f F) g \<ge> Limsup F (\<lambda>x. g (f x))"
  apply (cases "F = bot", simp)
  by (subst Limsup_def)
    (auto simp add: SUP_upper Limsup_bounded eventually_filtermap eventually_mono intro!: INF_greatest)

lemma Liminf_least: "(\<And>P. eventually P F \<Longrightarrow> (INF x\<in>Collect P. f x) \<le> x) \<Longrightarrow> Liminf F f \<le> x"
  by (auto intro!: SUP_least simp: Liminf_def)

lemma Limsup_greatest: "(\<And>P. eventually P F \<Longrightarrow> x \<le> (SUP x\<in>Collect P. f x)) \<Longrightarrow> Limsup F f \<ge> x"
  by (auto intro!: INF_greatest simp: Limsup_def)

lemma Liminf_filtermap_ge: "inj f \<Longrightarrow> Liminf (filtermap f F) g \<ge> Liminf F (\<lambda>x. g (f x))"
  apply (cases "F = bot", simp)
  apply (rule Liminf_least)
  subgoal for P
    by (auto simp: eventually_filtermap the_inv_f_f
        intro!: Liminf_bounded INF_lower2 eventually_mono[of P])
  done

lemma Limsup_filtermap_le: "inj f \<Longrightarrow> Limsup (filtermap f F) g \<le> Limsup F (\<lambda>x. g (f x))"
  apply (cases "F = bot", simp)
  apply (rule Limsup_greatest)
  subgoal for P
    by (auto simp: eventually_filtermap the_inv_f_f
        intro!: Limsup_bounded SUP_upper2 eventually_mono[of P])
  done

lemma Liminf_filtermap_eq: "inj f \<Longrightarrow> Liminf (filtermap f F) g = Liminf F (\<lambda>x. g (f x))"
  using Liminf_filtermap_le[of f F g] Liminf_filtermap_ge[of f F g]
  by simp

lemma Limsup_filtermap_eq: "inj f \<Longrightarrow> Limsup (filtermap f F) g = Limsup F (\<lambda>x. g (f x))"
  using Limsup_filtermap_le[of f F g] Limsup_filtermap_ge[of F g f]
  by simp

subsection \<open>More Limits\<close>

lemma convergent_limsup_cl:
  fixes X :: "nat \<Rightarrow> 'a::{complete_linorder,linorder_topology}"
  shows "convergent X \<Longrightarrow> limsup X = lim X"
  by (auto simp: convergent_def limI lim_imp_Limsup)

lemma convergent_liminf_cl:
  fixes X :: "nat \<Rightarrow> 'a::{complete_linorder,linorder_topology}"
  shows "convergent X \<Longrightarrow> liminf X = lim X"
  by (auto simp: convergent_def limI lim_imp_Liminf)

lemma lim_increasing_cl:
  assumes "\<And>n m. n \<ge> m \<Longrightarrow> f n \<ge> f m"
  obtains l where "f \<longlonglongrightarrow> (l::'a::{complete_linorder,linorder_topology})"
proof
  show "f \<longlonglongrightarrow> (SUP n. f n)"
    using assms
    by (intro increasing_tendsto)
       (auto simp: SUP_upper eventually_sequentially less_SUP_iff intro: less_le_trans)
qed

lemma lim_decreasing_cl:
  assumes "\<And>n m. n \<ge> m \<Longrightarrow> f n \<le> f m"
  obtains l where "f \<longlonglongrightarrow> (l::'a::{complete_linorder,linorder_topology})"
proof
  show "f \<longlonglongrightarrow> (INF n. f n)"
    using assms
    by (intro decreasing_tendsto)
       (auto simp: INF_lower eventually_sequentially INF_less_iff intro: le_less_trans)
qed

lemma compact_complete_linorder:
  fixes X :: "nat \<Rightarrow> 'a::{complete_linorder,linorder_topology}"
  shows "\<exists>l r. strict_mono r \<and> (X \<circ> r) \<longlonglongrightarrow> l"
proof -
  obtain r where "strict_mono r" and mono: "monoseq (X \<circ> r)"
    using seq_monosub[of X]
    unfolding comp_def
    by auto
  then have "(\<forall>n m. m \<le> n \<longrightarrow> (X \<circ> r) m \<le> (X \<circ> r) n) \<or> (\<forall>n m. m \<le> n \<longrightarrow> (X \<circ> r) n \<le> (X \<circ> r) m)"
    by (auto simp add: monoseq_def)
  then obtain l where "(X \<circ> r) \<longlonglongrightarrow> l"
     using lim_increasing_cl[of "X \<circ> r"] lim_decreasing_cl[of "X \<circ> r"]
     by auto
  then show ?thesis
    using \<open>strict_mono r\<close> by auto
qed

lemma tendsto_Limsup:
  fixes f :: "_ \<Rightarrow> 'a :: {complete_linorder,linorder_topology}"
  shows "F \<noteq> bot \<Longrightarrow> Limsup F f = Liminf F f \<Longrightarrow> (f \<longlongrightarrow> Limsup F f) F"
  by (subst tendsto_iff_Liminf_eq_Limsup) auto

lemma tendsto_Liminf:
  fixes f :: "_ \<Rightarrow> 'a :: {complete_linorder,linorder_topology}"
  shows "F \<noteq> bot \<Longrightarrow> Limsup F f = Liminf F f \<Longrightarrow> (f \<longlongrightarrow> Liminf F f) F"
  by (subst tendsto_iff_Liminf_eq_Limsup) auto

end


(*
Author:  Christian Sternagel <c.sternagel@gmail.com>
License: LGPL
*)

theory Sorted_Wrt
  imports Main
begin

lemma sorted_wrt_filter:
  "sorted_wrt P xs \<Longrightarrow> sorted_wrt P (filter Q xs)"
  by (induct xs) (auto)

lemma sorted_wrt_map_mono:
  assumes "sorted_wrt Q xs"
    and "\<And>x y. Q x y \<Longrightarrow> P (f x) (f y)"
  shows "sorted_wrt P (map f xs)"
  using assms by (induct xs) (auto)

lemma sorted_wrt_concat_map_map:
  assumes "sorted_wrt Q xs"
    and "sorted_wrt Q ys"
    and "\<And>a x y. Q x y \<Longrightarrow> P (f x a) (f y a)"
    and "\<And>x y u v. x \<in> set xs \<Longrightarrow> y \<in> set xs \<Longrightarrow> Q u v \<Longrightarrow> P (f x u) (f y v)"
  shows "sorted_wrt P [f x y . y \<leftarrow> ys, x \<leftarrow> xs]"
  using assms by (induct ys)
    (auto simp: sorted_wrt_append intro: sorted_wrt_map_mono [of Q])

lemma sorted_wrt_concat_map:
  assumes "sorted_wrt P (map h xs)"
    and "\<And>x. x \<in> set xs \<Longrightarrow> sorted_wrt P (map h (f x))"
    and "\<And>x y u v. P (h x) (h y) \<Longrightarrow> x \<in> set xs \<Longrightarrow> y \<in> set xs \<Longrightarrow> u \<in> set (f x) \<Longrightarrow> v \<in> set (f y) \<Longrightarrow> P (h u) (h v)"
  shows "sorted_wrt P (concat (map (map h \<circ> f) xs))"
  using assms by (induct xs) (auto simp: sorted_wrt_append)

lemma sorted_wrt_map_distr:
  assumes "sorted_wrt (\<lambda>x y. P x y) (map f xs)"
  shows "sorted_wrt (\<lambda>x y. P (f x) (f y)) xs"
  using assms
  by (induct xs) (auto)

lemma sorted_wrt_tl:
  "xs \<noteq> [] \<Longrightarrow> sorted_wrt P xs \<Longrightarrow> sorted_wrt P (tl xs)"
  by (cases xs) (auto)

end


section "Kruskal on UGraphs"

theory UGraph_Impl
imports
 Kruskal_Impl UGraph
begin

definition "\<alpha> = (\<lambda>(u,w,v). Upair u v)"

subsection "Interpreting \<open>Kruskl_Impl\<close> with a UGraph"

abbreviation (in uGraph)
  "getEdges_SPEC csuper_E
    \<equiv> (SPEC (\<lambda>L. distinct (map \<alpha> L) \<and> \<alpha> ` set L = E
                \<and> (\<forall>(a, wv, b)\<in>set L. w (\<alpha> (a, wv, b)) = wv) \<and> set L \<subseteq> csuper_E))"

locale uGraph_impl = uGraph E w for E :: "nat uprod set" and w :: "nat uprod \<Rightarrow> int" +
  fixes getEdges_impl :: "(nat \<times> int \<times> nat) list Heap" and csuper_E :: "(nat \<times> int \<times> nat) set"
  assumes getEdges_impl:
    "(uncurry0 getEdges_impl, uncurry0 (getEdges_SPEC csuper_E))
       \<in> unit_assn\<^sup>k \<rightarrow>\<^sub>a list_assn (nat_assn \<times>\<^sub>a int_assn \<times>\<^sub>a nat_assn)"
begin

  abbreviation "V \<equiv> \<Union> (set_uprod ` E)"

  lemma max_node_is_Max_V: " E = \<alpha> ` set la \<Longrightarrow>  max_node la = Max (insert 0 V)"
  proof -
    assume E: "E = \<alpha> ` set la"
    have *: "fst ` set la \<union> (snd \<circ> snd) ` set la = (\<Union>x\<in>set la. case x of (x1, x1a, x2a) \<Rightarrow> {x1, x2a})"
      by auto force
    show ?thesis
    unfolding E using *
    by (auto simp add: \<alpha>_def  max_node_def prod.case_distrib)
  qed

sublocale s: Kruskal_Impl E "\<Union>(set_uprod ` E)" set_uprod "\<lambda>u v e. Upair u v = e"
  "subforest" "uconnectedV"  w \<alpha> "PR_CONST (\<lambda>(u,w,v). RETURN (u,v))"
  "PR_CONST (getEdges_SPEC csuper_E)"
 getEdges_impl "csuper_E" " (\<lambda>(u,w,v). return (u,v))"
  unfolding subforest_def
proof (unfold_locales, goal_cases)
  show "finite E" by simp
next
  fix E'
  assume "forest E' \<and> E' \<subseteq> E"
  then show "E' \<subseteq> E" by auto
next
  show "forest {} \<and> {} \<subseteq> E" apply (auto simp: decycle_def forest_def)
    using epath.elims(2) by fastforce
next
  fix X Y
  assume "forest X \<and> X \<subseteq> E" "Y \<subseteq> X"
  then show "forest Y \<and> Y \<subseteq> E" using forest_mono by auto
next
  case (5 u v)
  then show ?case unfolding uconnected_def apply auto
    using epath.elims(2) by force
next
  case (6 E1 E2 u v)
  then have "(u, v) \<in> (uconnected E1)" and uv: "u \<in> V" "v \<in> V"
    by auto
  then obtain p where 1: "epath E1 u p v" unfolding uconnected_def by auto
  from 6 uv have 2: "\<not>(\<exists>p.  epath E2 u p v)" unfolding uconnected_def by auto
  from 1 2 have "\<exists>a b. (a, b) \<notin> uconnected E2
           \<and> Upair a b \<notin> E2 \<and> Upair a b \<in> E1" by(rule findaugmenting_edge)
  then show ?case by auto
next
  case (7 F e u v)
  note f = \<open>forest F \<and> F \<subseteq> E\<close>
  note notin = \<open>e \<in> E - F\<close> \<open>Upair u v = e\<close>
  from notin ecard2 have unv: "u\<noteq>v" by fastforce
  show "(forest (insert e F) \<and> insert e F \<subseteq> E) = ((u, v) \<notin> uconnectedV F)"
  proof
    assume a: "forest (insert e F) \<and> insert e F \<subseteq> E "
    have "(u, v) \<notin> uconnected F" apply(rule insert_stays_forest_means_not_connected)
      using notin a unv by auto
    then show "((u, v) \<notin> Restr (uconnected F) V)" by auto
  next
    assume a: "(u, v) \<notin> Restr (uconnected F) V"
    have "forest (insert (Upair u v) F)" apply(rule augment_forest_overedges[where E=E])
      using notin f a unv  by auto
    moreover have "insert e F \<subseteq> E"
      using notin f by auto
    ultimately show "forest (insert e F) \<and> insert e F \<subseteq> E" using notin by auto
  qed
next
  fix F
  assume "F\<subseteq>E"
  show "equiv V (uconnectedV F)" by(rule equiv_vert_uconnected)
next
  case (9 F)
  then show ?case  by auto
next
  case (10 x y F)
  then show ?case using insert_uconnectedV_per' by metis
next
  case (11 x)
  then show ?case apply(cases x) by auto
next
  case (12 u v e)
  then show ?case by auto
next
  case (13 u v e)
  then show ?case by auto
next
  case (14 a F e)
  then show ?case using ecard2 by force
next
  case (15 v)
  then show ?case using ecard2 by auto
next
  case 16
  show "V \<subseteq> V" by auto
next
  case 17
  show "finite V" by simp
next
  case (18 a b e T)
  then show ?case
    apply auto
    subgoal unfolding uconnected_def apply auto apply(rule exI[where x="[e]"]) apply simp
        using ecard2 by force
    subgoal by force
    subgoal by force
    done
next
  case (19 xi x)
  then show ?case by (auto split: prod.splits simp: \<alpha>_def)
next
  case 20
  show ?case by auto
next
  case 21
  show ?case using getEdges_impl by simp
next
  case (22 l)
  from max_node_is_Max_V[OF 22] show "max_node l = Max (insert 0 V)" .
next
  case (23)
  then show ?case
    apply sepref_to_hoare by sep_auto
qed

lemma spanningForest_eq_basis: "spanningForest = s.basis"
  unfolding spanningForest_def  s.basis_def by auto

lemma minSpanningForest_eq_minbasis: "minSpanningForest = s.minBasis"
  unfolding minSpanningForest_def  s.MSF_def spanningForest_eq_basis by auto

lemma kruskal_correct':
  "<emp> kruskal getEdges_impl (\<lambda>(u,w,v). return (u,v)) ()
    <\<lambda>r. \<up> (distinct r \<and> set r \<subseteq> csuper_E \<and> s.MSF (set (map \<alpha> r)))>\<^sub>t"
  using s.kruskal_correct_forest   by auto

lemma kruskal_correct:
  "<emp> kruskal getEdges_impl (\<lambda>(u,w,v). return (u,v)) ()
    <\<lambda>r. \<up> (distinct r \<and> set r \<subseteq> csuper_E \<and> minSpanningForest (set (map \<alpha> r)))>\<^sub>t"
  using s.kruskal_correct_forest minSpanningForest_eq_minbasis  by auto

end

subsection "Kruskal on UGraph from list of concrete edges"

definition "uGraph_from_list_\<alpha>_weight L e = (THE w. \<exists>a' b'. Upair a' b' = e \<and> (a', w, b') \<in> set L)"
abbreviation "uGraph_from_list_\<alpha>_edges L \<equiv> \<alpha> ` set L"

locale fromlist = fixes
  L :: "(nat \<times> int \<times> nat) list"
assumes dist: "distinct (map \<alpha> L)" and no_selfloop: "\<forall>u w v. (u,w,v)\<in>set L \<longrightarrow> u\<noteq>v"
begin

lemma not_distinct_map: "a\<in>set l \<Longrightarrow> b\<in>set l \<Longrightarrow> a\<noteq>b \<Longrightarrow> \<alpha> a = \<alpha> b \<Longrightarrow> \<not> distinct (map \<alpha> l)"

  by (meson distinct_map_eq)

lemma ii: "(a, aa, b) \<in> set L \<Longrightarrow> uGraph_from_list_\<alpha>_weight L (Upair a b) = aa"
  unfolding uGraph_from_list_\<alpha>_weight_def
  apply rule
  subgoal by auto
  apply clarify
  subgoal for w a' b'
    apply(auto)
    subgoal using distinct_map_eq[OF dist, of "(a, aa, b)" "(a, w, b)"]
      unfolding \<alpha>_def by auto
    subgoal using distinct_map_eq[OF dist, of "(a, aa, b)" "(a', w, b')"]
      unfolding \<alpha>_def by fastforce
    done
  done

sublocale uGraph_impl "\<alpha> ` set L" "uGraph_from_list_\<alpha>_weight L" "return L" "set L"
proof (unfold_locales)
  fix e assume *: "e \<in> \<alpha> ` set L"
  from * obtain u w v where "(u,w,v) \<in> set L" "e = \<alpha> (u, w, v)" by auto
  then show "proper_uprod e" using no_selfloop unfolding \<alpha>_def by auto
next
  show "finite (\<alpha> ` set L)" by auto
next
  show "(uncurry0 (return L),uncurry0((SPEC
          (\<lambda>La. distinct (map \<alpha> La) \<and> \<alpha> ` set La = \<alpha> ` set L
      \<and> (\<forall>(aa, wv, ba)\<in>set La. uGraph_from_list_\<alpha>_weight L (\<alpha> (aa, wv, ba)) = wv)
      \<and> set La \<subseteq> set L))))
    \<in> unit_assn\<^sup>k \<rightarrow>\<^sub>a list_assn (nat_assn \<times>\<^sub>a int_assn \<times>\<^sub>a nat_assn)"
    apply sepref_to_hoare using dist  apply sep_auto
    subgoal using ii unfolding \<alpha>_def by auto
    subgoal by simp
    subgoal by (auto simp: pure_fold list_assn_emp)
    done
qed

lemmas kruskal_correct = kruskal_correct

definition (in -) "kruskal_algo L = kruskal (return L) (\<lambda>(u,w,v). return (u,v)) ()"

end

subsection \<open>Outside the locale\<close>

definition uGraph_from_list_invar :: "(nat\<times>int\<times>nat) list \<Rightarrow> bool" where
  "uGraph_from_list_invar L = (distinct (map \<alpha> L) \<and> (\<forall>p\<in>set L. case p of (u,w,v) \<Rightarrow>u\<noteq>v))"

lemma uGraph_from_list_invar_conv: "uGraph_from_list_invar L = fromlist L"
  by(auto simp add: uGraph_from_list_invar_def fromlist_def)

lemma uGraph_from_list_invar_subset:
  "uGraph_from_list_invar L \<Longrightarrow> set L'\<subseteq> set L \<Longrightarrow> distinct L' \<Longrightarrow> uGraph_from_list_invar L'"
  unfolding uGraph_from_list_invar_def by (auto simp: distinct_map inj_on_subset)

lemma  uGraph_from_list_\<alpha>_inj_on: "uGraph_from_list_invar E \<Longrightarrow> inj_on \<alpha> (set E)"
  by(auto simp: distinct_map uGraph_from_list_invar_def  )

lemma sum_easier: "uGraph_from_list_invar L
    \<Longrightarrow> set E \<subseteq> set L
    \<Longrightarrow> sum (uGraph_from_list_\<alpha>_weight L) (uGraph_from_list_\<alpha>_edges E) = sum (\<lambda>(u,w,v). w) (set E)"
 proof -
  assume a: "uGraph_from_list_invar L"
  assume b: "set E \<subseteq> set L"

  have *: "\<And>e. e\<in>set E \<Longrightarrow>
   ((\<lambda>e. THE w. \<exists>a' b'. Upair a' b' = e \<and> (a', w, b') \<in> set L) \<circ> \<alpha>) e
       = (case e of (u, w, v) \<Rightarrow> w)"
    apply simp
    apply(rule the_equality)
    subgoal using b by(auto simp: \<alpha>_def split: prod.splits)
    subgoal using a b apply(auto simp: uGraph_from_list_invar_def distinct_map split: prod.splits)
      using \<alpha>_def
      by (smt \<alpha>_def inj_onD old.prod.case prod.inject set_mp)
    done

  have inj_on_E: "inj_on \<alpha> (set E)"
    apply(rule inj_on_subset)
    apply(rule uGraph_from_list_\<alpha>_inj_on) by fact+

  show ?thesis
    unfolding uGraph_from_list_\<alpha>_weight_def
    apply(subst sum.reindex[OF inj_on_E] )
    using * by auto
qed

lemma corr: "uGraph_from_list_invar L \<Longrightarrow>
  <emp> kruskal_algo L
     <\<lambda>F. \<up> (uGraph_from_list_invar F \<and> set F \<subseteq> set L \<and>
       uGraph.minSpanningForest (uGraph_from_list_\<alpha>_edges L)
         (uGraph_from_list_\<alpha>_weight L) (uGraph_from_list_\<alpha>_edges F))>\<^sub>t"
  apply(sep_auto heap: fromlist.kruskal_correct
          simp: uGraph_from_list_invar_conv kruskal_algo_def  )
  using uGraph_from_list_invar_subset uGraph_from_list_invar_conv by simp

lemma "uGraph_from_list_invar L \<Longrightarrow>
  <emp> kruskal_algo L
     <\<lambda>F. \<up> (uGraph_from_list_invar F \<and> set F \<subseteq> set L \<and>
       uGraph.spanningForest (uGraph_from_list_\<alpha>_edges L) (uGraph_from_list_\<alpha>_edges F)
      \<and> (\<forall>F'. uGraph.spanningForest (uGraph_from_list_\<alpha>_edges L) (uGraph_from_list_\<alpha>_edges F')
          \<longrightarrow> set F' \<subseteq> set L \<longrightarrow>  sum (\<lambda>(u,w,v). w) (set F) \<le> sum (\<lambda>(u,w,v). w) (set F')))>\<^sub>t"
proof -
  assume a: "uGraph_from_list_invar L"
  then interpret fromlist L apply unfold_locales by (auto simp: uGraph_from_list_invar_def)
  from a show ?thesis
    by(sep_auto heap: corr simp: minSpanningForest_def sum_easier)
qed

subsection \<open>Kruskal with input check\<close>

definition "kruskal' L = kruskal (return L)  (\<lambda>(u,w,v). return (u,v)) ()"

definition "kruskal_checked L = (if uGraph_from_list_invar L
                             then do { F \<leftarrow> kruskal' L; return (Some F) }
                             else return None)"

lemma "<emp> kruskal_checked L <\<lambda>
    Some F \<Rightarrow> \<up> (uGraph_from_list_invar L \<and> set F \<subseteq> set L
       \<and> uGraph.minSpanningForest (uGraph_from_list_\<alpha>_edges L) (uGraph_from_list_\<alpha>_weight L)
           (uGraph_from_list_\<alpha>_edges F))
  | None \<Rightarrow> \<up> (\<not> uGraph_from_list_invar L)>\<^sub>t"
  unfolding kruskal_checked_def
  apply(cases "uGraph_from_list_invar L") apply simp_all
  subgoal proof -
    assume [simp]: "uGraph_from_list_invar L"
    then interpret fromlist L apply unfold_locales by(auto simp: uGraph_from_list_invar_def)
    show ?thesis unfolding kruskal'_def by (sep_auto heap: kruskal_correct)
  qed
  subgoal by sep_auto
  done

subsection \<open>Code export\<close>

export_code uGraph_from_list_invar checking SML_imp
export_code kruskal_checked checking SML_imp

ML_val \<open>
  val export_nat = @{code integer_of_nat}
  val import_nat = @{code nat_of_integer}
  val export_int = @{code integer_of_int}
  val import_int = @{code int_of_integer}
  val import_list = map (fn (a,b,c) => (import_nat a, (import_int b, import_nat c)))
  val export_list = map (fn (a,(b,c)) => (export_nat a, export_int b, export_nat c))
  val export_Some_list = (fn SOME l => SOME (export_list l) | NONE => NONE)

  fun kruskal l = @{code kruskal} (fn () => import_list l) (fn (a,(_,c)) => fn () => (a,c)) () ()
                     |> export_list
  fun kruskal_checked l = @{code kruskal_checked} (import_list l) ()  |> export_Some_list

  val result = kruskal [(1,~9,2),(2,~3,3),(3,~4,1)]
  val result4 = kruskal [(1,~100,4), (3,64,5), (1,13,2), (3,20,2), (2,5,5), (4,80,3), (4,40,5)]

  val result' = kruskal_checked [(1,~9,2),(2,~3,3),(3,~4,1)]
  val result1' = kruskal_checked [(1,~9,2),(2,~3,3),(3,~4,1),(1,5,3)]
  val result2' = kruskal_checked [(1,~9,2),(2,~3,3),(3,~4,1),(3,~4,1)]
  val result3' = kruskal_checked [(1,~9,2),(2,~3,3),(3,~4,1),(1,~4,1)]
  val result4' = kruskal_checked [(1,~100,4), (3,64,5), (1,13,2), (3,20,2),
                                   (2,5,5), (4,80,3), (4,40,5)]
\<close>

end


theory Lists1_1
imports Main
begin

primrec snoc :: "'a list \<Rightarrow> 'a \<Rightarrow> 'a list"
where
"snoc [] y = [y]" |
"snoc (x # xs) y = (x # (snoc xs y))"

value "snoc [2,3] 1"

lemma snoc_append: "snoc xs x = xs @ [x]"
apply (induct_tac xs)
apply auto
done

lemma rev_cons: "rev (x # xs) = snoc (rev xs) x"
apply (induct_tac xs)
apply simp
apply (simp add:snoc_append)
done

end


(*  Title:      ZF/Induct/Acc.thy
    Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
    Copyright   1994  University of Cambridge
*)

section \<open>The accessible part of a relation\<close>

theory Acc imports ZF begin

text \<open>
  Inductive definition of \<open>acc(r)\<close>; see @{cite "paulin-tlca"}.
\<close>

consts
  acc :: "i => i"

inductive
  domains "acc(r)" \<subseteq> "field(r)"
  intros
    vimage:  "[| r-``{a}: Pow(acc(r)); a \<in> field(r) |] ==> a \<in> acc(r)"
  monos      Pow_mono

text \<open>
  The introduction rule must require @{prop "a \<in> field(r)"},
  otherwise \<open>acc(r)\<close> would be a proper class!

  \medskip
  The intended introduction rule:
\<close>

lemma accI: "[| !!b. <b,a>:r ==> b \<in> acc(r);  a \<in> field(r) |] ==> a \<in> acc(r)"
  by (blast intro: acc.intros)

lemma acc_downward: "[| b \<in> acc(r);  <a,b>: r |] ==> a \<in> acc(r)"
  by (erule acc.cases) blast

lemma acc_induct [consumes 1, case_names vimage, induct set: acc]:
    "[| a \<in> acc(r);
        !!x. [| x \<in> acc(r);  \<forall>y. <y,x>:r \<longrightarrow> P(y) |] ==> P(x)
     |] ==> P(a)"
  by (erule acc.induct) (blast intro: acc.intros)

lemma wf_on_acc: "wf[acc(r)](r)"
  apply (rule wf_onI2)
  apply (erule acc_induct)
  apply fast
  done

lemma acc_wfI: "field(r) \<subseteq> acc(r) \<Longrightarrow> wf(r)"
  by (erule wf_on_acc [THEN wf_on_subset_A, THEN wf_on_field_imp_wf])

lemma acc_wfD: "wf(r) ==> field(r) \<subseteq> acc(r)"
  apply (rule subsetI)
  apply (erule wf_induct2, assumption)
   apply (blast intro: accI)+
  done

lemma wf_acc_iff: "wf(r) \<longleftrightarrow> field(r) \<subseteq> acc(r)"
  by (rule iffI, erule acc_wfD, erule acc_wfI)

end


(*<*)
theory Examples
  imports Monitor_Impl
begin
(*>*)

section \<open>Examples\<close>

abbreviation "TT \<equiv> Formula.Eq (Formula.Const (EInt 0)) (Formula.Const (EInt 0))"
abbreviation "Eventually I \<psi> \<equiv> Formula.Until TT I \<psi>"

abbreviation "p \<equiv> STR ''p''"
abbreviation "A \<equiv> STR ''A''"
abbreviation "B \<equiv> STR ''B''"

definition \<phi>\<^sub>e\<^sub>x :: "ty Formula.formula" where
  "\<phi>\<^sub>e\<^sub>x = Formula.Let p (Eventually (interval 0 10) (Formula.Pred A [Formula.Var 0]))
  (Formula.Pred p [Formula.Var 0])"

definition \<phi>\<^sub>e\<^sub>x\<^sub>2 :: "ty Formula.formula" where
  "\<phi>\<^sub>e\<^sub>x\<^sub>2 = Formula.Let p (Formula.And (Eventually (interval 0 10) (Formula.Pred B [Formula.Var 0])) (Eventually (interval 0 10) (Formula.Pred A [Formula.Var 0])))
  (Formula.Pred p [Formula.Var 0])"

lemma "mmonitorable \<phi>\<^sub>e\<^sub>x" by eval
lemma "mmonitorable \<phi>\<^sub>e\<^sub>x\<^sub>2" by eval

definition "s0 = minit \<phi>\<^sub>e\<^sub>x"
definition "m1 = mstep (Mapping.update (A, 1) [{[Some (EInt 0)], [Some (EInt 1)]}] Mapping.empty, 1) s0"
definition "m2 = mstep (Mapping.empty, 2) (snd m1)"
definition "m3 = mstep (Mapping.update (A, 1) [{[Some (EInt 2)]}] Mapping.empty, 2) (snd m2)"
definition "m4 = mstep (Mapping.update (A, 1) [{[Some (EInt 5)]}] Mapping.empty, 12) (snd m3)"
definition "m5 = mstep (Mapping.update (A, 1) [{[Some (EInt 6)]}] Mapping.empty, 13) (snd m4)"

value s0
value m1
value m2
value m3
value m4
value m5

(*<*)
end
(*>*)