/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios
! This file was ported from Lean 3 source module order.bounded
! leanprover-community/mathlib commit aba57d4d3dae35460225919dcd82fe91355162f9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.RelClasses
import Mathlib.Data.Set.Intervals.Basic

/-!
# Bounded and unbounded sets
We prove miscellaneous lemmas about bounded and unbounded sets. Many of these are just variations on
the same ideas, or similar results with a few minor differences. The file is divided into these
different general ideas.
-/

namespace Set

variable {α : Type _} {r : α → α → Prop} {s t : Set α}

/-! ### Subsets of bounded and unbounded sets -/

theorem Bounded.mono (hst : s ⊆ t) (hs : Bounded r t) : Bounded r s :=
  hs.imp fun _ ha b hb => ha b (hst hb)
#align set.bounded.mono Set.Bounded.mono

theorem Unbounded.mono (hst : s ⊆ t) (hs : Unbounded r s) : Unbounded r t := fun a =>
  let ⟨b, hb, hb'⟩ := hs a
  ⟨b, hst hb, hb'⟩
#align set.unbounded.mono Set.Unbounded.mono

/-! ### Alternate characterizations of unboundedness on orders -/

theorem unbounded_le_of_forall_exists_lt [Preorder α] (h : ∀ a, ∃ b ∈ s, a < b) :
    Unbounded (· ≤ ·) s := fun a =>
  let ⟨b, hb, hb'⟩ := h a
  ⟨b, hb, fun hba => hba.not_lt hb'⟩
#align set.unbounded_le_of_forall_exists_lt Set.unbounded_le_of_forall_exists_lt

theorem unbounded_le_iff [LinearOrder α] : Unbounded (· ≤ ·) s ↔ ∀ a, ∃ b ∈ s, a < b := by
  simp only [Unbounded, not_le]
#align set.unbounded_le_iff Set.unbounded_le_iff

theorem unbounded_lt_of_forall_exists_le [Preorder α] (h : ∀ a, ∃ b ∈ s, a ≤ b) :
    Unbounded (· < ·) s := fun a =>
  let ⟨b, hb, hb'⟩ := h a
  ⟨b, hb, fun hba => hba.not_le hb'⟩
#align set.unbounded_lt_of_forall_exists_le Set.unbounded_lt_of_forall_exists_le

theorem unbounded_lt_iff [LinearOrder α] : Unbounded (· < ·) s ↔ ∀ a, ∃ b ∈ s, a ≤ b := by
  simp only [Unbounded, not_lt]
#align set.unbounded_lt_iff Set.unbounded_lt_iff

theorem unbounded_ge_of_forall_exists_gt [Preorder α] (h : ∀ a, ∃ b ∈ s, b < a) :
    Unbounded (· ≥ ·) s :=
  @unbounded_le_of_forall_exists_lt αᵒᵈ _ _ h
#align set.unbounded_ge_of_forall_exists_gt Set.unbounded_ge_of_forall_exists_gt

theorem unbounded_ge_iff [LinearOrder α] : Unbounded (· ≥ ·) s ↔ ∀ a, ∃ b ∈ s, b < a :=
  ⟨fun h a =>
    let ⟨b, hb, hba⟩ := h a
    ⟨b, hb, lt_of_not_ge hba⟩,
    unbounded_ge_of_forall_exists_gt⟩
#align set.unbounded_ge_iff Set.unbounded_ge_iff

theorem unbounded_gt_of_forall_exists_ge [Preorder α] (h : ∀ a, ∃ b ∈ s, b ≤ a) :
    Unbounded (· > ·) s := fun a =>
  let ⟨b, hb, hb'⟩ := h a
  ⟨b, hb, fun hba => not_le_of_gt hba hb'⟩
#align set.unbounded_gt_of_forall_exists_ge Set.unbounded_gt_of_forall_exists_ge

theorem unbounded_gt_iff [LinearOrder α] : Unbounded (· > ·) s ↔ ∀ a, ∃ b ∈ s, b ≤ a :=
  ⟨fun h a =>
    let ⟨b, hb, hba⟩ := h a
    ⟨b, hb, le_of_not_gt hba⟩,
    unbounded_gt_of_forall_exists_ge⟩
#align set.unbounded_gt_iff Set.unbounded_gt_iff

/-! ### Relation between boundedness by strict and nonstrict orders. -/

/-! #### Less and less or equal -/

theorem Bounded.rel_mono {r' : α → α → Prop} (h : Bounded r s) (hrr' : r ≤ r') : Bounded r' s :=
  let ⟨a, ha⟩ := h
  ⟨a, fun b hb => hrr' b a (ha b hb)⟩
#align set.bounded.rel_mono Set.Bounded.rel_mono

theorem bounded_le_of_bounded_lt [Preorder α] (h : Bounded (· < ·) s) : Bounded (· ≤ ·) s :=
  h.rel_mono fun _ _ => le_of_lt
#align set.bounded_le_of_bounded_lt Set.bounded_le_of_bounded_lt

theorem Unbounded.rel_mono {r' : α → α → Prop} (hr : r' ≤ r) (h : Unbounded r s) : Unbounded r' s :=
  fun a =>
  let ⟨b, hb, hba⟩ := h a
  ⟨b, hb, fun hba' => hba (hr b a hba')⟩
#align set.unbounded.rel_mono Set.Unbounded.rel_mono

theorem unbounded_lt_of_unbounded_le [Preorder α] (h : Unbounded (· ≤ ·) s) : Unbounded (· < ·) s :=
  h.rel_mono fun _ _ => le_of_lt
#align set.unbounded_lt_of_unbounded_le Set.unbounded_lt_of_unbounded_le

theorem bounded_le_iff_bounded_lt [Preorder α] [NoMaxOrder α] :
    Bounded (· ≤ ·) s ↔ Bounded (· < ·) s := by
  refine' ⟨fun h => _, bounded_le_of_bounded_lt⟩
  cases' h with a ha
  cases' exists_gt a with b hb
  exact ⟨b, fun c hc => lt_of_le_of_lt (ha c hc) hb⟩
#align set.bounded_le_iff_bounded_lt Set.bounded_le_iff_bounded_lt

theorem unbounded_lt_iff_unbounded_le [Preorder α] [NoMaxOrder α] :
    Unbounded (· < ·) s ↔ Unbounded (· ≤ ·) s := by
  simp_rw [← not_bounded_iff, bounded_le_iff_bounded_lt]
#align set.unbounded_lt_iff_unbounded_le Set.unbounded_lt_iff_unbounded_le

/-! #### Greater and greater or equal -/

theorem bounded_ge_of_bounded_gt [Preorder α] (h : Bounded (· > ·) s) : Bounded (· ≥ ·) s :=
  let ⟨a, ha⟩ := h
  ⟨a, fun b hb => le_of_lt (ha b hb)⟩
#align set.bounded_ge_of_bounded_gt Set.bounded_ge_of_bounded_gt

theorem unbounded_gt_of_unbounded_ge [Preorder α] (h : Unbounded (· ≥ ·) s) : Unbounded (· > ·) s :=
  fun a =>
  let ⟨b, hb, hba⟩ := h a
  ⟨b, hb, fun hba' => hba (le_of_lt hba')⟩
#align set.unbounded_gt_of_unbounded_ge Set.unbounded_gt_of_unbounded_ge

theorem bounded_ge_iff_bounded_gt [Preorder α] [NoMinOrder α] :
    Bounded (· ≥ ·) s ↔ Bounded (· > ·) s :=
  @bounded_le_iff_bounded_lt αᵒᵈ _ _ _
#align set.bounded_ge_iff_bounded_gt Set.bounded_ge_iff_bounded_gt

theorem unbounded_gt_iff_unbounded_ge [Preorder α] [NoMinOrder α] :
    Unbounded (· > ·) s ↔ Unbounded (· ≥ ·) s :=
  @unbounded_lt_iff_unbounded_le αᵒᵈ _ _ _
#align set.unbounded_gt_iff_unbounded_ge Set.unbounded_gt_iff_unbounded_ge

/-! ### The universal set -/

theorem unbounded_le_univ [LE α] [NoTopOrder α] : Unbounded (· ≤ ·) (@Set.univ α) := fun a =>
  let ⟨b, hb⟩ := exists_not_le a
  ⟨b, ⟨⟩, hb⟩
#align set.unbounded_le_univ Set.unbounded_le_univ

theorem unbounded_lt_univ [Preorder α] [NoTopOrder α] : Unbounded (· < ·) (@Set.univ α) :=
  unbounded_lt_of_unbounded_le unbounded_le_univ
#align set.unbounded_lt_univ Set.unbounded_lt_univ

theorem unbounded_ge_univ [LE α] [NoBotOrder α] : Unbounded (· ≥ ·) (@Set.univ α) := fun a =>
  let ⟨b, hb⟩ := exists_not_ge a
  ⟨b, ⟨⟩, hb⟩
#align set.unbounded_ge_univ Set.unbounded_ge_univ

theorem unbounded_gt_univ [Preorder α] [NoBotOrder α] : Unbounded (· > ·) (@Set.univ α) :=
  unbounded_gt_of_unbounded_ge unbounded_ge_univ
#align set.unbounded_gt_univ Set.unbounded_gt_univ

/-! ### Bounded and unbounded intervals -/

theorem bounded_self (a : α) : Bounded r { b | r b a } :=
  ⟨a, fun _ => id⟩
#align set.bounded_self Set.bounded_self

/-! #### Half-open bounded intervals -/

theorem bounded_lt_Iio [Preorder α] (a : α) : Bounded (· < ·) (Iio a) :=
  bounded_self a
#align set.bounded_lt_Iio Set.bounded_lt_Iio

theorem bounded_le_Iio [Preorder α] (a : α) : Bounded (· ≤ ·) (Iio a) :=
  bounded_le_of_bounded_lt (bounded_lt_Iio a)
#align set.bounded_le_Iio Set.bounded_le_Iio

theorem bounded_le_Iic [Preorder α] (a : α) : Bounded (· ≤ ·) (Iic a) :=
  bounded_self a
#align set.bounded_le_Iic Set.bounded_le_Iic

theorem bounded_lt_Iic [Preorder α] [NoMaxOrder α] (a : α) : Bounded (· < ·) (Iic a) := by
  simp only [← bounded_le_iff_bounded_lt, bounded_le_Iic]
#align set.bounded_lt_Iic Set.bounded_lt_Iic

theorem bounded_gt_Ioi [Preorder α] (a : α) : Bounded (· > ·) (Ioi a) :=
  bounded_self a
#align set.bounded_gt_Ioi Set.bounded_gt_Ioi

theorem bounded_ge_Ioi [Preorder α] (a : α) : Bounded (· ≥ ·) (Ioi a) :=
  bounded_ge_of_bounded_gt (bounded_gt_Ioi a)
#align set.bounded_ge_Ioi Set.bounded_ge_Ioi

theorem bounded_ge_Ici [Preorder α] (a : α) : Bounded (· ≥ ·) (Ici a) :=
  bounded_self a
#align set.bounded_ge_Ici Set.bounded_ge_Ici

theorem bounded_gt_Ici [Preorder α] [NoMinOrder α] (a : α) : Bounded (· > ·) (Ici a) := by
  simp only [← bounded_ge_iff_bounded_gt, bounded_ge_Ici]
#align set.bounded_gt_Ici Set.bounded_gt_Ici

/-! #### Other bounded intervals -/

theorem bounded_lt_Ioo [Preorder α] (a b : α) : Bounded (· < ·) (Ioo a b) :=
  (bounded_lt_Iio b).mono Set.Ioo_subset_Iio_self
#align set.bounded_lt_Ioo Set.bounded_lt_Ioo

theorem bounded_lt_Ico [Preorder α] (a b : α) : Bounded (· < ·) (Ico a b) :=
  (bounded_lt_Iio b).mono Set.Ico_subset_Iio_self
#align set.bounded_lt_Ico Set.bounded_lt_Ico

theorem bounded_lt_Ioc [Preorder α] [NoMaxOrder α] (a b : α) : Bounded (· < ·) (Ioc a b) :=
  (bounded_lt_Iic b).mono Set.Ioc_subset_Iic_self
#align set.bounded_lt_Ioc Set.bounded_lt_Ioc

theorem bounded_lt_Icc [Preorder α] [NoMaxOrder α] (a b : α) : Bounded (· < ·) (Icc a b) :=
  (bounded_lt_Iic b).mono Set.Icc_subset_Iic_self
#align set.bounded_lt_Icc Set.bounded_lt_Icc

theorem bounded_le_Ioo [Preorder α] (a b : α) : Bounded (· ≤ ·) (Ioo a b) :=
  (bounded_le_Iio b).mono Set.Ioo_subset_Iio_self
#align set.bounded_le_Ioo Set.bounded_le_Ioo

theorem bounded_le_Ico [Preorder α] (a b : α) : Bounded (· ≤ ·) (Ico a b) :=
  (bounded_le_Iio b).mono Set.Ico_subset_Iio_self
#align set.bounded_le_Ico Set.bounded_le_Ico

theorem bounded_le_Ioc [Preorder α] (a b : α) : Bounded (· ≤ ·) (Ioc a b) :=
  (bounded_le_Iic b).mono Set.Ioc_subset_Iic_self
#align set.bounded_le_Ioc Set.bounded_le_Ioc

theorem bounded_le_Icc [Preorder α] (a b : α) : Bounded (· ≤ ·) (Icc a b) :=
  (bounded_le_Iic b).mono Set.Icc_subset_Iic_self
#align set.bounded_le_Icc Set.bounded_le_Icc

theorem bounded_gt_Ioo [Preorder α] (a b : α) : Bounded (· > ·) (Ioo a b) :=
  (bounded_gt_Ioi a).mono Set.Ioo_subset_Ioi_self
#align set.bounded_gt_Ioo Set.bounded_gt_Ioo

theorem bounded_gt_Ico [Preorder α] [NoMinOrder α] (a b : α) : Bounded (· > ·) (Ico a b) :=
  (bounded_gt_Ici a).mono Set.Ico_subset_Ici_self
#align set.bounded_gt_Ico Set.bounded_gt_Ico

theorem bounded_gt_Icc [Preorder α] [NoMinOrder α] (a b : α) : Bounded (· > ·) (Icc a b) :=
  (bounded_gt_Ici a).mono Set.Icc_subset_Ici_self
#align set.bounded_gt_Icc Set.bounded_gt_Icc

theorem bounded_ge_Ioo [Preorder α] (a b : α) : Bounded (· ≥ ·) (Ioo a b) :=
  (bounded_ge_Ioi a).mono Set.Ioo_subset_Ioi_self
#align set.bounded_ge_Ioo Set.bounded_ge_Ioo

theorem bounded_ge_Ioc [Preorder α] (a b : α) : Bounded (· ≥ ·) (Ioc a b) :=
  (bounded_ge_Ioi a).mono Set.Ioc_subset_Ioi_self
#align set.bounded_ge_Ioc Set.bounded_ge_Ioc

theorem bounded_ge_Ico [Preorder α] (a b : α) : Bounded (· ≥ ·) (Ico a b) :=
  (bounded_ge_Ici a).mono Set.Ico_subset_Ici_self
#align set.bounded_ge_Ico Set.bounded_ge_Ico

theorem bounded_ge_Icc [Preorder α] (a b : α) : Bounded (· ≥ ·) (Icc a b) :=
  (bounded_ge_Ici a).mono Set.Icc_subset_Ici_self
#align set.bounded_ge_Icc Set.bounded_ge_Icc

/-! #### Unbounded intervals -/

theorem unbounded_le_Ioi [SemilatticeSup α] [NoMaxOrder α] (a : α) :
    Unbounded (· ≤ ·) (Ioi a) := fun b =>
  let ⟨c, hc⟩ := exists_gt (a ⊔ b)
  ⟨c, le_sup_left.trans_lt hc, (le_sup_right.trans_lt hc).not_le⟩
#align set.unbounded_le_Ioi Set.unbounded_le_Ioi

theorem unbounded_le_Ici [SemilatticeSup α] [NoMaxOrder α] (a : α) :
    Unbounded (· ≤ ·) (Ici a) :=
  (unbounded_le_Ioi a).mono Set.Ioi_subset_Ici_self
#align set.unbounded_le_Ici Set.unbounded_le_Ici

theorem unbounded_lt_Ioi [SemilatticeSup α] [NoMaxOrder α] (a : α) :
    Unbounded (· < ·) (Ioi a) :=
  unbounded_lt_of_unbounded_le (unbounded_le_Ioi a)
#align set.unbounded_lt_Ioi Set.unbounded_lt_Ioi

theorem unbounded_lt_Ici [SemilatticeSup α] (a : α) : Unbounded (· < ·) (Ici a) := fun b =>
  ⟨a ⊔ b, le_sup_left, le_sup_right.not_lt⟩
#align set.unbounded_lt_Ici Set.unbounded_lt_Ici

/-! ### Bounded initial segments -/

theorem bounded_inter_not (H : ∀ a b, ∃ m, ∀ c, r c a ∨ r c b → r c m) (a : α) :
    Bounded r (s ∩ { b | ¬r b a }) ↔ Bounded r s := by
  refine' ⟨_, Bounded.mono (Set.inter_subset_left s _)⟩
  rintro ⟨b, hb⟩
  cases' H a b with m hm
  exact ⟨m, fun c hc => hm c (or_iff_not_imp_left.2 fun hca => hb c ⟨hc, hca⟩)⟩
#align set.bounded_inter_not Set.bounded_inter_not

theorem unbounded_inter_not (H : ∀ a b, ∃ m, ∀ c, r c a ∨ r c b → r c m) (a : α) :
    Unbounded r (s ∩ { b | ¬r b a }) ↔ Unbounded r s := by
  simp_rw [← not_bounded_iff, bounded_inter_not H]
#align set.unbounded_inter_not Set.unbounded_inter_not

/-! #### Less or equal -/

theorem bounded_le_inter_not_le [SemilatticeSup α] (a : α) :
    Bounded (· ≤ ·) (s ∩ { b | ¬b ≤ a }) ↔ Bounded (· ≤ ·) s :=
  bounded_inter_not (fun x y => ⟨x ⊔ y, fun _ h => h.elim le_sup_of_le_left le_sup_of_le_right⟩) a
#align set.bounded_le_inter_not_le Set.bounded_le_inter_not_le

theorem unbounded_le_inter_not_le [SemilatticeSup α] (a : α) :
    Unbounded (· ≤ ·) (s ∩ { b | ¬b ≤ a }) ↔ Unbounded (· ≤ ·) s := by
  rw [← not_bounded_iff, ← not_bounded_iff, not_iff_not]
  exact bounded_le_inter_not_le a
#align set.unbounded_le_inter_not_le Set.unbounded_le_inter_not_le

theorem bounded_le_inter_lt [LinearOrder α] (a : α) :
    Bounded (· ≤ ·) (s ∩ { b | a < b }) ↔ Bounded (· ≤ ·) s := by
  simp_rw [← not_le, bounded_le_inter_not_le]
#align set.bounded_le_inter_lt Set.bounded_le_inter_lt

theorem unbounded_le_inter_lt [LinearOrder α] (a : α) :
    Unbounded (· ≤ ·) (s ∩ { b | a < b }) ↔ Unbounded (· ≤ ·) s := by
  convert @unbounded_le_inter_not_le _ s _ a
  exact lt_iff_not_le
#align set.unbounded_le_inter_lt Set.unbounded_le_inter_lt

theorem bounded_le_inter_le [LinearOrder α] (a : α) :
    Bounded (· ≤ ·) (s ∩ { b | a ≤ b }) ↔ Bounded (· ≤ ·) s := by
  refine' ⟨_, Bounded.mono (Set.inter_subset_left s _)⟩
  rw [← @bounded_le_inter_lt _ s _ a]
  exact Bounded.mono fun x ⟨hx, hx'⟩ => ⟨hx, le_of_lt hx'⟩
#align set.bounded_le_inter_le Set.bounded_le_inter_le

theorem unbounded_le_inter_le [LinearOrder α] (a : α) :
    Unbounded (· ≤ ·) (s ∩ { b | a ≤ b }) ↔ Unbounded (· ≤ ·) s := by
  rw [← not_bounded_iff, ← not_bounded_iff, not_iff_not]
  exact bounded_le_inter_le a
#align set.unbounded_le_inter_le Set.unbounded_le_inter_le

/-! #### Less than -/

theorem bounded_lt_inter_not_lt [SemilatticeSup α] (a : α) :
    Bounded (· < ·) (s ∩ { b | ¬b < a }) ↔ Bounded (· < ·) s :=
  bounded_inter_not (fun x y => ⟨x ⊔ y, fun _ h => h.elim lt_sup_of_lt_left lt_sup_of_lt_right⟩) a
#align set.bounded_lt_inter_not_lt Set.bounded_lt_inter_not_lt

theorem unbounded_lt_inter_not_lt [SemilatticeSup α] (a : α) :
    Unbounded (· < ·) (s ∩ { b | ¬b < a }) ↔ Unbounded (· < ·) s := by
  rw [← not_bounded_iff, ← not_bounded_iff, not_iff_not]
  exact bounded_lt_inter_not_lt a
#align set.unbounded_lt_inter_not_lt Set.unbounded_lt_inter_not_lt

theorem bounded_lt_inter_le [LinearOrder α] (a : α) :
    Bounded (· < ·) (s ∩ { b | a ≤ b }) ↔ Bounded (· < ·) s := by
  convert @bounded_lt_inter_not_lt _ s _ a
  exact not_lt.symm
#align set.bounded_lt_inter_le Set.bounded_lt_inter_le

theorem unbounded_lt_inter_le [LinearOrder α] (a : α) :
    Unbounded (· < ·) (s ∩ { b | a ≤ b }) ↔ Unbounded (· < ·) s := by
  convert @unbounded_lt_inter_not_lt _ s _ a
  exact not_lt.symm
#align set.unbounded_lt_inter_le Set.unbounded_lt_inter_le

theorem bounded_lt_inter_lt [LinearOrder α] [NoMaxOrder α] (a : α) :
    Bounded (· < ·) (s ∩ { b | a < b }) ↔ Bounded (· < ·) s := by
  rw [← bounded_le_iff_bounded_lt, ← bounded_le_iff_bounded_lt]
  exact bounded_le_inter_lt a
#align set.bounded_lt_inter_lt Set.bounded_lt_inter_lt

theorem unbounded_lt_inter_lt [LinearOrder α] [NoMaxOrder α] (a : α) :
    Unbounded (· < ·) (s ∩ { b | a < b }) ↔ Unbounded (· < ·) s := by
  rw [← not_bounded_iff, ← not_bounded_iff, not_iff_not]
  exact bounded_lt_inter_lt a
#align set.unbounded_lt_inter_lt Set.unbounded_lt_inter_lt

/-! #### Greater or equal -/

theorem bounded_ge_inter_not_ge [SemilatticeInf α] (a : α) :
    Bounded (· ≥ ·) (s ∩ { b | ¬a ≤ b }) ↔ Bounded (· ≥ ·) s :=
  @bounded_le_inter_not_le αᵒᵈ s _ a
#align set.bounded_ge_inter_not_ge Set.bounded_ge_inter_not_ge

theorem unbounded_ge_inter_not_ge [SemilatticeInf α] (a : α) :
    Unbounded (· ≥ ·) (s ∩ { b | ¬a ≤ b }) ↔ Unbounded (· ≥ ·) s :=
  @unbounded_le_inter_not_le αᵒᵈ s _ a
#align set.unbounded_ge_inter_not_ge Set.unbounded_ge_inter_not_ge

theorem bounded_ge_inter_gt [LinearOrder α] (a : α) :
    Bounded (· ≥ ·) (s ∩ { b | b < a }) ↔ Bounded (· ≥ ·) s :=
  @bounded_le_inter_lt αᵒᵈ s _ a
#align set.bounded_ge_inter_gt Set.bounded_ge_inter_gt

theorem unbounded_ge_inter_gt [LinearOrder α] (a : α) :
    Unbounded (· ≥ ·) (s ∩ { b | b < a }) ↔ Unbounded (· ≥ ·) s :=
  @unbounded_le_inter_lt αᵒᵈ s _ a
#align set.unbounded_ge_inter_gt Set.unbounded_ge_inter_gt

theorem bounded_ge_inter_ge [LinearOrder α] (a : α) :
    Bounded (· ≥ ·) (s ∩ { b | b ≤ a }) ↔ Bounded (· ≥ ·) s :=
  @bounded_le_inter_le αᵒᵈ s _ a
#align set.bounded_ge_inter_ge Set.bounded_ge_inter_ge

theorem unbounded_ge_iff_unbounded_inter_ge [LinearOrder α] (a : α) :
    Unbounded (· ≥ ·) (s ∩ { b | b ≤ a }) ↔ Unbounded (· ≥ ·) s :=
  @unbounded_le_inter_le αᵒᵈ s _ a
#align set.unbounded_ge_iff_unbounded_inter_ge Set.unbounded_ge_iff_unbounded_inter_ge

/-! #### Greater than -/

theorem bounded_gt_inter_not_gt [SemilatticeInf α] (a : α) :
    Bounded (· > ·) (s ∩ { b | ¬a < b }) ↔ Bounded (· > ·) s :=
  @bounded_lt_inter_not_lt αᵒᵈ s _ a
#align set.bounded_gt_inter_not_gt Set.bounded_gt_inter_not_gt

theorem unbounded_gt_inter_not_gt [SemilatticeInf α] (a : α) :
    Unbounded (· > ·) (s ∩ { b | ¬a < b }) ↔ Unbounded (· > ·) s :=
  @unbounded_lt_inter_not_lt αᵒᵈ s _ a
#align set.unbounded_gt_inter_not_gt Set.unbounded_gt_inter_not_gt

theorem bounded_gt_inter_ge [LinearOrder α] (a : α) :
    Bounded (· > ·) (s ∩ { b | b ≤ a }) ↔ Bounded (· > ·) s :=
  @bounded_lt_inter_le αᵒᵈ s _ a
#align set.bounded_gt_inter_ge Set.bounded_gt_inter_ge

theorem unbounded_inter_ge [LinearOrder α] (a : α) :
    Unbounded (· > ·) (s ∩ { b | b ≤ a }) ↔ Unbounded (· > ·) s :=
  @unbounded_lt_inter_le αᵒᵈ s _ a
#align set.unbounded_inter_ge Set.unbounded_inter_ge

theorem bounded_gt_inter_gt [LinearOrder α] [NoMinOrder α] (a : α) :
    Bounded (· > ·) (s ∩ { b | b < a }) ↔ Bounded (· > ·) s :=
  @bounded_lt_inter_lt αᵒᵈ s _ _ a
#align set.bounded_gt_inter_gt Set.bounded_gt_inter_gt

theorem unbounded_gt_inter_gt [LinearOrder α] [NoMinOrder α] (a : α) :
    Unbounded (· > ·) (s ∩ { b | b < a }) ↔ Unbounded (· > ·) s :=
  @unbounded_lt_inter_lt αᵒᵈ s _ _ a
#align set.unbounded_gt_inter_gt Set.unbounded_gt_inter_gt

end Set


import SciLean.Core

namespace SciLean

  def π : ℝ := 3.14159265359

  function_properties Math.sin (x : ℝ) : ℝ
  argument x
    isSmooth   := sorry,
    diff_simp  := dx * Math.cos x by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp := dx' * Math.cos x by simp[adjointDifferential]; done

  function_properties Math.cos (x : ℝ) : ℝ
  argument x
    isSmooth   := sorry,
    diff_simp  := -dx * Math.sin x by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp := -dx' * Math.sin x by simp[adjointDifferential]; unfold hold; simp; admit

  -- These are related to Spherical Bessle functions
  -- Also these are just sin and cos that are slowly pealing of terms in Taylor series
  def Math.sinr1 (x : ℝ) : ℝ := if x = 0 then 1     else Math.sin x / x
  def Math.sinr3 (x : ℝ) : ℝ := if x = 0 then -1/6  else (Math.sinr1 x - 1)   / (x*x)
  def Math.sinr5 (x : ℝ) : ℝ := if x = 0 then 1/120 else (Math.sinr3 x + 1/6) / (x*x)

  def Math.cosr2 (x : ℝ) : ℝ := if x = 0 then -1/2 else (Math.cos x   - 1)   / (x*x)
  def Math.cosr4 (x : ℝ) : ℝ := if x = 0 then 1/24 else (Math.cosr2 x + 1/2) / (x*x)

  abbrev Math.sinc := Math.sinr1

  function_properties Math.sinr1 (x : ℝ) : ℝ
  argument x
    isSmooth := sorry,
    diff_simp := dx * (- x * Math.sinr3 x + x * Math.cosr2 x) by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp by simp[adjointDifferential]

  function_properties Math.cosr2 (x : ℝ) : ℝ
  argument x
    isSmooth := sorry,
    diff_simp := dx * (- x * Math.sinr3 x - 2 * x * Math.cosr4 x) by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp by simp[adjointDifferential]

  --------------------------------------------------------------------
  -- Sin and Cos with halfed powers

  def Math.sinr1Sqrt (x : ℝ) : ℝ :=
    if x = 0 then 1 else
    if x > 0 then Math.sinr1 (Math.sqrt x)
    else panic! "Implement sinr1Sqrt for negative values!"
  def Math.sinr3Sqrt (x : ℝ) : ℝ := if x = 0 then -1/6  else (Math.sinr1Sqrt x - 1) / x
  def Math.sinr5Sqrt (x : ℝ) : ℝ := if x = 0 then 1/120 else (Math.sinr3Sqrt x + 1/6) / x

  def Math.cosSqrt (x : ℝ) : ℝ :=
    if (x≥0) then Math.cos (Math.sqrt x) else panic! "Implement cosSqrt for negative values!"
  def Math.cosr2Sqrt (x : ℝ) : ℝ := if x = 0 then -1/2 else (Math.cosSqrt x - 1)   / x
  def Math.cosr4Sqrt (x : ℝ) : ℝ := if x = 0 then 1/24 else (Math.cosr2Sqrt x + 1/2) / x

  function_properties Math.sinr1Sqrt (x : ℝ) : ℝ
  argument x
    isSmooth   := sorry,
    diff_simp  := dx/2 * (cosr2Sqrt x - sinr3Sqrt x) by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp by simp[adjointDifferential]; unfold hold; simp

  function_properties Math.cosSqrt (x : ℝ) : ℝ
  argument x
    isSmooth   := sorry,
    diff_simp  := -dx/2 * Math.sinr1Sqrt x by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp by simp[adjointDifferential]; unfold hold; simp

  function_properties Math.cosr2Sqrt (x : ℝ) : ℝ
  argument x
    isSmooth  := sorry,
    diff_simp := -dx/2 * (sinr3Sqrt x + 2 * cosr4Sqrt x) by sorry,
    hasAdjDiff := by constructor; infer_instance; simp; infer_instance; done,
    adjDiff_simp by simp[adjointDifferential]; unfold hold; simp

  @[simp]
  theorem Math.cos.of_norm_is_cosSqrt {X} [Hilbert X] (x : X) : Math.cos ∥x∥ = Math.cosSqrt ∥x∥² := sorry

  @[simp]
  theorem Math.sinc.of_norm_is_sinr1Sqrt {X} [Hilbert X] (x : X) : Math.sinc ∥x∥ = Math.sinr1Sqrt ∥x∥² := sorry


/-
Copyright (c) 2022 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies

! This file was ported from Lean 3 source module analysis.convex.join
! leanprover-community/mathlib commit 951bf1d9e98a2042979ced62c0620bcfb3587cf8
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Convex.Combination

/-!
# Convex join

This file defines the convex join of two sets. The convex join of `s` and `t` is the union of the
segments with one end in `s` and the other in `t`. This is notably a useful gadget to deal with
convex hulls of finite sets.
-/

open Set

open BigOperators

variable {ι : Sort _} {𝕜 E : Type _}

section OrderedSemiring

variable (𝕜) [OrderedSemiring 𝕜] [AddCommMonoid E] [Module 𝕜 E] {s t s₁ s₂ t₁ t₂ u : Set E}
  {x y : E}

/-- The join of two sets is the union of the segments joining them. This can be interpreted as the
topological join, but within the original space. -/
def convexJoin (s t : Set E) : Set E :=
  ⋃ (x ∈ s) (y ∈ t), segment 𝕜 x y
#align convex_join convexJoin

variable {𝕜}

theorem mem_convexJoin : x ∈ convexJoin 𝕜 s t ↔ ∃ a ∈ s, ∃ b ∈ t, x ∈ segment 𝕜 a b := by
  simp [convexJoin]
#align mem_convex_join mem_convexJoin

theorem convexJoin_comm (s t : Set E) : convexJoin 𝕜 s t = convexJoin 𝕜 t s :=
  (unionᵢ₂_comm _).trans <| by simp_rw [convexJoin, segment_symm]
#align convex_join_comm convexJoin_comm

theorem convexJoin_mono (hs : s₁ ⊆ s₂) (ht : t₁ ⊆ t₂) : convexJoin 𝕜 s₁ t₁ ⊆ convexJoin 𝕜 s₂ t₂ :=
  bunionᵢ_mono hs fun x hx => bunionᵢ_mono ht fun y hy => Subset.rfl
#align convex_join_mono convexJoin_mono

theorem convexJoin_mono_left (hs : s₁ ⊆ s₂) : convexJoin 𝕜 s₁ t ⊆ convexJoin 𝕜 s₂ t :=
  convexJoin_mono hs Subset.rfl
#align convex_join_mono_left convexJoin_mono_left

theorem convexJoin_mono_right (ht : t₁ ⊆ t₂) : convexJoin 𝕜 s t₁ ⊆ convexJoin 𝕜 s t₂ :=
  convexJoin_mono Subset.rfl ht
#align convex_join_mono_right convexJoin_mono_right

@[simp]
theorem convexJoin_empty_left (t : Set E) : convexJoin 𝕜 ∅ t = ∅ := by simp [convexJoin]
#align convex_join_empty_left convexJoin_empty_left

@[simp]
theorem convexJoin_empty_right (s : Set E) : convexJoin 𝕜 s ∅ = ∅ := by simp [convexJoin]
#align convex_join_empty_right convexJoin_empty_right

@[simp]
theorem convexJoin_singleton_left (t : Set E) (x : E) :
    convexJoin 𝕜 {x} t = ⋃ y ∈ t, segment 𝕜 x y := by simp [convexJoin]
#align convex_join_singleton_left convexJoin_singleton_left

@[simp]
theorem convexJoin_singleton_right (s : Set E) (y : E) :
    convexJoin 𝕜 s {y} = ⋃ x ∈ s, segment 𝕜 x y := by simp [convexJoin]
#align convex_join_singleton_right convexJoin_singleton_right

@[simp]
theorem convexJoin_singletons (x : E) : convexJoin 𝕜 {x} {y} = segment 𝕜 x y := by simp [convexJoin]
#align convex_join_singletons convexJoin_singletons

@[simp]
theorem convexJoin_union_left (s₁ s₂ t : Set E) :
    convexJoin 𝕜 (s₁ ∪ s₂) t = convexJoin 𝕜 s₁ t ∪ convexJoin 𝕜 s₂ t := by
  simp_rw [convexJoin, mem_union, Union_or, Union_union_distrib]
#align convex_join_union_left convexJoin_union_left

@[simp]
theorem convexJoin_union_right (s t₁ t₂ : Set E) :
    convexJoin 𝕜 s (t₁ ∪ t₂) = convexJoin 𝕜 s t₁ ∪ convexJoin 𝕜 s t₂ := by
  simp_rw [convexJoin, mem_union, Union_or, Union_union_distrib]
#align convex_join_union_right convexJoin_union_right

@[simp]
theorem convexJoin_unionᵢ_left (s : ι → Set E) (t : Set E) :
    convexJoin 𝕜 (⋃ i, s i) t = ⋃ i, convexJoin 𝕜 (s i) t :=
  by
  simp_rw [convexJoin, mem_Union, Union_exists]
  exact Union_comm _
#align convex_join_Union_left convexJoin_unionᵢ_left

@[simp]
theorem convexJoin_unionᵢ_right (s : Set E) (t : ι → Set E) :
    convexJoin 𝕜 s (⋃ i, t i) = ⋃ i, convexJoin 𝕜 s (t i) := by
  simp_rw [convexJoin_comm s, convexJoin_unionᵢ_left]
#align convex_join_Union_right convexJoin_unionᵢ_right

theorem segment_subset_convexJoin (hx : x ∈ s) (hy : y ∈ t) : segment 𝕜 x y ⊆ convexJoin 𝕜 s t :=
  (subset_unionᵢ₂ y hy).trans (subset_unionᵢ₂ x hx)
#align segment_subset_convex_join segment_subset_convexJoin

theorem subset_convexJoin_left (h : t.Nonempty) : s ⊆ convexJoin 𝕜 s t := fun x hx =>
  let ⟨y, hy⟩ := h
  segment_subset_convexJoin hx hy <| left_mem_segment _ _ _
#align subset_convex_join_left subset_convexJoin_left

theorem subset_convexJoin_right (h : s.Nonempty) : t ⊆ convexJoin 𝕜 s t := fun y hy =>
  let ⟨x, hx⟩ := h
  segment_subset_convexJoin hx hy <| right_mem_segment _ _ _
#align subset_convex_join_right subset_convexJoin_right

theorem convexJoin_subset (hs : s ⊆ u) (ht : t ⊆ u) (hu : Convex 𝕜 u) : convexJoin 𝕜 s t ⊆ u :=
  unionᵢ₂_subset fun x hx => unionᵢ₂_subset fun y hy => hu.segment_subset (hs hx) (ht hy)
#align convex_join_subset convexJoin_subset

theorem convexJoin_subset_convexHull (s t : Set E) : convexJoin 𝕜 s t ⊆ convexHull 𝕜 (s ∪ t) :=
  convexJoin_subset ((subset_union_left _ _).trans <| subset_convexHull _ _)
      ((subset_union_right _ _).trans <| subset_convexHull _ _) <|
    convex_convexHull _ _
#align convex_join_subset_convex_hull convexJoin_subset_convexHull

end OrderedSemiring

section LinearOrderedField

variable [LinearOrderedField 𝕜] [AddCommGroup E] [Module 𝕜 E] {s t u : Set E} {x y : E}

theorem convexJoin_assoc_aux (s t u : Set E) :
    convexJoin 𝕜 (convexJoin 𝕜 s t) u ⊆ convexJoin 𝕜 s (convexJoin 𝕜 t u) :=
  by
  simp_rw [subset_def, mem_convexJoin]
  rintro _ ⟨z, ⟨x, hx, y, hy, a₁, b₁, ha₁, hb₁, hab₁, rfl⟩, z, hz, a₂, b₂, ha₂, hb₂, hab₂, rfl⟩
  obtain rfl | hb₂ := hb₂.eq_or_lt
  · refine' ⟨x, hx, y, ⟨y, hy, z, hz, left_mem_segment _ _ _⟩, a₁, b₁, ha₁, hb₁, hab₁, _⟩
    rw [add_zero] at hab₂
    rw [hab₂, one_smul, zero_smul, add_zero]
  have ha₂b₁ : 0 ≤ a₂ * b₁ := mul_nonneg ha₂ hb₁
  have hab : 0 < a₂ * b₁ + b₂ := add_pos_of_nonneg_of_pos ha₂b₁ hb₂
  refine'
    ⟨x, hx, (a₂ * b₁ / (a₂ * b₁ + b₂)) • y + (b₂ / (a₂ * b₁ + b₂)) • z,
      ⟨y, hy, z, hz, _, _, _, _, _, rfl⟩, a₂ * a₁, a₂ * b₁ + b₂, mul_nonneg ha₂ ha₁, hab.le, _, _⟩
  · exact div_nonneg ha₂b₁ hab.le
  · exact div_nonneg hb₂.le hab.le
  · rw [← add_div, div_self hab.ne']
  · rw [← add_assoc, ← mul_add, hab₁, mul_one, hab₂]
  · simp_rw [smul_add, ← mul_smul, mul_div_cancel' _ hab.ne', add_assoc]
#align convex_join_assoc_aux convexJoin_assoc_aux

theorem convexJoin_assoc (s t u : Set E) :
    convexJoin 𝕜 (convexJoin 𝕜 s t) u = convexJoin 𝕜 s (convexJoin 𝕜 t u) :=
  by
  refine' (convexJoin_assoc_aux _ _ _).antisymm _
  simp_rw [convexJoin_comm s, convexJoin_comm _ u]
  exact convexJoin_assoc_aux _ _ _
#align convex_join_assoc convexJoin_assoc

theorem convexJoin_left_comm (s t u : Set E) :
    convexJoin 𝕜 s (convexJoin 𝕜 t u) = convexJoin 𝕜 t (convexJoin 𝕜 s u) := by
  simp_rw [← convexJoin_assoc, convexJoin_comm]
#align convex_join_left_comm convexJoin_left_comm

theorem convexJoin_right_comm (s t u : Set E) :
    convexJoin 𝕜 (convexJoin 𝕜 s t) u = convexJoin 𝕜 (convexJoin 𝕜 s u) t := by
  simp_rw [convexJoin_assoc, convexJoin_comm]
#align convex_join_right_comm convexJoin_right_comm

theorem convexJoin_convexJoin_convexJoin_comm (s t u v : Set E) :
    convexJoin 𝕜 (convexJoin 𝕜 s t) (convexJoin 𝕜 u v) =
      convexJoin 𝕜 (convexJoin 𝕜 s u) (convexJoin 𝕜 t v) :=
  by simp_rw [← convexJoin_assoc, convexJoin_right_comm]
#align convex_join_convex_join_convex_join_comm convexJoin_convexJoin_convexJoin_comm

theorem convexHull_insert (hs : s.Nonempty) :
    convexHull 𝕜 (insert x s) = convexJoin 𝕜 {x} (convexHull 𝕜 s) := by
  classical
    refine'
      (convexJoin_subset ((singleton_subset_iff.2 <| mem_insert _ _).trans <| subset_convexHull _ _)
              (convexHull_mono <| subset_insert _ _) <|
            convex_convexHull _ _).antisymm'
        fun x hx => _
    rw [convexHull_eq] at hx
    obtain ⟨ι, t, w, z, hw₀, hw₁, hz, rfl⟩ := hx
    have :
      ((∑ i in t.filter fun i => z i = x, w i) • x + ∑ i in t.filter fun i => z i ≠ x, w i • z i) =
        t.center_mass w z :=
      by
      rw [Finset.centerMass_eq_of_sum_1 _ _ hw₁, Finset.sum_smul]
      convert Finset.sum_filter_add_sum_filter_not _ _ (w • z) using 2
      refine' Finset.sum_congr rfl fun i hi => _
      rw [Pi.smul_apply', (Finset.mem_filter.1 hi).2]
    rw [← this]
    have hw₀' : ∀ i ∈ t.filter fun i => z i ≠ x, 0 ≤ w i := fun i hi =>
      hw₀ _ <| Finset.filter_subset _ _ hi
    obtain hw | hw := (Finset.sum_nonneg hw₀').eq_or_gt
    · rw [← Finset.sum_filter_add_sum_filter_not _ fun i => z i = x, hw, add_zero] at hw₁
      rw [hw₁, one_smul, Finset.sum_eq_zero, add_zero]
      · exact subset_convexJoin_left hs.convex_hull (mem_singleton _)
      simp_rw [Finset.sum_eq_zero_iff_of_nonneg hw₀'] at hw
      rintro i hi
      rw [hw _ hi, zero_smul]
    refine'
      mem_convexJoin.2
        ⟨x, mem_singleton _, (t.filter fun i => z i ≠ x).centerMass w z,
          Finset.centerMass_mem_convexHull _ hw₀' hw fun i hi => _,
          ∑ i in t.filter fun i => z i = x, w i, ∑ i in t.filter fun i => z i ≠ x, w i,
          Finset.sum_nonneg fun i hi => hw₀ _ <| Finset.filter_subset _ _ hi,
          Finset.sum_nonneg hw₀', _, _⟩
    · rw [Finset.mem_filter] at hi
      exact mem_of_mem_insert_of_ne (hz _ hi.1) hi.2
    · rw [Finset.sum_filter_add_sum_filter_not, hw₁]
    · rw [Finset.centerMass, smul_inv_smul₀ hw.ne', Finset.sum_smul]
#align convex_hull_insert convexHull_insert

theorem convexJoin_segments (a b c d : E) :
    convexJoin 𝕜 (segment 𝕜 a b) (segment 𝕜 c d) = convexHull 𝕜 {a, b, c, d} := by
  simp only [convexHull_insert, insert_nonempty, singleton_nonempty, convexHull_pair, ←
    convexJoin_assoc, convexJoin_singletons]
#align convex_join_segments convexJoin_segments

theorem convexJoin_segment_singleton (a b c : E) :
    convexJoin 𝕜 (segment 𝕜 a b) {c} = convexHull 𝕜 {a, b, c} := by
  rw [← pair_eq_singleton, ← convexJoin_segments, segment_same, pair_eq_singleton]
#align convex_join_segment_singleton convexJoin_segment_singleton

theorem convexJoin_singleton_segment (a b c : E) :
    convexJoin 𝕜 {a} (segment 𝕜 b c) = convexHull 𝕜 {a, b, c} := by
  rw [← segment_same 𝕜, convexJoin_segments, insert_idem]
#align convex_join_singleton_segment convexJoin_singleton_segment

protected theorem Convex.convexJoin (hs : Convex 𝕜 s) (ht : Convex 𝕜 t) :
    Convex 𝕜 (convexJoin 𝕜 s t) :=
  by
  rw [convex_iff_segment_subset] at ht hs⊢
  simp_rw [mem_convexJoin]
  rintro x ⟨xa, hxa, xb, hxb, hx⟩ y ⟨ya, hya, yb, hyb, hy⟩
  refine' (segment_subset_convexJoin hx hy).trans _
  have triv : ({xa, xb, ya, yb} : Set E) = {xa, ya, xb, yb} := by simp only [Set.insert_comm]
  rw [convexJoin_segments, triv, ← convexJoin_segments]
  exact convexJoin_mono (hs hxa hya) (ht hxb hyb)
#align convex.convex_join Convex.convexJoin

protected theorem Convex.convexHull_union (hs : Convex 𝕜 s) (ht : Convex 𝕜 t) (hs₀ : s.Nonempty)
    (ht₀ : t.Nonempty) : convexHull 𝕜 (s ∪ t) = convexJoin 𝕜 s t :=
  (convexHull_min (union_subset (subset_convexJoin_left ht₀) <| subset_convexJoin_right hs₀) <|
        hs.convexJoin ht).antisymm <|
    convexJoin_subset_convexHull _ _
#align convex.convex_hull_union Convex.convexHull_union

theorem convexHull_union (hs : s.Nonempty) (ht : t.Nonempty) :
    convexHull 𝕜 (s ∪ t) = convexJoin 𝕜 (convexHull 𝕜 s) (convexHull 𝕜 t) :=
  by
  rw [← convexHull_convexHull_union_left, ← convexHull_convexHull_union_right]
  exact
    (convex_convexHull 𝕜 s).convexHull_union (convex_convexHull 𝕜 t) hs.convex_hull ht.convex_hull
#align convex_hull_union convexHull_union

end LinearOrderedField


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import order.conditionally_complete_lattice.basic
import data.set.finite
import algebra.big_operators.basic
import algebra.group.prod
import algebra.group.pi
import algebra.module.basic
import group_theory.group_action.pi

/-!
# Support of a function

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

In this file we define `function.support f = {x | f x ≠ 0}` and prove its basic properties.
We also define `function.mul_support f = {x | f x ≠ 1}`.
-/

open set
open_locale big_operators
namespace function

variables {α β A B M N P R S G M₀ G₀ : Type*} {ι : Sort*}

section has_one

variables [has_one M] [has_one N] [has_one P]

/-- `support` of a function is the set of points `x` such that `f x ≠ 0`. -/
def support [has_zero A] (f : α → A) : set α := {x | f x ≠ 0}

/-- `mul_support` of a function is the set of points `x` such that `f x ≠ 1`. -/
@[to_additive] def mul_support (f : α → M) : set α := {x | f x ≠ 1}

@[to_additive] lemma mul_support_eq_preimage (f : α → M) : mul_support f = f ⁻¹' {1}ᶜ := rfl

@[to_additive] lemma nmem_mul_support {f : α → M} {x : α} :
  x ∉ mul_support f ↔ f x = 1 :=
not_not

@[to_additive] lemma compl_mul_support {f : α → M} :
  (mul_support f)ᶜ = {x | f x = 1} :=
ext $ λ x, nmem_mul_support

@[simp, to_additive] lemma mem_mul_support {f : α → M} {x : α} :
  x ∈ mul_support f ↔ f x ≠ 1 :=
iff.rfl

@[simp, to_additive] lemma mul_support_subset_iff {f : α → M} {s : set α} :
  mul_support f ⊆ s ↔ ∀ x, f x ≠ 1 → x ∈ s :=
iff.rfl

@[to_additive] lemma mul_support_subset_iff' {f : α → M} {s : set α} :
  mul_support f ⊆ s ↔ ∀ x ∉ s, f x = 1 :=
forall_congr $ λ x, not_imp_comm

@[to_additive] lemma mul_support_eq_iff {f : α → M} {s : set α} :
  mul_support f = s ↔ ((∀ x, x ∈ s → f x ≠ 1) ∧ (∀ x, x ∉ s → f x = 1)) :=
by simp only [set.ext_iff, mem_mul_support, ne.def, imp_not_comm, ← forall_and_distrib,
  ← iff_def, ← xor_iff_not_iff', ← xor_iff_iff_not]

@[to_additive] lemma mul_support_disjoint_iff {f : α → M} {s : set α} :
  disjoint (mul_support f) s ↔ eq_on f 1 s :=
by simp_rw [←subset_compl_iff_disjoint_right, mul_support_subset_iff', not_mem_compl_iff, eq_on,
  pi.one_apply]

@[to_additive] lemma disjoint_mul_support_iff {f : α → M} {s : set α} :
  disjoint s (mul_support f) ↔ eq_on f 1 s :=
by rw [disjoint.comm, mul_support_disjoint_iff]

@[simp, to_additive] lemma mul_support_eq_empty_iff {f : α → M} :
  mul_support f = ∅ ↔ f = 1 :=
by { simp_rw [← subset_empty_iff, mul_support_subset_iff', funext_iff], simp }

@[simp, to_additive] lemma mul_support_nonempty_iff {f : α → M} :
  (mul_support f).nonempty ↔ f ≠ 1 :=
by rw [nonempty_iff_ne_empty, ne.def, mul_support_eq_empty_iff]

@[to_additive]
lemma range_subset_insert_image_mul_support (f : α → M) :
  range f ⊆ insert 1 (f '' mul_support f) :=
by simpa only [range_subset_iff, mem_insert_iff, or_iff_not_imp_left]
  using λ x (hx : x ∈ mul_support f), mem_image_of_mem f hx

@[simp, to_additive] lemma mul_support_one' : mul_support (1 : α → M) = ∅ :=
mul_support_eq_empty_iff.2 rfl

@[simp, to_additive] lemma mul_support_one : mul_support (λ x : α, (1 : M)) = ∅ :=
mul_support_one'

@[to_additive]

@[to_additive] lemma mul_support_binop_subset (op : M → N → P) (op1 : op 1 1 = 1)
  (f : α → M) (g : α → N) :
  mul_support (λ x, op (f x) (g x)) ⊆ mul_support f ∪ mul_support g :=
λ x hx, not_or_of_imp (λ hf hg, hx $ by simp only [hf, hg, op1])

@[to_additive] lemma mul_support_sup [semilattice_sup M] (f g : α → M) :
  mul_support (λ x, f x ⊔ g x) ⊆ mul_support f ∪ mul_support g :=
mul_support_binop_subset (⊔) sup_idem f g

@[to_additive] lemma mul_support_inf [semilattice_inf M] (f g : α → M) :
  mul_support (λ x, f x ⊓ g x) ⊆ mul_support f ∪ mul_support g :=
mul_support_binop_subset (⊓) inf_idem f g

@[to_additive] lemma mul_support_max [linear_order M] (f g : α → M) :
  mul_support (λ x, max (f x) (g x)) ⊆ mul_support f ∪ mul_support g :=
mul_support_sup f g

@[to_additive] lemma mul_support_min [linear_order M] (f g : α → M) :
  mul_support (λ x, min (f x) (g x)) ⊆ mul_support f ∪ mul_support g :=
mul_support_inf f g

@[to_additive] lemma mul_support_supr [conditionally_complete_lattice M] [nonempty ι]
  (f : ι → α → M) :
  mul_support (λ x, ⨆ i, f i x) ⊆ ⋃ i, mul_support (f i) :=
begin
  rw mul_support_subset_iff',
  simp only [mem_Union, not_exists, nmem_mul_support],
  intros x hx,
  simp only [hx, csupr_const]
end

@[to_additive] lemma mul_support_infi [conditionally_complete_lattice M] [nonempty ι]
  (f : ι → α → M) :
  mul_support (λ x, ⨅ i, f i x) ⊆ ⋃ i, mul_support (f i) :=
@mul_support_supr _ Mᵒᵈ ι ⟨(1:M)⟩ _ _ f

@[to_additive] lemma mul_support_comp_subset {g : M → N} (hg : g 1 = 1) (f : α → M) :
  mul_support (g ∘ f) ⊆ mul_support f :=
λ x, mt $ λ h, by simp only [(∘), *]

@[to_additive] lemma mul_support_subset_comp {g : M → N} (hg : ∀ {x}, g x = 1 → x = 1)
  (f : α → M) :
  mul_support f ⊆ mul_support (g ∘ f) :=
λ x, mt hg

@[to_additive] lemma mul_support_comp_eq (g : M → N) (hg : ∀ {x}, g x = 1 ↔ x = 1)
  (f : α → M) :
  mul_support (g ∘ f) = mul_support f :=
set.ext $ λ x, not_congr hg

@[to_additive] lemma mul_support_comp_eq_preimage (g : β → M) (f : α → β) :
  mul_support (g ∘ f) = f ⁻¹' mul_support g :=
rfl

@[to_additive support_prod_mk] lemma mul_support_prod_mk (f : α → M) (g : α → N) :
  mul_support (λ x, (f x, g x)) = mul_support f ∪ mul_support g :=
set.ext $ λ x, by simp only [mul_support, not_and_distrib, mem_union, mem_set_of_eq,
  prod.mk_eq_one, ne.def]

@[to_additive support_prod_mk'] lemma mul_support_prod_mk' (f : α → M × N) :
  mul_support f = mul_support (λ x, (f x).1) ∪ mul_support (λ x, (f x).2) :=
by simp only [← mul_support_prod_mk, prod.mk.eta]

@[to_additive] lemma mul_support_along_fiber_subset (f : α × β → M) (a : α) :
  mul_support (λ b, f (a, b)) ⊆ (mul_support f).image prod.snd :=
by tidy

@[simp, to_additive] lemma mul_support_along_fiber_finite_of_finite
  (f : α × β → M) (a : α) (h : (mul_support f).finite) :
  (mul_support (λ b, f (a, b))).finite :=
(h.image prod.snd).subset (mul_support_along_fiber_subset f a)

end has_one

@[to_additive] lemma mul_support_mul [mul_one_class M] (f g : α → M) :
  mul_support (λ x, f x * g x) ⊆ mul_support f ∪ mul_support g :=
mul_support_binop_subset (*) (one_mul _) f g

@[to_additive] lemma mul_support_pow [monoid M] (f : α → M) (n : ℕ) :
  mul_support (λ x, f x ^ n) ⊆ mul_support f :=
begin
  induction n with n hfn,
  { simpa only [pow_zero, mul_support_one] using empty_subset _ },
  { simpa only [pow_succ] using (mul_support_mul f _).trans (union_subset subset.rfl hfn) }
end

section division_monoid
variables [division_monoid G] (f g : α → G)

@[simp, to_additive]
lemma mul_support_inv : mul_support (λ x, (f x)⁻¹) = mul_support f := ext $ λ _, inv_ne_one

@[simp, to_additive] lemma mul_support_inv' : mul_support f⁻¹ = mul_support f := mul_support_inv f

@[to_additive] lemma mul_support_mul_inv :
  mul_support (λ x, f x * (g x)⁻¹) ⊆ mul_support f ∪ mul_support g :=
mul_support_binop_subset (λ a b, a * b⁻¹) (by simp) f g

@[to_additive] lemma mul_support_div :
  mul_support (λ x, f x / g x) ⊆ mul_support f ∪ mul_support g :=
mul_support_binop_subset (/) one_div_one f g

end division_monoid

section zero_one
variables (R) [has_zero R] [has_one R] [ne_zero (1 : R)]

@[simp] lemma support_one : support (1 : α → R) = univ := support_const one_ne_zero
@[simp] lemma mul_support_zero : mul_support (0 : α → R) = univ := mul_support_const zero_ne_one

end zero_one

section add_monoid_with_one
variables [add_monoid_with_one R] [char_zero R] {n : ℕ}

lemma support_nat_cast (hn : n ≠ 0) : support (n : α → R) = univ :=
support_const $ nat.cast_ne_zero.2 hn

lemma mul_support_nat_cast (hn : n ≠ 1) : mul_support (n : α → R) = univ :=
mul_support_const $ nat.cast_ne_one.2 hn

end add_monoid_with_one

section add_group_with_one
variables [add_group_with_one R] [char_zero R] {n : ℤ}

lemma support_int_cast (hn : n ≠ 0) : support (n : α → R) = univ :=
support_const $ int.cast_ne_zero.2 hn

lemma mul_support_int_cast (hn : n ≠ 1) : mul_support (n : α → R) = univ :=
mul_support_const $ int.cast_ne_one.2 hn

end add_group_with_one

lemma support_smul [has_zero R] [has_zero M] [smul_with_zero R M] [no_zero_smul_divisors R M]
  (f : α → R) (g : α → M) :
  support (f • g) = support f ∩ support g :=
ext $ λ x, smul_ne_zero_iff

@[simp] lemma support_mul [mul_zero_class R] [no_zero_divisors R] (f g : α → R) :
  support (λ x, f x * g x) = support f ∩ support g :=
support_smul f g

@[simp] lemma support_mul_subset_left [mul_zero_class R] (f g : α → R) :
  support (λ x, f x * g x) ⊆ support f :=
λ x hfg hf, hfg $ by simp only [hf, zero_mul]

@[simp] lemma support_mul_subset_right [mul_zero_class R] (f g : α → R) :
  support (λ x, f x * g x) ⊆ support g :=
λ x hfg hg, hfg $ by simp only [hg, mul_zero]

lemma support_smul_subset_right [add_monoid A] [monoid B] [distrib_mul_action B A]
  (b : B) (f : α → A) :
  support (b • f) ⊆ support f :=
λ x hbf hf, hbf $ by rw [pi.smul_apply, hf, smul_zero]

lemma support_smul_subset_left [has_zero M] [has_zero β] [smul_with_zero M β]
  (f : α → M) (g : α → β) :
  support (f • g) ⊆ support f :=
λ x hfg hf, hfg $ by rw [pi.smul_apply', hf, zero_smul]

lemma support_const_smul_of_ne_zero [semiring R] [add_comm_monoid M] [module R M]
  [no_zero_smul_divisors R M] (c : R) (g : α → M) (hc : c ≠ 0) :
  support (c • g) = support g :=
ext $ λ x, by simp only [hc, mem_support, pi.smul_apply, ne.def, smul_eq_zero, false_or]

@[simp] lemma support_inv [group_with_zero G₀] (f : α → G₀) :
  support (λ x, (f x)⁻¹) = support f :=
set.ext $ λ x, not_congr inv_eq_zero

@[simp] lemma support_div [group_with_zero G₀] (f g : α → G₀) :
  support (λ x, f x / g x) = support f ∩ support g :=
by simp [div_eq_mul_inv]

@[to_additive] lemma mul_support_prod [comm_monoid M] (s : finset α) (f : α → β → M) :
  mul_support (λ x, ∏ i in s, f i x) ⊆ ⋃ i ∈ s, mul_support (f i) :=
begin
  rw mul_support_subset_iff',
  simp only [mem_Union, not_exists, nmem_mul_support],
  exact λ x, finset.prod_eq_one
end

lemma support_prod_subset [comm_monoid_with_zero A] (s : finset α) (f : α → β → A) :
  support (λ x, ∏ i in s, f i x) ⊆ ⋂ i ∈ s, support (f i) :=
λ x hx, mem_Inter₂.2 $ λ i hi H, hx $ finset.prod_eq_zero hi H

lemma support_prod [comm_monoid_with_zero A] [no_zero_divisors A] [nontrivial A]
  (s : finset α) (f : α → β → A) :
  support (λ x, ∏ i in s, f i x) = ⋂ i ∈ s, support (f i) :=
set.ext $ λ x, by
  simp only [support, ne.def, finset.prod_eq_zero_iff, mem_set_of_eq, set.mem_Inter, not_exists]

lemma mul_support_one_add [has_one R] [add_left_cancel_monoid R] (f : α → R) :
  mul_support (λ x, 1 + f x) = support f :=
set.ext $ λ x, not_congr add_right_eq_self

lemma mul_support_one_add' [has_one R] [add_left_cancel_monoid R] (f : α → R) :
  mul_support (1 + f) = support f :=
mul_support_one_add f

lemma mul_support_add_one [has_one R] [add_right_cancel_monoid R] (f : α → R) :
  mul_support (λ x, f x + 1) = support f :=
set.ext $ λ x, not_congr add_left_eq_self

lemma mul_support_add_one' [has_one R] [add_right_cancel_monoid R] (f : α → R) :
  mul_support (f + 1) = support f :=
mul_support_add_one f

lemma mul_support_one_sub' [has_one R] [add_group R] (f : α → R) :
  mul_support (1 - f) = support f :=
by rw [sub_eq_add_neg, mul_support_one_add', support_neg']

lemma mul_support_one_sub [has_one R] [add_group R] (f : α → R) :
  mul_support (λ x, 1 - f x) = support f :=
mul_support_one_sub' f

end function

namespace set

open function

variables {α β M : Type*} [has_one M] {f : α → M}

@[to_additive] lemma image_inter_mul_support_eq {s : set β} {g : β → α} :
  (g '' s ∩ mul_support f) = g '' (s ∩ mul_support (f ∘ g)) :=
by rw [mul_support_comp_eq_preimage f g, image_inter_preimage]

end set

namespace pi
variables {A : Type*} {B : Type*} [decidable_eq A] [has_one B] {a : A} {b : B}

open function

@[to_additive] lemma mul_support_mul_single_subset : mul_support (mul_single a b) ⊆ {a} :=
λ x hx, by_contra $ λ hx', hx $ mul_single_eq_of_ne hx' _

@[to_additive] lemma mul_support_mul_single_one : mul_support (mul_single a (1 : B)) = ∅ :=
by simp

@[simp, to_additive] lemma mul_support_mul_single_of_ne (h : b ≠ 1) :
  mul_support (mul_single a b) = {a} :=
mul_support_mul_single_subset.antisymm $
  λ x (hx : x = a), by rwa [mem_mul_support, hx, mul_single_eq_same]

@[to_additive] lemma mul_support_mul_single [decidable_eq B] :
  mul_support (mul_single a b) = if b = 1 then ∅ else {a} := by { split_ifs with h; simp [h] }

@[to_additive]
lemma mul_support_mul_single_disjoint {b' : B} (hb : b ≠ 1) (hb' : b' ≠ 1) {i j : A} :
  disjoint (mul_support (mul_single i b)) (mul_support (mul_single j b')) ↔ i ≠ j :=
by rw [mul_support_mul_single_of_ne hb, mul_support_mul_single_of_ne hb', disjoint_singleton]

end pi


open tactic
open tactic.interactive
open interactive (parse)
open lean.parser

variables {p q : Prop}

-- This tactic is like `left`, but only works on ∨
meta def myorleft : tactic unit :=
do
  -- Pattern match with the current goal to get exprs l and r for
  -- the left and right side of the `or`
  `(%%l ∨ %%r) ← target,
  -- Make a metavar (to use, and form the goal) out of the expr l
  g ← mk_meta_var l,
  -- Use it to construct the or
  exact `(@or.inl %%l %%r %%g),
  -- Set the goals
  gs ← get_goals,
  set_goals (g::gs)

example (h : p) : p ∨ q :=
begin
  myorleft,
  from h,
end

-- And similarly for ∧
meta def myandsplit : tactic unit :=
do
  -- Pattern match with and
  `(%%l ∧ %%r) ← target,
  -- Make the two metavars we'll need
  gl ← mk_meta_var l,
  gr ← mk_meta_var r,
  -- Exact it
  exact `(@and.intro %%l %%r %%gl %%gr),
  -- Set the goals
  gs ← get_goals,
  set_goals (gl :: gr :: gs)

example (h : q ∧ p): p ∧ q :=
begin
  myandsplit, {
    from h.right,
  }, {
    from h.left,
  },
end

-- Change the goal to false
meta def myexfalso : tactic unit :=
do
  -- Get the current goal
  g ← target,
  -- Create an expr false
  f ← to_expr ``(false),
  -- Create a metavar for the expression false, so we can make a goal
  gfalse ← mk_meta_var f,
  -- mk_app creates an application of false.elim with args g gfalse
  -- Using exact with `(@false.elim) gave a weird error:
  -- invalid quotation, contains universe metavariable
  -- tactic.exact `(@false.elim %%g %%gfalse)
  -- Then we send this to exact
  mk_app ``false.elim [g, gfalse] >>= tactic.exact,
  -- Get the goals (if we chuck them away it would break more complex proofs)
  gs ← get_goals,
  set_goals (gfalse :: gs)

example (hp : p) (h : p → false): q :=
begin
  myexfalso,
  apply h,
  exact hp,
  -- myexfalso, -- fails
end

open classical
-- Use classical reasoning, to convert goal p ∨ q to just q, but
-- assuming ¬p
-- NOTE: If you don't call it `tactic.interactive.` then
-- it doesn't accept arguments.
meta def tactic.interactive.ornotfirst
(h : parse (optional (tk "with" *> ident))) : tactic unit :=
do
  -- Use pattern matching on the target, to get the exprs l and r
  `(%%l ∨ %%r) ← target,
  -- Use what we're given, call it "this" otherwise
  let hh := h.get_or_else `this,
  -- Do cases on excluded middle, load it into temporary thing called
  -- hp (probably risky if there could be something else called hp
  -- - use mk_fresh_name?) and put the ¬p into hh
  cases `(em %%l) [`hp, hh],
  -- Get the temporary thing we just made, as an expr
  hhp ← to_expr ```(hp),
  -- Use it to form the term we need
  tactic.exact `(@or.inl %%l %%r %%hhp),
  -- Now the user only needs to solve the right
  tactic.right,
  -- Do nothing, necessary to form type tactic unit
  tactic.skip

example (h : ¬p → q): p ∨ q :=
(or.elim (em p)
  (assume hp,
   show p∨q, from or.inl hp)
  (assume hnp,
   show p∨q, from or.inr (h hnp)))

example (h : ¬p → q): p ∨ q :=
begin
  ornotfirst with hnp,
  from h hnp,
end

-- From the tactic writing tutorial : here for reference
open interactive (loc.ns)
open interactive.types (texpr location)
meta def tactic.interactive.mul_left (q : parse texpr) : parse location → tactic unit
| (loc.ns [some h]) := do
   e ← tactic.i_to_expr q,
   H ← get_local h,
   `(%%l = %%r) ← infer_type H,
   «have» h ``(%%e*%%l = %%e*%%r) ``(congr_arg (λ x, %%e*x) %%H),
   tactic.clear H
| _ := tactic.fail "mul_left takes exactly one location"

example (a b c : ℤ) (hyp : a = b) : c*a = c*b :=
begin
  mul_left c at hyp,
  exact hyp
end


/-
Copyright (c) 2020 Jean Lo. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jean Lo
-/
import dynamics.flow

/-!
# ω-limits

For a function `ϕ : τ → α → β` where `β` is a topological space, we
define the ω-limit under `ϕ` of a set `s` in `α` with respect to
filter `f` on `τ`: an element `y : β` is in the ω-limit of `s` if the
forward images of `s` intersect arbitrarily small neighbourhoods of
`y` frequently "in the direction of `f`".

In practice `ϕ` is often a continuous monoid-act, but the definition
requires only that `ϕ` has a coercion to the appropriate function
type. In the case where `τ` is `ℕ` or `ℝ` and `f` is `at_top`, we
recover the usual definition of the ω-limit set as the set of all `y`
such that there exist sequences `(tₙ)`, `(xₙ)` such that `ϕ tₙ xₙ ⟶ y`
as `n ⟶ ∞`.

## Notations

The `omega_limit` locale provides the localised notation `ω` for
`omega_limit`, as well as `ω⁺` and `ω⁻` for `omega_limit at_top` and
`omega_limit at_bot` respectively for when the acting monoid is
endowed with an order.
-/

open set function filter
open_locale topological_space

/-!
### Definition and notation
-/

section omega_limit

variables {τ : Type*} {α : Type*} {β : Type*} {ι : Type*}

/-- The ω-limit of a set `s` under `ϕ` with respect to a filter `f` is
    ⋂ u ∈ f, cl (ϕ u s). -/
def omega_limit [topological_space β] (f : filter τ) (ϕ : τ → α → β) (s : set α) : set β :=
⋂ u ∈ f, closure (image2 ϕ u s)

localized "notation `ω` := omega_limit" in omega_limit

localized "notation `ω⁺` := omega_limit filter.at_top" in omega_limit
localized "notation `ω⁻` := omega_limit filter.at_bot" in omega_limit

variables [topological_space β]
variables (f : filter τ) (ϕ : τ → α → β) (s s₁ s₂: set α)

/-!
### Elementary properties
-/

lemma omega_limit_def : ω f ϕ s = ⋂ u ∈ f, closure (image2 ϕ u s) := rfl

lemma omega_limit_subset_of_tendsto {m : τ → τ} {f₁ f₂ : filter τ} (hf : tendsto m f₁ f₂) :
  ω f₁ (λ t x, ϕ (m t) x) s ⊆ ω f₂ ϕ s :=
begin
  refine Inter₂_mono' (λ u hu, ⟨m ⁻¹' u, tendsto_def.mp hf _ hu, _⟩),
  rw ←image2_image_left,
  exact closure_mono (image2_subset (image_preimage_subset _ _) subset.rfl),
end

lemma omega_limit_mono_left {f₁ f₂ : filter τ} (hf : f₁ ≤ f₂) : ω f₁ ϕ s ⊆ ω f₂ ϕ s :=
omega_limit_subset_of_tendsto ϕ s (tendsto_id' hf)

lemma omega_limit_mono_right {s₁ s₂ : set α} (hs : s₁ ⊆ s₂) : ω f ϕ s₁ ⊆ ω f ϕ s₂ :=
Inter₂_mono $ λ u hu, closure_mono (image2_subset subset.rfl hs)

lemma is_closed_omega_limit : is_closed (ω f ϕ s) :=
is_closed_Inter $ λ u, is_closed_Inter $ λ hu, is_closed_closure

lemma maps_to_omega_limit' {α' β' : Type*} [topological_space β'] {f : filter τ} {ϕ : τ → α → β}
  {ϕ' : τ → α' → β'} {ga : α → α'} {s' : set α'} (hs : maps_to ga s s')
  {gb : β → β'} (hg : ∀ᶠ t in f, eq_on (gb ∘ (ϕ t)) (ϕ' t ∘ ga) s)
  (hgc : continuous gb) :
  maps_to gb (ω f ϕ s) (ω f ϕ' s') :=
begin
  simp only [omega_limit_def, mem_Inter, maps_to],
  intros y hy u hu,
  refine map_mem_closure hgc (hy _ (inter_mem hu hg)) (forall_image2_iff.2 $ λ t ht x hx, _),
  calc gb (ϕ t x) = ϕ' t (ga x) : ht.2 hx
  ... ∈ image2 ϕ' u s' : mem_image2_of_mem ht.1 (hs hx)
end

lemma maps_to_omega_limit {α' β' : Type*} [topological_space β'] {f : filter τ} {ϕ : τ → α → β}
  {ϕ' : τ → α' → β'} {ga : α → α'} {s' : set α'} (hs : maps_to ga s s')
  {gb : β → β'} (hg : ∀ t x, gb (ϕ t x) = ϕ' t (ga x))
  (hgc : continuous gb) :
  maps_to gb (ω f ϕ s) (ω f ϕ' s') :=
maps_to_omega_limit' _ hs (eventually_of_forall $ λ t x hx, hg t x) hgc

lemma omega_limit_image_eq {α' : Type*} (ϕ : τ → α' → β) (f : filter τ) (g : α → α') :
  ω f ϕ (g '' s) = ω f (λ t x, ϕ t (g x)) s :=
by simp only [omega_limit, image2_image_right]

lemma omega_limit_preimage_subset {α' : Type*} (ϕ : τ → α' → β) (s : set α') (f : filter τ)
  (g : α → α') :
  ω f (λ t x, ϕ t (g x)) (g ⁻¹' s) ⊆ ω f ϕ s :=
maps_to_omega_limit _ (maps_to_preimage _ _) (λ t x, rfl) continuous_id

/-!
### Equivalent definitions of the omega limit

The next few lemmas are various versions of the property
characterising ω-limits:
-/

/-- An element `y` is in the ω-limit set of `s` w.r.t. `f` if the
    preimages of an arbitrary neighbourhood of `y` frequently
    (w.r.t. `f`) intersects of `s`. -/
lemma mem_omega_limit_iff_frequently (y : β) : y ∈ ω f ϕ s ↔
  ∀ n ∈ 𝓝 y, ∃ᶠ t in f, (s ∩ ϕ t ⁻¹' n).nonempty :=
begin
  simp_rw [frequently_iff, omega_limit_def, mem_Inter, mem_closure_iff_nhds],
  split,
  { intros h _ hn _ hu,
    rcases h _ hu _ hn with ⟨_, _, _, _, ht, hx, hϕtx⟩,
    exact ⟨_, ht, _, hx, by rwa [mem_preimage, hϕtx]⟩, },
  { intros h _ hu _ hn,
    rcases h _ hn hu with ⟨_, ht, _, hx, hϕtx⟩,
    exact  ⟨_, hϕtx, _, _, ht, hx, rfl⟩ }
end

/-- An element `y` is in the ω-limit set of `s` w.r.t. `f` if the
    forward images of `s` frequently (w.r.t. `f`) intersect arbitrary
    neighbourhoods of `y`. -/
lemma mem_omega_limit_iff_frequently₂ (y : β) : y ∈ ω f ϕ s ↔
  ∀ n ∈ 𝓝 y, ∃ᶠ t in f, (ϕ t '' s ∩ n).nonempty :=
by simp_rw [mem_omega_limit_iff_frequently, image_inter_nonempty_iff]

/-- An element `y` is in the ω-limit of `x` w.r.t. `f` if the forward
    images of `x` frequently (w.r.t. `f`) falls within an arbitrary
    neighbourhood of `y`. -/
lemma mem_omega_limit_singleton_iff_map_cluster_point (x : α) (y : β) :
  y ∈ ω f ϕ {x} ↔ map_cluster_pt y f (λ t, ϕ t x) :=
by simp_rw [mem_omega_limit_iff_frequently, map_cluster_pt_iff, singleton_inter_nonempty,
  mem_preimage]

/-!
### Set operations and omega limits
-/

lemma omega_limit_inter : ω f ϕ (s₁ ∩ s₂) ⊆ ω f ϕ s₁ ∩ ω f ϕ s₂ :=
subset_inter (omega_limit_mono_right _ _ (inter_subset_left _ _))
  (omega_limit_mono_right _ _(inter_subset_right _ _))

lemma omega_limit_Inter (p : ι → set α) : ω f ϕ (⋂ i, p i) ⊆ ⋂ i, ω f ϕ (p i) :=
subset_Inter $ λ i, omega_limit_mono_right _ _ (Inter_subset _ _)

lemma omega_limit_union : ω f ϕ (s₁ ∪ s₂) = ω f ϕ s₁ ∪ ω f ϕ s₂ :=
begin
  ext y, split,
  { simp only [mem_union, mem_omega_limit_iff_frequently, union_inter_distrib_right,
      union_nonempty, frequently_or_distrib],
    contrapose!,
    simp only [not_frequently, not_nonempty_iff_eq_empty, ← subset_empty_iff],
    rintro ⟨⟨n₁, hn₁, h₁⟩, ⟨n₂, hn₂, h₂⟩⟩,
    refine ⟨n₁ ∩ n₂, inter_mem hn₁ hn₂, h₁.mono $ λ t, _, h₂.mono $ λ t, _⟩,
    exacts [subset.trans $ inter_subset_inter_right _ $ preimage_mono $ inter_subset_left _ _,
      subset.trans $ inter_subset_inter_right _ $ preimage_mono $ inter_subset_right _ _] },
  { rintros (hy|hy),
    exacts [omega_limit_mono_right _ _ (subset_union_left  _ _) hy,
      omega_limit_mono_right _ _ (subset_union_right _ _) hy] },
end

lemma omega_limit_Union (p : ι → set α) : (⋃ i, ω f ϕ (p i)) ⊆ ω f ϕ ⋃ i, p i :=
by { rw Union_subset_iff,
     exact λ i, omega_limit_mono_right _ _ (subset_Union _ _)}

/-!
Different expressions for omega limits, useful for rewrites. In
particular, one may restrict the intersection to sets in `f` which are
subsets of some set `v` also in `f`.
-/

lemma omega_limit_eq_Inter : ω f ϕ s = ⋂ u : ↥f.sets, closure (image2 ϕ u s) :=
bInter_eq_Inter _ _

lemma omega_limit_eq_bInter_inter {v : set τ} (hv : v ∈ f) :
  ω f ϕ s = ⋂ u ∈ f, closure (image2 ϕ (u ∩ v) s) :=
subset.antisymm
  (Inter₂_mono' $ λ u hu, ⟨u ∩ v, inter_mem hu hv, subset.rfl⟩)
  (Inter₂_mono $ λ u hu, closure_mono $ image2_subset (inter_subset_left _ _) subset.rfl)

lemma omega_limit_eq_Inter_inter {v : set τ} (hv : v ∈ f) :
  ω f ϕ s = ⋂ (u : ↥f.sets), closure (image2 ϕ (u ∩ v) s) :=
by { rw omega_limit_eq_bInter_inter _ _ _ hv, apply bInter_eq_Inter }

lemma omega_limit_subset_closure_fw_image {u : set τ} (hu : u ∈ f) :
  ω f ϕ s ⊆ closure (image2 ϕ u s) :=
begin
  rw omega_limit_eq_Inter,
  intros _ hx,
  rw mem_Inter at hx,
  exact hx ⟨u, hu⟩,
end

/-!
### `ω-limits and compactness
-/

/-- A set is eventually carried into any open neighbourhood of its ω-limit:
if `c` is a compact set such that `closure {ϕ t x | t ∈ v, x ∈ s} ⊆ c` for some `v ∈ f`
and `n` is an open neighbourhood of `ω f ϕ s`, then for some `u ∈ f` we have
`closure {ϕ t x | t ∈ u, x ∈ s} ⊆ n`. -/
lemma eventually_closure_subset_of_is_compact_absorbing_of_is_open_of_omega_limit_subset'
  {c : set β} (hc₁ : is_compact c) (hc₂ : ∃ v ∈ f, closure (image2 ϕ v s) ⊆ c)
  {n : set β} (hn₁ : is_open n) (hn₂ : ω f ϕ s ⊆ n) :
  ∃ u ∈ f, closure (image2 ϕ u s) ⊆ n :=
begin
  rcases hc₂ with ⟨v, hv₁, hv₂⟩,
  let k := closure (image2 ϕ v s),
  have hk : is_compact (k \ n) :=
    is_compact.diff (compact_of_is_closed_subset hc₁ is_closed_closure hv₂) hn₁,
  let j := λ u, (closure (image2 ϕ (u ∩ v) s))ᶜ,
  have hj₁ : ∀ u ∈ f, is_open (j u), from
    λ _ _, (is_open_compl_iff.mpr is_closed_closure),
  have hj₂ : k \ n ⊆ ⋃ u ∈ f, j u, begin
    have : (⋃ u ∈ f, j u) = ⋃ (u : ↥f.sets), j u, from bUnion_eq_Union _ _,
    rw [this, diff_subset_comm, diff_Union],
    rw omega_limit_eq_Inter_inter _ _ _ hv₁ at hn₂,
    simp_rw diff_compl,
    rw ←inter_Inter,
    exact subset.trans (inter_subset_right _ _) hn₂,
  end,
  rcases hk.elim_finite_subcover_image hj₁ hj₂ with ⟨g, hg₁ : ∀ u ∈ g, u ∈ f, hg₂, hg₃⟩,
  let w := (⋂ u ∈ g, u) ∩ v,
  have hw₂ : w ∈ f, by simpa *,
  have hw₃ : k \ n ⊆ (closure (image2 ϕ w s))ᶜ, from
    calc k \ n ⊆ ⋃ u ∈ g, j u : hg₃
    ... ⊆ (closure (image2 ϕ w s))ᶜ :
    begin
      simp only [Union_subset_iff, compl_subset_compl],
      intros u hu,
      mono* using [w],
      exact Inter_subset_of_subset u (Inter_subset_of_subset hu subset.rfl),
    end,
  have hw₄ : kᶜ ⊆ (closure (image2 ϕ w s))ᶜ, begin
    rw compl_subset_compl,
    calc closure (image2 ϕ w s)
        ⊆ _ : closure_mono (image2_subset (inter_subset_right _ _) subset.rfl)
  end,
  have hnc : nᶜ ⊆ (k \ n) ∪ kᶜ, by rw [union_comm, ←inter_subset, diff_eq, inter_comm],
  have hw : closure (image2 ϕ w s) ⊆ n, from
    compl_subset_compl.mp (subset.trans hnc (union_subset hw₃ hw₄)),
  exact ⟨_, hw₂, hw⟩
end

/-- A set is eventually carried into any open neighbourhood of its ω-limit:
if `c` is a compact set such that `closure {ϕ t x | t ∈ v, x ∈ s} ⊆ c` for some `v ∈ f`
and `n` is an open neighbourhood of `ω f ϕ s`, then for some `u ∈ f` we have
`closure {ϕ t x | t ∈ u, x ∈ s} ⊆ n`. -/
lemma eventually_closure_subset_of_is_compact_absorbing_of_is_open_of_omega_limit_subset
  [t2_space β] {c : set β} (hc₁ : is_compact c) (hc₂ : ∀ᶠ t in f, maps_to (ϕ t) s c)
  {n : set β} (hn₁ : is_open n) (hn₂ : ω f ϕ s ⊆ n) :
  ∃ u ∈ f, closure (image2 ϕ u s) ⊆ n :=
eventually_closure_subset_of_is_compact_absorbing_of_is_open_of_omega_limit_subset'
  f ϕ _ hc₁ ⟨_, hc₂, closure_minimal (image2_subset_iff.2 (λ t, id)) hc₁.is_closed⟩ hn₁ hn₂

lemma eventually_maps_to_of_is_compact_absorbing_of_is_open_of_omega_limit_subset
  [t2_space β] {c : set β} (hc₁ : is_compact c) (hc₂ : ∀ᶠ t in f, maps_to (ϕ t) s c)
  {n : set β} (hn₁ : is_open n) (hn₂ : ω f ϕ s ⊆ n) :
  ∀ᶠ t in f, maps_to (ϕ t) s n :=
begin
  rcases eventually_closure_subset_of_is_compact_absorbing_of_is_open_of_omega_limit_subset
    f ϕ s hc₁ hc₂ hn₁ hn₂ with ⟨u, hu_mem, hu⟩,
  refine mem_of_superset hu_mem (λ t ht x hx, _),
  exact hu (subset_closure $ mem_image2_of_mem ht hx)
end

lemma eventually_closure_subset_of_is_open_of_omega_limit_subset [compact_space β]
  {v : set β} (hv₁ : is_open v) (hv₂ : ω f ϕ s ⊆ v) :
  ∃ u ∈ f, closure (image2 ϕ u s) ⊆ v :=
eventually_closure_subset_of_is_compact_absorbing_of_is_open_of_omega_limit_subset'
  _ _ _ compact_univ ⟨univ, univ_mem, subset_univ _⟩ hv₁ hv₂

lemma eventually_maps_to_of_is_open_of_omega_limit_subset [compact_space β]
  {v : set β} (hv₁ : is_open v) (hv₂ : ω f ϕ s ⊆ v) :
  ∀ᶠ t in f, maps_to (ϕ t) s v :=
begin
  rcases eventually_closure_subset_of_is_open_of_omega_limit_subset f ϕ s hv₁ hv₂
    with ⟨u, hu_mem, hu⟩,
  refine mem_of_superset hu_mem (λ t ht x hx, _),
  exact hu (subset_closure $ mem_image2_of_mem ht hx)
end

/-- The ω-limit of a nonempty set w.r.t. a nontrivial filter is nonempty. -/
lemma nonempty_omega_limit_of_is_compact_absorbing [ne_bot f] {c : set β} (hc₁ : is_compact c)
  (hc₂ : ∃ v ∈ f, closure (image2 ϕ v s) ⊆ c) (hs : s.nonempty) :
  (ω f ϕ s).nonempty :=
begin
  rcases hc₂ with ⟨v, hv₁, hv₂⟩,
  rw omega_limit_eq_Inter_inter _ _ _ hv₁,
  apply is_compact.nonempty_Inter_of_directed_nonempty_compact_closed,
  { rintro ⟨u₁, hu₁⟩ ⟨u₂, hu₂⟩,
    use ⟨u₁ ∩ u₂, inter_mem hu₁ hu₂⟩, split,
   all_goals { exact closure_mono (image2_subset
        (inter_subset_inter_left _ (by simp)) subset.rfl) }},
  { intro u,
    have hn : (image2 ϕ (u ∩ v) s).nonempty, from
      nonempty.image2 (nonempty_of_mem (inter_mem u.prop hv₁)) hs,
    exact hn.mono subset_closure },
  { intro _,
    apply compact_of_is_closed_subset hc₁ is_closed_closure,
    calc _ ⊆ closure (image2 ϕ v s) : closure_mono (image2_subset
                                        (inter_subset_right _ _) subset.rfl)
    ...    ⊆ c : hv₂ },
  { exact λ _, is_closed_closure },
end

lemma nonempty_omega_limit [compact_space β] [ne_bot f] (hs : s.nonempty) :
  (ω f ϕ s).nonempty :=
nonempty_omega_limit_of_is_compact_absorbing _ _ _
  compact_univ ⟨univ, univ_mem, subset_univ _⟩ hs

end omega_limit

/-!
### ω-limits of Flows by a Monoid
-/

namespace flow

variables
{τ : Type*} [topological_space τ] [add_monoid τ] [has_continuous_add τ]
{α : Type*} [topological_space α]
(f : filter τ) (ϕ : flow τ α) (s : set α)

open_locale omega_limit

lemma is_invariant_omega_limit (hf : ∀ t, tendsto ((+) t) f f) :
  is_invariant ϕ (ω f ϕ s) :=
λ t, maps_to.mono (subset.refl _) (omega_limit_subset_of_tendsto ϕ s (hf t)) $
  maps_to_omega_limit _ (maps_to_id _) (λ t' x, (ϕ.map_add _ _ _).symm)
    (continuous_const.flow ϕ continuous_id)

lemma omega_limit_image_subset (t : τ) (ht : tendsto (+ t) f f) :
  ω f ϕ (ϕ t '' s) ⊆ ω f ϕ s :=
begin
  simp only [omega_limit_image_eq, ← map_add],
  exact omega_limit_subset_of_tendsto ϕ s ht
end

end flow

/-!
### ω-limits of Flows by a Group
-/

namespace flow

variables
{τ : Type*} [topological_space τ] [add_comm_group τ] [topological_add_group τ]
{α : Type*} [topological_space α]
(f : filter τ) (ϕ : flow τ α) (s : set α)

open_locale omega_limit

/-- the ω-limit of a forward image of `s` is the same as the ω-limit of `s`. -/
@[simp] lemma omega_limit_image_eq (hf : ∀ t, tendsto (+ t) f f) (t : τ) :
  ω f ϕ (ϕ t '' s) = ω f ϕ s :=
subset.antisymm (omega_limit_image_subset _ _ _ _ (hf t)) $
calc ω f ϕ s = ω f ϕ (ϕ (-t) '' (ϕ t '' s)) : by simp [image_image, ← map_add]
... ⊆ ω f ϕ (ϕ t '' s) : omega_limit_image_subset _ _ _ _ (hf _)

lemma omega_limit_omega_limit (hf : ∀ t, tendsto ((+) t) f f) :
  ω f ϕ (ω f ϕ s) ⊆ ω f ϕ s :=
begin
  simp only [subset_def, mem_omega_limit_iff_frequently₂, frequently_iff],
  intros _ h,
  rintro n hn u hu,
  rcases mem_nhds_iff.mp hn with ⟨o, ho₁, ho₂, ho₃⟩,
  rcases h o (is_open.mem_nhds ho₂ ho₃) hu with ⟨t, ht₁, ht₂⟩,
  have l₁ : (ω f ϕ s ∩ o).nonempty, from
    ht₂.mono (inter_subset_inter_left _
      ((is_invariant_iff_image _ _).mp (is_invariant_omega_limit _ _ _ hf) _)),
  have l₂ : ((closure (image2 ϕ u s)) ∩ o).nonempty :=
    l₁.mono (λ b hb, ⟨omega_limit_subset_closure_fw_image _ _ _ hu hb.1, hb.2⟩),
  have l₃ : (o ∩ image2 ϕ u s).nonempty,
  begin
    rcases l₂ with ⟨b, hb₁, hb₂⟩,
    exact mem_closure_iff_nhds.mp hb₁ o (is_open.mem_nhds ho₂ hb₂)
  end,
  rcases l₃ with ⟨ϕra, ho, ⟨_, _, hr, ha, hϕra⟩⟩,
  exact ⟨_, hr, ϕra, ⟨_, ha, hϕra⟩, ho₁ ho⟩,
end

end flow


/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Violeta Hernández Palacios, Grayson Burton
-/
import data.set.intervals.ord_connected

/-!
# The covering relation

This file defines the covering relation in an order. `b` is said to cover `a` if `a < b` and there
is no element in between. ∃ b, a < b

## Notation

`a ⋖ b` means that `b` covers `a`.
-/

open set

variables {α β : Type*}

section has_lt
variables [has_lt α] {a b : α}

/-- `covby a b` means that `b` covers `a`: `a < b` and there is no element in between. -/
def covby (a b : α) : Prop := a < b ∧ ∀ ⦃c⦄, a < c → ¬ c < b

infix ` ⋖ `:50 := covby

lemma covby.lt (h : a ⋖ b) : a < b := h.1

/-- If `a < b`, then `b` does not cover `a` iff there's an element in between. -/
lemma not_covby_iff (h : a < b) : ¬a ⋖ b ↔ ∃ c, a < c ∧ c < b :=
by { simp_rw [covby, not_and, not_forall, exists_prop, not_not], exact imp_iff_right h }

alias not_covby_iff ↔ exists_lt_lt_of_not_covby _
alias exists_lt_lt_of_not_covby ← has_lt.lt.exists_lt_lt

/-- In a dense order, nothing covers anything. -/
lemma not_covby [densely_ordered α] : ¬ a ⋖ b :=
λ h, let ⟨c, hc⟩ := exists_between h.1 in h.2 hc.1 hc.2

lemma densely_ordered_iff_forall_not_covby : densely_ordered α ↔ ∀ a b : α, ¬ a ⋖ b :=
⟨λ h a b, @not_covby _ _ _ _ h, λ h, ⟨λ a b hab, exists_lt_lt_of_not_covby hab $ h _ _⟩⟩

open order_dual

@[simp] lemma to_dual_covby_to_dual_iff : to_dual b ⋖ to_dual a ↔ a ⋖ b :=
and_congr_right' $ forall_congr $ λ c, forall_swap

@[simp] lemma of_dual_covby_of_dual_iff {a b : order_dual α} :
  of_dual a ⋖ of_dual b ↔ b ⋖ a :=
and_congr_right' $ forall_congr $ λ c, forall_swap

alias to_dual_covby_to_dual_iff ↔ _ covby.to_dual
alias of_dual_covby_of_dual_iff ↔ _ covby.of_dual

end has_lt

section preorder
variables [preorder α] [preorder β] {a b : α} {f : α ↪o β} {e : α ≃o β}

lemma covby.le (h : a ⋖ b) : a ≤ b := h.1.le
protected lemma covby.ne (h : a ⋖ b) : a ≠ b := h.lt.ne
lemma covby.ne' (h : a ⋖ b) : b ≠ a := h.lt.ne'

instance covby.is_irrefl : is_irrefl α (⋖) := ⟨λ a ha, ha.ne rfl⟩

lemma covby.Ioo_eq (h : a ⋖ b) : Ioo a b = ∅ :=
eq_empty_iff_forall_not_mem.2 $ λ x hx, h.2 hx.1 hx.2

lemma covby.of_image (h : f a ⋖ f b) : a ⋖ b :=
begin
  refine ⟨_, λ c hac hcb, _⟩,
  { rw ←order_embedding.lt_iff_lt f,
    exact h.1 },
  rw ←order_embedding.lt_iff_lt f at hac hcb,
  exact h.2 hac hcb,
end

lemma covby.image (hab : a ⋖ b) (h : (set.range f).ord_connected) : f a ⋖ f b :=
begin
  refine ⟨f.strict_mono hab.1, λ c ha hb, _⟩,
  obtain ⟨c, rfl⟩ := h.out (mem_range_self _) (mem_range_self _) ⟨ha.le, hb.le⟩,
  rw f.lt_iff_lt at ha hb,
  exact hab.2 ha hb,
end

protected lemma set.ord_connected.image_covby_image_iff (h : (set.range f).ord_connected) :
  f a ⋖ f b ↔ a ⋖ b :=
⟨covby.of_image, λ hab, hab.image h⟩

@[simp] lemma apply_covby_apply_iff : e a ⋖ e b ↔ a ⋖ b :=
⟨covby.of_image, λ hab, begin
  refine ⟨e.strict_mono hab.1, λ c ha hb, _⟩,
  rw [←e.symm.lt_iff_lt, order_iso.symm_apply_apply] at ha hb,
  exact hab.2 ha hb,
end⟩

end preorder

section partial_order
variables [partial_order α] {a b : α}

lemma covby.Ico_eq (h : a ⋖ b) : Ico a b = {a} :=
by rw [←set.Ioo_union_left h.lt, h.Ioo_eq, empty_union]

lemma covby.Ioc_eq (h : a ⋖ b) : Ioc a b = {b} :=
by rw [←set.Ioo_union_right h.lt, h.Ioo_eq, empty_union]

lemma covby.Icc_eq (h : a ⋖ b) : Icc a b = {a, b} :=
by { rw [←set.Ico_union_right h.le, h.Ico_eq], refl }

end partial_order