Theory Lazy_List_Limsup

(*  Title:       Limsup of Lazy Lists
    Author:      Asta Halkjær From <ahfrom at dtu.dk>, 2021

    Modelled on Ordered_Resolution_Prover.Lazy_List_Liminf
*)

section ‹Limsup of Lazy Lists›

theory Lazy_List_Limsup
  imports Coinductive.Coinductive_List
begin

subsection ‹Library›

lemma less_llength_ltake: "i < llength (ltake k Xs) ⟷ i < k ∧ i < llength Xs"
  by simp

subsection ‹Infimum›

definition Inf_llist :: ‹'a set llist ⇒ 'a set› where
  ‹Inf_llist Xs = (⋂i ∈ {i. enat i < llength Xs}. lnth Xs i)›

lemma Inf_llist_subset_lnth: ‹enat i < llength Xs ⟹ Inf_llist Xs ⊆ lnth Xs i›
  unfolding Inf_llist_def by fast

lemma Inf_llist_imp_exists_index:
  assumes ‹¬ lnull Xs›
  shows ‹x ∈ Inf_llist Xs ⟹ ∃i. enat i < llength Xs ∧ x ∈ lnth Xs i›
  unfolding Inf_llist_def using assms
  by (metis INT_E i0_less llength_eq_0 mem_Collect_eq zero_enat_def)

lemma Inf_llist_imp_all_index: ‹x ∈ Inf_llist Xs ⟹ ∀i. enat i < llength Xs ⟶ x ∈ lnth Xs i›
  unfolding Inf_llist_def by blast

lemma all_index_imp_Inf_llist: ‹∀i. enat i < llength Xs ⟶ x ∈ lnth Xs i ⟹ x ∈ Inf_llist Xs›
  unfolding Inf_llist_def by auto

lemma Inf_llist_LNil[simp]: ‹Inf_llist LNil = UNIV›
  unfolding Inf_llist_def by auto

lemma Inf_llist_LCons[simp]: ‹Inf_llist (LCons X Xs) = X ∩ Inf_llist Xs›
  unfolding Inf_llist_def
proof (intro subset_antisym subsetI)
  fix x
  assume ‹x ∈ (⋂i ∈ {i. enat i < llength (LCons X Xs)}. lnth (LCons X Xs) i)›
  then have ‹enat i < llength (LCons X Xs) ⟶ x ∈ lnth (LCons X Xs) i› for i
    by blast
  then have ‹x ∈ X ∧ (∀i. enat i < llength Xs ⟶ x ∈ lnth Xs i)›
    by (metis Suc_ile_eq iless_Suc_eq llength_LCons lnth_0 lnth_Suc_LCons zero_enat_def zero_le)
  then show ‹x ∈ X ∩ (⋂i ∈ {i. enat i < llength Xs}. lnth Xs i)›
    by blast
next
  fix x
  assume ‹x ∈ X ∩ ⋂ (lnth Xs ` {i. enat i < llength Xs})›
  then have ‹x ∈ X ∧ (∀i. enat i < llength Xs ⟶ x ∈ lnth Xs i)›
    by simp
  then have ‹∀i. enat i ≤ llength Xs ⟶ x ∈ lnth (LCons X Xs) i›
    by (metis Suc_diff_1 Suc_ile_eq lnth_0 lnth_Suc_LCons neq0_conv)
  then show ‹x ∈ ⋂ (lnth (LCons X Xs) ` {i. enat i < llength (LCons X Xs)})›
    by simp
qed

lemma lhd_subset_Inf_llist: ‹¬ lnull Xs ⟹ Inf_llist Xs ⊆ lhd Xs›
  by (cases Xs) simp_all

subsection ‹Infimum up-to›

definition Inf_upto_llist :: ‹'a set llist ⇒ enat ⇒ 'a set› where
  ‹Inf_upto_llist Xs j = (⋂i ∈ {i. enat i < llength Xs ∧ enat i ≤ j}. lnth Xs i)›

lemma Inf_upto_llist_eq_Inf_llist_ltake: ‹Inf_upto_llist Xs j = Inf_llist (ltake (eSuc j) Xs)›
  unfolding Inf_upto_llist_def Inf_llist_def
  by (smt (verit, best) Collect_cong Sup.SUP_cong iless_Suc_eq less_llength_ltake lnth_ltake
      mem_Collect_eq)

lemma Inf_upto_llist_enat_0[simp]:
  ‹Inf_upto_llist Xs (enat 0) = (if lnull Xs then UNIV else lhd Xs)›
  unfolding Inf_upto_llist_def image_def
  by (cases ‹lnull Xs›) (auto simp: lhd_conv_lnth enat_0 enat_0_iff)

lemma Inf_upto_llist_Suc[simp]:
  ‹Inf_upto_llist Xs (enat (Suc j)) =
   Inf_upto_llist Xs (enat j) ∩ (if enat (Suc j) < llength Xs then lnth Xs (Suc j) else UNIV)›
  unfolding Inf_upto_llist_def image_def by (auto intro: le_SucI elim: le_SucE)

lemma Inf_upto_llist_infinity[simp]: ‹Inf_upto_llist Xs ∞ = Inf_llist Xs›
  unfolding Inf_upto_llist_def Inf_llist_def by simp

lemma Inf_upto_llist_0[simp]: ‹Inf_upto_llist Xs 0 = (if lnull Xs then UNIV else lhd Xs)›
  unfolding zero_enat_def by (rule Inf_upto_llist_enat_0)

lemma Inf_upto_llist_eSuc[simp]:
  ‹Inf_upto_llist Xs (eSuc j) =
   (case j of
      enat k ⇒ Inf_upto_llist Xs (enat (Suc k))
    | ∞ ⇒ Inf_llist Xs)›
  by (auto simp: eSuc_enat split: enat.split)

lemma Inf_upto_llist_anti[simp]: ‹j ≤ k ⟹ Inf_upto_llist Xs k ⊆ Inf_upto_llist Xs j›
  unfolding Inf_upto_llist_def by auto

lemma Inf_llist_subset_Inf_upto_llist: ‹Inf_llist Xs ⊆ Inf_upto_llist Xs j›
  unfolding Inf_llist_def Inf_upto_llist_def by auto

lemma elem_Inf_llist_imp_Inf_upto_llist:
  ‹x ∈ Inf_llist Xs ⟹ x ∈ Inf_upto_llist Xs (enat j)›
  unfolding Inf_llist_def Inf_upto_llist_def by blast

lemma Inf_upto_llist_subset_lnth: ‹j < llength Xs ⟹ Inf_upto_llist Xs j ⊆ lnth Xs j›
  unfolding Inf_upto_llist_def by auto

lemma Inf_llist_imp_Inf_upto_llist:
  assumes ‹X ⊆ Inf_llist Xs›
  shows ‹X ⊆ Inf_upto_llist Xs (enat k)›
  using assms elem_Inf_llist_imp_Inf_upto_llist by fast

subsection ‹Limsup›

definition Limsup_llist :: ‹'a set llist ⇒ 'a set› where
  ‹Limsup_llist Xs =
   (⋂i ∈ {i. enat i < llength Xs}. ⋃j ∈ {j. i ≤ j ∧ enat j < llength Xs}. lnth Xs j)›

lemma Limsup_llist_LNil[simp]: ‹Limsup_llist LNil = UNIV›
  unfolding Limsup_llist_def by simp

lemma Limsup_llist_LCons:
  ‹Limsup_llist (LCons X Xs) = (if lnull Xs then X else Limsup_llist Xs)› (is ‹?lhs = ?rhs›)
proof (cases "lnull Xs")
  case nnull: False
  show ?thesis
  proof
    {
      fix x
      assume ‹∀i. enat i ≤ llength Xs ⟶ (∃j ≥ i. enat j ≤ llength Xs ∧ x ∈ lnth (LCons X Xs) j)›
      then have ‹∀i. enat i < llength Xs ⟶ (∃j ≥ i. enat j < llength Xs ∧ x ∈ lnth Xs j)›
        by (metis Suc_ile_eq Suc_le_D Suc_le_mono lnth_Suc_LCons)
    }
    then show ‹?lhs ⊆ ?rhs›
      by (auto simp: Limsup_llist_def nnull)

    {
      fix x
      assume ‹∀i. enat i < llength Xs ⟶ (∃j ≥ i. enat j < llength Xs ∧ x ∈ lnth Xs j)›
      then have ‹∀i. enat i ≤ llength Xs ⟶
          (∃j ≥ i. enat j ≤ llength Xs ∧ x ∈ lnth (LCons X Xs) j)›
        by (metis Suc_ile_eq Suc_le_D iless_Suc_eq llength_LCons lnth_Suc_LCons nat_le_linear
            nnull not_less_eq_eq not_lnull_conv zero_enat_def zero_le)
    }
    then show "?rhs ⊆ ?lhs"
      by (auto simp: Limsup_llist_def nnull)
  qed
qed (simp add: Limsup_llist_def enat_0_iff(1))

lemma lfinite_Limsup_llist: ‹lfinite Xs ⟹ Limsup_llist Xs = (if lnull Xs then UNIV else llast Xs)›
proof (induction rule: lfinite_induct)
  case (LCons xs)
  then obtain y ys where xs: ‹xs = LCons y ys›
    by (meson not_lnull_conv)
  show ?case
    unfolding xs by (simp add: Limsup_llist_LCons LCons.IH[unfolded xs, simplified] llast_LCons)
qed (simp add: Limsup_llist_def)

lemma Limsup_llist_ltl: ‹¬ lnull (ltl Xs) ⟹ Limsup_llist Xs = Limsup_llist (ltl Xs)›
  by (metis Limsup_llist_LCons lhd_LCons_ltl lnull_ltlI)

lemma Inf_llist_subset_Limsup_llist: ‹Inf_llist Xs ⊆ Limsup_llist Xs›
  unfolding Limsup_llist_def Inf_llist_def by fast

lemma image_Limsup_llist_subset: ‹f ` Limsup_llist Ns ⊆ Limsup_llist (lmap ((`) f) Ns)›
  unfolding Limsup_llist_def by fastforce

lemma Limsup_llist_imp_exists_index:
  assumes ‹¬ lnull Xs›
  shows ‹x ∈ Limsup_llist Xs ⟹ ∃i. enat i < llength Xs ∧ x ∈ lnth Xs i›
  unfolding Limsup_llist_def using assms
  by simp (metis i0_less llength_eq_0 zero_enat_def)

lemma finite_subset_Limsup_llist_imp_exists_index:
  assumes
    nnil: ‹¬ lnull Xs› and
    fin: ‹finite X› and
    in_sup: ‹X ⊆ Limsup_llist Xs›
  shows ‹∀i. enat i < llength Xs ⟶ X ⊆ (⋃j ∈ {j. i ≤ j ∧ enat j < llength Xs}. lnth Xs j)›
  using assms unfolding Limsup_llist_def by blast

lemma Limsup_llist_lmap_image:
  assumes f_inj: ‹inj_on f (Inf_llist (lmap g xs))›
  shows ‹Limsup_llist (lmap (λx. f ` g x) xs) = f ` Limsup_llist (lmap g xs)› (is ‹?lhs = ?rhs›)
  oops

lemma Limsup_llist_lmap_union:
  assumes ‹∀x ∈ lset xs. ∀y ∈ lset xs. g x ∩ h y = {}›
  shows ‹Limsup_llist (lmap (λx. g x ∪ h x) xs) =
    Limsup_llist (lmap g xs) ∪ Limsup_llist (lmap h xs)› (is ‹?lhs = ?rhs›)
proof (intro equalityI subsetI)
  fix x
  assume x_in: ‹x ∈ ?lhs›
  then have ‹∀i. enat i < llength xs ⟶ (∃j ≥ i. enat j < llength xs ∧
      (x ∈ g (lnth xs j) ∨ x ∈ h (lnth xs j)))›
    unfolding Limsup_llist_def by auto
  then have ‹(∀i'. enat i' < llength xs ⟶ (∃j ≥ i'. enat j < llength xs ∧ x ∈ g (lnth xs j)))
     ∨ (∀i'. enat i' < llength xs ⟶ (∃j ≥ i'. enat j < llength xs ∧ x ∈ h (lnth xs j)))›
    using assms[unfolded disjoint_iff_not_equal] by (metis in_lset_conv_lnth)
  then show ‹x ∈ ?rhs›
    unfolding Limsup_llist_def by simp
next
  fix x
  show ‹x ∈ ?rhs ⟹ x ∈ ?lhs›
    using assms unfolding Limsup_llist_def by auto
qed

lemma Limsup_set_filter_commute:
  assumes ‹¬ lnull Xs›
  shows ‹Limsup_llist (lmap (λX. {x ∈ X. p x}) Xs) = {x ∈ Limsup_llist Xs. p x}› (is ‹?lhs = ?rhs›)
proof (intro equalityI subsetI)
  fix x
  assume ‹x ∈ ?lhs›
  then show ‹x ∈ ?rhs›
    unfolding Limsup_llist_def
    by simp (metis assms i0_less llength_eq_0 zero_enat_def)
qed (simp add: Limsup_llist_def)

subsection ‹Limsup up-to›

definition Limsup_upto_llist :: ‹'a set llist ⇒ enat ⇒ 'a set› where
  ‹Limsup_upto_llist Xs k =
   (⋂i ∈ {i. enat i < llength Xs ∧ enat i ≤ k}.
      ⋃j ∈ {j. i ≤ j ∧ enat j < llength Xs ∧ enat j ≤ k}. lnth Xs j)›

lemma Limsup_upto_llist_eq_Limsup_llist_ltake:
  ‹Limsup_upto_llist Xs j = Limsup_llist (ltake (eSuc j) Xs)›
  unfolding Limsup_upto_llist_def Limsup_llist_def
  by (smt (z3) Collect_cong Sup.SUP_cong iless_Suc_eq lnth_ltake less_llength_ltake mem_Collect_eq)

lemma Limsup_upto_llist_enat[simp]:
  ‹Limsup_upto_llist Xs (enat k) =
   (if enat k < llength Xs then lnth Xs k else if lnull Xs then UNIV else llast Xs)›
proof (cases ‹enat k < llength Xs›)
  case True
  then show ?thesis
    unfolding Limsup_upto_llist_def by force
next
  case k_ge: False
  show ?thesis
proof (cases ‹lnull Xs›)
  case nil: True
  then show ?thesis
    unfolding Limsup_upto_llist_def by simp
next
  case nnil: False
  then obtain j where
    j: ‹eSuc (enat j) = llength Xs›
    using k_ge by (metis eSuc_enat_iff enat_ile le_less_linear lhd_LCons_ltl llength_LCons)

  have fin: ‹lfinite Xs›
    using k_ge not_lfinite_llength by fastforce
  have le_k: ‹enat i < llength Xs ∧ i ≤ k ⟷ enat i < llength Xs› for i
    using k_ge linear order_le_less_subst2 by fastforce
  have ‹Limsup_upto_llist Xs (enat k) = llast Xs›
    using j nnil lfinite_Limsup_llist[OF fin] nnil
    unfolding Limsup_upto_llist_def Limsup_llist_def using llast_conv_lnth[OF j[symmetric]]
    by (simp add: le_k)
  then show ?thesis
    using k_ge nnil by simp
  qed
qed

lemma Limsup_upto_llist_infinity[simp]: ‹Limsup_upto_llist Xs ∞ = Limsup_llist Xs›
  unfolding Limsup_upto_llist_def Limsup_llist_def by simp

lemma Limsup_upto_llist_0[simp]:
  ‹Limsup_upto_llist Xs 0 = (if lnull Xs then UNIV else lhd Xs)›
  unfolding Limsup_upto_llist_def image_def
  by (simp add: enat_0[symmetric]) (simp add: enat_0 lnth_0_conv_lhd)

lemma Limsup_upto_llist_eSuc[simp]:
  ‹Limsup_upto_llist Xs (eSuc j) =
   (case j of
      enat k ⇒ Limsup_upto_llist Xs (enat (Suc k))
    | ∞ ⇒ Limsup_llist Xs)›
  by (auto simp: eSuc_enat split: enat.split)

lemma Liminf_upto_llist_imp_elem_Limsup_llist:
  assumes ‹∃i < llength Xs. ∀j ≥ i. j < llength Xs ⟶ x ∈ Limsup_upto_llist Xs (enat j)›
  shows ‹x ∈ Limsup_llist Xs›
  using assms by (simp add: Limsup_llist_def Limsup_upto_llist_def)
    (metis dual_order.strict_implies_order enat_iless enat_less_imp_le enat_ord_simps(1) not_less)

end