/-
Copyright (c) Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel

! This file was ported from Lean 3 source module category_theory.limits.shapes.equivalence
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Adjunction.Limits
import Mathbin.CategoryTheory.Limits.Shapes.Terminal

/-!
# Transporting existence of specific limits across equivalences

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

For now, we only treat the case of initial and terminal objects, but other special shapes can be
added in the future.
-/

open CategoryTheory CategoryTheory.Limits

namespace CategoryTheory

universe v₁ v₂ u₁ u₂

variable {C : Type u₁} [Category.{v₁} C] {D : Type u₂} [Category.{v₂} D]

#print CategoryTheory.hasInitial_of_equivalence /-
theorem hasInitial_of_equivalence (e : D ⥤ C) [IsEquivalence e] [HasInitial C] : HasInitial D :=
  Adjunction.hasColimitsOfShape_of_equivalence e
#align category_theory.has_initial_of_equivalence CategoryTheory.hasInitial_of_equivalence
-/

/- warning: category_theory.equivalence.has_initial_iff -> CategoryTheory.Equivalence.hasInitial_iff is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C _inst_1 D _inst_2) -> (Iff (CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1) (CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C D _inst_1 _inst_2) -> (Iff (CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1) (CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2))
Case conversion may be inaccurate. Consider using '#align category_theory.equivalence.has_initial_iff CategoryTheory.Equivalence.hasInitial_iffₓ'. -/
theorem Equivalence.hasInitial_iff (e : C ≌ D) : HasInitial C ↔ HasInitial D :=
  ⟨fun h => has_initial_of_equivalence e.inverse, fun h => has_initial_of_equivalence e.functor⟩
#align category_theory.equivalence.has_initial_iff CategoryTheory.Equivalence.hasInitial_iff

#print CategoryTheory.hasTerminal_of_equivalence /-
theorem hasTerminal_of_equivalence (e : D ⥤ C) [IsEquivalence e] [HasTerminal C] : HasTerminal D :=
  Adjunction.hasLimitsOfShape_of_equivalence e
#align category_theory.has_terminal_of_equivalence CategoryTheory.hasTerminal_of_equivalence
-/

/- warning: category_theory.equivalence.has_terminal_iff -> CategoryTheory.Equivalence.hasTerminal_iff is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C _inst_1 D _inst_2) -> (Iff (CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1) (CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C D _inst_1 _inst_2) -> (Iff (CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1) (CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2))
Case conversion may be inaccurate. Consider using '#align category_theory.equivalence.has_terminal_iff CategoryTheory.Equivalence.hasTerminal_iffₓ'. -/
theorem Equivalence.hasTerminal_iff (e : C ≌ D) : HasTerminal C ↔ HasTerminal D :=
  ⟨fun h => has_terminal_of_equivalence e.inverse, fun h => has_terminal_of_equivalence e.functor⟩
#align category_theory.equivalence.has_terminal_iff CategoryTheory.Equivalence.hasTerminal_iff

end CategoryTheory


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module algebra.category.Mon.colimits
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Category.Mon.Basic
import Mathbin.CategoryTheory.Limits.HasLimits
import Mathbin.CategoryTheory.ConcreteCategory.Elementwise

/-!
# The category of monoids has all colimits.

We do this construction knowing nothing about monoids.
In particular, I want to claim that this file could be produced by a python script
that just looks at the output of `#print monoid`:

  -- structure monoid : Type u → Type u
  -- fields:
  -- monoid.mul : Π {α : Type u} [c : monoid α], α → α → α
  -- monoid.mul_assoc : ∀ {α : Type u} [c : monoid α] (a b c_1 : α), a * b * c_1 = a * (b * c_1)
  -- monoid.one : Π (α : Type u) [c : monoid α], α
  -- monoid.one_mul : ∀ {α : Type u} [c : monoid α] (a : α), 1 * a = a
  -- monoid.mul_one : ∀ {α : Type u} [c : monoid α] (a : α), a * 1 = a

and if we'd fed it the output of `#print comm_ring`, this file would instead build
colimits of commutative rings.

A slightly bolder claim is that we could do this with tactics, as well.
-/

universe v

open CategoryTheory

open CategoryTheory.Limits

namespace MonCat.Colimits

/-!
We build the colimit of a diagram in `Mon` by constructing the
free monoid on the disjoint union of all the monoids in the diagram,
then taking the quotient by the monoid laws within each monoid,
and the identifications given by the morphisms in the diagram.
-/

variable {J : Type v} [SmallCategory J] (F : J ⥤ MonCat.{v})

/-- An inductive type representing all monoid expressions (without relations)
on a collection of types indexed by the objects of `J`.
-/
inductive Prequotient-- There's always `of`

  | of : ∀ (j : J) (x : F.obj j), prequotient-- Then one generator for each operation

  | one : prequotient
  | mul : prequotient → prequotient → prequotient
#align Mon.colimits.prequotient MonCat.Colimits.Prequotient

instance : Inhabited (Prequotient F) :=
  ⟨Prequotient.one⟩

open Prequotient

/-- The relation on `prequotient` saying when two expressions are equal
because of the monoid laws, or
because one element is mapped to another by a morphism in the diagram.
-/
inductive Relation : Prequotient F → Prequotient F → Prop-- Make it an equivalence relation:

  | refl : ∀ x, relation x x
  | symm : ∀ (x y) (h : relation x y), relation y x
  |
  trans :
    ∀ (x y z) (h : relation x y) (k : relation y z), relation x z-- There's always a `map` relation

  |
  map :
    ∀ (j j' : J) (f : j ⟶ j') (x : F.obj j),
      relation (of j' ((F.map f) x))
        (of j x)-- Then one relation per operation, describing the interaction with `of`

  | mul : ∀ (j) (x y : F.obj j), relation (of j (x * y)) (mul (of j x) (of j y))
  | one : ∀ j, relation (of j 1) one-- Then one relation per argument of each operation

  | mul_1 : ∀ (x x' y) (r : relation x x'), relation (mul x y) (mul x' y)
  |
  mul_2 : ∀ (x y y') (r : relation y y'), relation (mul x y) (mul x y')-- And one relation per axiom

  | mul_assoc : ∀ x y z, relation (mul (mul x y) z) (mul x (mul y z))
  | one_mul : ∀ x, relation (mul one x) x
  | mul_one : ∀ x, relation (mul x one) x
#align Mon.colimits.relation MonCat.Colimits.Relation

/-- The setoid corresponding to monoid expressions modulo monoid relations and identifications.
-/
def colimitSetoid : Setoid (Prequotient F)
    where
  R := Relation F
  iseqv := ⟨Relation.refl, Relation.symm, Relation.trans⟩
#align Mon.colimits.colimit_setoid MonCat.Colimits.colimitSetoid

attribute [instance] colimit_setoid

/-- The underlying type of the colimit of a diagram in `Mon`.
-/
def ColimitType : Type v :=
  Quotient (colimitSetoid F)deriving Inhabited
#align Mon.colimits.colimit_type MonCat.Colimits.ColimitType

instance monoidColimitType : Monoid (ColimitType F)
    where
  mul := by
    fapply @Quot.lift _ _ (colimit_type F → colimit_type F)
    · intro x
      fapply @Quot.lift
      · intro y
        exact Quot.mk _ (mul x y)
      · intro y y' r
        apply Quot.sound
        exact relation.mul_2 _ _ _ r
    · intro x x' r
      funext y
      induction y
      dsimp
      apply Quot.sound
      · exact relation.mul_1 _ _ _ r
      · rfl
  one := Quot.mk _ one
  mul_assoc x y z := by
    induction x
    induction y
    induction z
    dsimp
    apply Quot.sound
    apply relation.mul_assoc
    rfl
    rfl
    rfl
  one_mul x := by
    induction x
    dsimp
    apply Quot.sound
    apply relation.one_mul
    rfl
  mul_one x := by
    induction x
    dsimp
    apply Quot.sound
    apply relation.mul_one
    rfl
#align Mon.colimits.monoid_colimit_type MonCat.Colimits.monoidColimitType

@[simp]
theorem quot_one : Quot.mk Setoid.r one = (1 : ColimitType F) :=
  rfl
#align Mon.colimits.quot_one MonCat.Colimits.quot_one

@[simp]
theorem quot_mul (x y) :
    Quot.mk Setoid.r (mul x y) = (Quot.mk Setoid.r x * Quot.mk Setoid.r y : ColimitType F) :=
  rfl
#align Mon.colimits.quot_mul MonCat.Colimits.quot_mul

/-- The bundled monoid giving the colimit of a diagram. -/
def colimit : MonCat :=
  ⟨ColimitType F, by infer_instance⟩
#align Mon.colimits.colimit MonCat.Colimits.colimit

/-- The function from a given monoid in the diagram to the colimit monoid. -/
def coconeFun (j : J) (x : F.obj j) : ColimitType F :=
  Quot.mk _ (of j x)
#align Mon.colimits.cocone_fun MonCat.Colimits.coconeFun

/-- The monoid homomorphism from a given monoid in the diagram to the colimit monoid. -/
def coconeMorphism (j : J) : F.obj j ⟶ colimit F
    where
  toFun := coconeFun F j
  map_one' := Quot.sound (Relation.one _)
  map_mul' x y := Quot.sound (Relation.mul _ _ _)
#align Mon.colimits.cocone_morphism MonCat.Colimits.coconeMorphism

@[simp]
theorem cocone_naturality {j j' : J} (f : j ⟶ j') :
    F.map f ≫ coconeMorphism F j' = coconeMorphism F j :=
  by
  ext
  apply Quot.sound
  apply Relation.Map
#align Mon.colimits.cocone_naturality MonCat.Colimits.cocone_naturality

@[simp]
theorem cocone_naturality_components (j j' : J) (f : j ⟶ j') (x : F.obj j) :
    (coconeMorphism F j') (F.map f x) = (coconeMorphism F j) x :=
  by
  rw [← cocone_naturality F f]
  rfl
#align Mon.colimits.cocone_naturality_components MonCat.Colimits.cocone_naturality_components

/-- The cocone over the proposed colimit monoid. -/
def colimitCocone : Cocone F where
  pt := colimit F
  ι := { app := coconeMorphism F }
#align Mon.colimits.colimit_cocone MonCat.Colimits.colimitCocone

/-- The function from the free monoid on the diagram to the cone point of any other cocone. -/
@[simp]
def descFunLift (s : Cocone F) : Prequotient F → s.pt
  | of j x => (s.ι.app j) x
  | one => 1
  | mul x y => desc_fun_lift x * desc_fun_lift y
#align Mon.colimits.desc_fun_lift MonCat.Colimits.descFunLift

/-- The function from the colimit monoid to the cone point of any other cocone. -/
def descFun (s : Cocone F) : ColimitType F → s.pt :=
  by
  fapply Quot.lift
  · exact desc_fun_lift F s
  · intro x y r
    induction r <;> try dsimp
    -- refl
    · rfl
    -- symm
    · exact r_ih.symm
    -- trans
    · exact Eq.trans r_ih_h r_ih_k
    -- map
    · simp
    -- mul
    · simp
    -- one
    · simp
    -- mul_1
    · rw [r_ih]
    -- mul_2
    · rw [r_ih]
    -- mul_assoc
    · rw [mul_assoc]
    -- one_mul
    · rw [one_mul]
    -- mul_one
    · rw [mul_one]
#align Mon.colimits.desc_fun MonCat.Colimits.descFun

/-- The monoid homomorphism from the colimit monoid to the cone point of any other cocone. -/
def descMorphism (s : Cocone F) : colimit F ⟶ s.pt
    where
  toFun := descFun F s
  map_one' := rfl
  map_mul' x y := by induction x <;> induction y <;> rfl
#align Mon.colimits.desc_morphism MonCat.Colimits.descMorphism

/-- Evidence that the proposed colimit is the colimit. -/
def colimitIsColimit : IsColimit (colimitCocone F)
    where
  desc s := descMorphism F s
  uniq s m w := by
    ext
    induction x
    induction x
    · have w' :=
        congr_fun (congr_arg (fun f : F.obj x_j ⟶ s.X => (f : F.obj x_j → s.X)) (w x_j)) x_x
      erw [w']
      rfl
    · simp [*]
    · simp [*]
    rfl
#align Mon.colimits.colimit_is_colimit MonCat.Colimits.colimitIsColimit

instance hasColimits_monCat : HasColimits MonCat
    where HasColimitsOfShape J 𝒥 :=
    {
      HasColimit := fun F =>
        has_colimit.mk
          { Cocone := colimit_cocone F
            IsColimit := colimit_is_colimit F } }
#align Mon.colimits.has_colimits_Mon MonCat.Colimits.hasColimits_monCat

end MonCat.Colimits


-- § 4.7
section

variables A B C D : Prop

example : A ∧ (A → B) → B := λ ⟨a, f⟩, f a

example : A → ¬(¬A ∧ B) := λ a ⟨f, b⟩, f a

example : ¬ (A ∧ B) → (A → ¬B) := λ f a b, f ⟨a, b⟩

example (h₁ : A ∨ B) (h₂ : A → C) (h₃ : B → D) : C ∨ D := h₁.cases_on (or.inl ∘ h₂) (or.inr ∘ h₃)

example (h : ¬A ∧ ¬B) : ¬(A ∨ B) := let ⟨f, g⟩ := h in λ ab, ab.cases_on f g

lemma t : ¬ (A ↔ ¬A) := λ ⟨f, g⟩, let a := g (λ a, f a a) in f a a

end

-- § 5.3
section

variables {A B C D : Prop}

example : (A ∨ ¬A) → (¬¬A → A) := λ d f, d.cases_on id (false.rec _ ∘ f)

variable em : ∀ {p : Prop}, p ∨ ¬p
variable by_contradiction : ∀ {p : Prop}, ¬¬p → p

example : ¬A ∨ ¬B → ¬(A ∧ B) := λ d ⟨a, b⟩, d.cases_on (λ f, f a) (λ f, f b)

example : ¬(A ∧ B) → ¬A ∨ ¬B := λ f, em.cases_on (λ a, or.inr (λ b, f ⟨a, b⟩)) (λ f, or.inl (λ a, f a))

example : (A → B) → ¬A ∨ B := λ f, em.cases_on (or.inr ∘ f) or.inl

lemma step1 (h₁ : ¬(A ∧ B)) (h₂ : A) : ¬A ∨ ¬B :=
have ¬B, from λ b, h₁ ⟨h₂, b⟩,
show ¬A ∨ ¬B, from or.inr this

lemma step2 (h₁ : ¬(A ∧ B)) (h₂ : ¬(¬A ∨ ¬B)) : false :=
have ¬A, from
assume : A,
have ¬A ∨ ¬B, from (step1 h₁ ‹A›),
show false, from h₂ this,
show false, from h₂ (or.inl ‹¬A›)

theorem step3 (h : ¬(A ∧ B)) : ¬A ∨ ¬B :=
  by_contradiction
    (assume h' : ¬(¬A ∨ ¬B),
    show false, from step2 h h')

example (h : ¬B → ¬A) : A → B := λ a, by_contradiction (λ f, h f a)

example (h : A → B) : ¬A ∨ B := em.cases_on (or.inr ∘ h) or.inl

end

-- § 8.6
section

variables {T : Type} {A B C : T → Prop} {even odd : T → Prop} {s : T → T}

example : (∀ x, A x → B x) → ((∀ x, A x) → (∀ x, B x)) := λ f g x, f x (g x)

example : (∀ x, A x ∨ B x) → (∀ y, ¬(A y)) → (∀ x, B x) := λ f g x, (f x).cases_on (false.rec _ ∘ g x) id

example (h₁ : ∀ x, even x ∨ odd x) (h₂ : ∀ x, odd x → even (s x)) : ∀ x,  even x ∨ even (s x) :=
  λ x, (h₁ x).cases_on or.inl (or.inr ∘ h₂ x)

example : (∃ x, A x) ∨ (∃ x, B x) → ∃ x, (A x ∨ B x) :=
  λ d, d.cases_on (λ ⟨x, p⟩, (⟨x, or.inl p⟩ : ∃ x, (A x ∨ B x))) (λ ⟨x, p⟩, ⟨x, or.inr p⟩)

example (h₁ : ∃ x, A x ∧ B x) (h₂ : ∀ x, A x ∧ B x → C x) : (∃ x, A x ∧ C x) :=
  let ⟨x, ⟨a, b⟩⟩ := h₁ in ⟨x, ⟨a, h₂ x ⟨a, b⟩⟩⟩

variables {politician : T → Prop} {trusts : T → T → Prop}

example : (∀ x y, politician x → ¬(trusts y x)) ↔ (∀ x y, trusts y x → ¬(politician x)) :=
  ⟨λ f x y t p, f x y p t, λ f x y p t, f x y t p⟩

variables {person : Type} {young healthy likes_baseball active : person → Prop}

example
  (h₁ : ∀ p, young p ∧ healthy p → likes_baseball p)
  (h₂ : ∀ p, active p → healthy p)
  (h₃ : ∃ p, young p ∧ active p)
  : ∃ p, likes_baseball p :=
  let ⟨p, ⟨y, a⟩⟩ := h₃ in ⟨p, h₁ p ⟨y, h₂ p a⟩⟩

end

section
variables {T : Type} {f : T → T} {P : T → Prop}
variable {refl : ∀ {t : T}, t = t}
variable {symm : ∀ {s t : T}, s = t → t = s}
variable {trans : ∀ {r s t : T}, r = s → s = t → r = t}
variable {congr : ∀ {s t : T}, s = t → f s = f t}
variable {subst : ∀ {s t : T}, s = t → P s → P t}

example : ∀ {x y z : T}, x = z → y = z → x = y :=
  λ x y z xz yz, trans xz (symm yz)
end

section
  variables {T : Type}
  example
    (h₁ : ∀ (x : T), x = x)
    (h₂ : ∀ (u v w : T), u = w → v = w → u = v)
    : ∀ x y, (x = y → y = x) :=
    λ x y xy, h₂ _ _ _ (h₁ y) xy
end

section
  variables {T: Type} {x : T} {A B : T → Prop}

  example : ¬(∃x, A x ∧ B x) ↔ (∀x, A x → ¬(B x)) := ⟨λ f x a b, (f ⟨x, ⟨a, b⟩⟩).elim, λ f ⟨x, ⟨a, b⟩⟩, f x a b⟩

  open classical

  example : ¬(∀x, A x) → ∃x, ¬(A x) :=
    λ f, by_contradiction (λ g, f (λ x, by_contradiction (λ h, g ⟨x, h⟩)))

  example : ¬(∀x, A x → B x) ↔ (∃x, A x ∧ ¬(B x)) :=
    ⟨λ f, by_contradiction (λ g, f (λ x a, (by_contradiction (λ h, g ⟨x, ⟨a, h⟩⟩) : B x))),
     λ ⟨x, ⟨a, f⟩⟩ g, (f (g x a)).elim⟩

variables {f : T → T} {P : T → Prop}
variable {refl : ∀ {t : T}, t = t}
variable {symm : ∀ {s t : T}, s = t → t = s}
variable {trans : ∀ {r s t : T}, r = s → s = t → r = t}
variable {congr : ∀ {s t : T}, s = t → f s = f t}
variable {subst : ∀ {s t : T}, s = t → P s → P t}

def L (P : T → Prop) := ∃ (x : T), P x ∧ ∀ y, P y → y = x
def R (P : T → Prop) := ∃ (x : T), P x ∧ ∀ y y', P y ∧ P y' → y = y'

example : L A → R A :=
  λ ⟨x, ax, f⟩, ⟨x, ax, (λ y y' ⟨ay, ay'⟩, trans (f y ay) (symm (f y' ay')))⟩

example : R A → L A :=
  λ ⟨x, ax, f⟩, ⟨x, ax, λ y ay, f y x ⟨ay, ax⟩⟩
end

-- § 9.5
section
  variable A : Type
  variable f : A → A
  variable P : A → Prop
  variable h : ∀ x, P x → P (f x)
  example : ∀ y, P y → P (f (f y)) :=
    λ _ py, h _ (h _ py).
end

section
  variable U : Type
  variables A B : U → Prop
  example : (∀ x, A x ∧ B x) → ∀ x, A x :=
    λ f x, (f x).elim_left
end

section
variable U : Type
variables A B C : U → Prop
variable h1 : ∀ x, A x ∨ B x
variable h2 : ∀ x, A x → C x
variable h3 : ∀ x, B x → C x
example : ∀ x, C x :=
  λ x, (h1 x).elim (h2 x) (h3 x)
end

section
variable not_iff_not_self (P : Prop) : ¬ (P ↔ ¬ P)
variable Person : Type
variable shaves : Person → Person → Prop
variable barber : Person
variable h : ∀ x, shaves barber x ↔ ¬ shaves x x
example : false :=
  not_iff_not_self _ (h barber)
end

section
variable U : Type
variables A B : U → Prop
example : (∃ x, A x) → ∃ x, A x ∨ B x :=
  λ ⟨x, a⟩, ⟨x, or.inl a⟩
end

section
variable U : Type
variables A B : U → Prop
variable h1 : ∀ x, A x → B x
variable h2 : ∃ x, A x
example : ∃ x, B x :=
  let ⟨x, a⟩ := h2 in ⟨x, h1 x a⟩
end

section
variable U : Type
variables A B C : U → Prop
example (h1 : ∃ x, A x ∧ B x) (h2 : ∀ x, B x → C x) : ∃ x, A x ∧ C x :=
  let ⟨x, a, b⟩ := h1 in ⟨x, a, h2 x b⟩
end

section
variable U : Type
variables A B C : U → Prop
example : (¬ ∃ x, A x) → ∀ x, ¬ A x :=
  λ f x a, f ⟨x, a⟩
example : (∀ x, ¬ A x) → ¬ ∃ x, A x :=
  λ f ⟨x, a⟩, f x a
end

section
variable U : Type
variables R : U → U → Prop
example : (∃ x, ∀ y, R x y) → ∀ y, ∃ x, R x y :=
  λ ⟨x, f⟩ y, ⟨x, f y⟩
end

section
theorem foo {A : Type} {a b c : A} : a = b → c = b → a = c :=
  λ h₁ h₂, eq.symm h₂ ▸ h₁

section
variable {A : Type}
variables {a b c : A}
-- replace the sorry with a proof, using foo and rfl, without using eq.symm.
theorem my_symm (h : b = a) : a = b := foo rfl h
-- now use foo, rfl, and my_symm to prove transitivity
theorem my_trans (h1 : a = b) (h2 : b = c) : a = c := foo h1 (my_symm h2)
end
end

-- § 10.6
section
variables {T : Type} (R : T → T → Prop)
def has_min := ∃x, ∀y, R x y
def has_max := ∃y, ∀x, R x y
def has_between := ∀ x y, R x y ∧ x ≠ y → ∃ z, R x z ∧ R z y ∧ x ≠ z ∧ y ≠ z

#print nat.less_than_or_equal

lemma not_succ_le {x : ℕ} : ¬(nat.succ x ≤ x) :=
  λ le, begin
  induction x,
  { cases le },
  { exact x_ih (nat.pred_le_pred le) }
end

#check nat.less_than_or_equal.rec_on

universe u

def double : ℕ → ℕ := nat.rec 0 (λ (n m : ℕ), nat.succ (nat.succ m))

#reduce double 7

lemma nat_le_rec_on
  {a b : ℕ} {T : Type} {P : ℕ → Prop} (le : a ≤ b) (x : T) :
  (T → P a) → (T → ∀ ⦃b : ℕ⦄, nat.less_than_or_equal a b → P b → P (nat.succ b)) → P b :=
  λ h₁ h₂, nat.less_than_or_equal.rec_on le (h₁ x) (h₂ x)

lemma not_succ_le' {x : ℕ} : ¬(nat.succ x ≤ x) :=
  λ le, nat_le_rec_on le x (λ x, _) (λ b le' x, _)

example : has_min nat.le := ⟨0, λ x, x.rec_on (nat.less_than_or_equal.refl _) (λ n h, nat.less_than_or_equal.step h)⟩
example : ¬has_max nat.le := λ ⟨y, f⟩,  not_succ_le (f (nat.succ y))
end


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro

! This file was ported from Lean 3 source module topology.bases
! leanprover-community/mathlib commit bcfa726826abd57587355b4b5b7e78ad6527b7e4
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.Constructions
import Mathlib.Topology.ContinuousOn

/-!
# Bases of topologies. Countability axioms.

A topological basis on a topological space `t` is a collection of sets,
such that all open sets can be generated as unions of these sets, without the need to take
finite intersections of them. This file introduces a framework for dealing with these collections,
and also what more we can say under certain countability conditions on bases,
which are referred to as first- and second-countable.
We also briefly cover the theory of separable spaces, which are those with a countable, dense
subset. If a space is second-countable, and also has a countably generated uniformity filter
(for example, if `t` is a metric space), it will automatically be separable (and indeed, these
conditions are equivalent in this case).

## Main definitions

* `TopologicalSpace.IsTopologicalBasis s`: The topological space `t` has basis `s`.
* `TopologicalSpace.SeparableSpace α`: The topological space `t` has a countable, dense subset.
* `TopologicalSpace.IsSeparable s`: The set `s` is contained in the closure of a countable set.
* `TopologicalSpace.FirstCountableTopology α`: A topology in which `𝓝 x` is countably generated for
  every `x`.
* `TopologicalSpace.SecondCountableTopology α`: A topology which has a topological basis which is
  countable.

## Main results

* `TopologicalSpace.FirstCountableTopology.tendsto_subseq`: In a first-countable space,
  cluster points are limits of subsequences.
* `TopologicalSpace.SecondCountableTopology.isOpen_unionᵢ_countable`: In a second-countable space,
  the union of arbitrarily-many open sets is equal to a sub-union of only countably many of these
  sets.
* `TopologicalSpace.SecondCountableTopology.countable_cover_nhds`: Consider `f : α → Set α` with the
  property that `f x ∈ 𝓝 x` for all `x`. Then there is some countable set `s` whose image covers the
  space.

## Implementation Notes
For our applications we are interested that there exists a countable basis, but we do not need the
concrete basis itself. This allows us to declare these type classes as `Prop` to use them as mixins.

### TODO:

More fine grained instances for `TopologicalSpace.FirstCountableTopology`,
`TopologicalSpace.SeparableSpace`, and more.
-/

open Set Filter Function Topology

noncomputable section

namespace TopologicalSpace

universe u

variable {α : Type u} [t : TopologicalSpace α]

/-- A topological basis is one that satisfies the necessary conditions so that
  it suffices to take unions of the basis sets to get a topology (without taking
  finite intersections as well). -/
structure IsTopologicalBasis (s : Set (Set α)) : Prop where
  /-- For every point `x`, the set of `t ∈ s` such that `x ∈ t` is directed downwards.  -/
  exists_subset_inter : ∀ t₁ ∈ s, ∀ t₂ ∈ s, ∀ x ∈ t₁ ∩ t₂, ∃ t₃ ∈ s, x ∈ t₃ ∧ t₃ ⊆ t₁ ∩ t₂
  /-- The sets from `s` cover the whole space. -/
  unionₛ_eq : ⋃₀ s = univ
  /-- The topology is generated by sets from `s`. -/
  eq_generateFrom : t = generateFrom s
#align topological_space.is_topological_basis TopologicalSpace.IsTopologicalBasis

theorem IsTopologicalBasis.insert_empty {s : Set (Set α)} (h : IsTopologicalBasis s) :
    IsTopologicalBasis (insert ∅ s) := by
  refine' ⟨_, by rw [unionₛ_insert, empty_union, h.unionₛ_eq], _⟩
  · rintro t₁ (rfl | h₁) t₂ (rfl | h₂) x ⟨hx₁, hx₂⟩
    · cases hx₁
    · cases hx₁
    · cases hx₂
    · obtain ⟨t₃, h₃, hs⟩ := h.exists_subset_inter _ h₁ _ h₂ x ⟨hx₁, hx₂⟩
      exact ⟨t₃, .inr h₃, hs⟩
  · rw [h.eq_generateFrom]
    refine' le_antisymm (le_generateFrom fun t => _) (generateFrom_anti <| subset_insert ∅ s)
    rintro (rfl | ht)
    · exact @isOpen_empty _ (generateFrom s)
    · exact .basic t ht
#align topological_space.is_topological_basis.insert_empty TopologicalSpace.IsTopologicalBasis.insert_empty

theorem IsTopologicalBasis.diff_empty {s : Set (Set α)} (h : IsTopologicalBasis s) :
    IsTopologicalBasis (s \ {∅}) := by
  refine' ⟨_, by rw [unionₛ_diff_singleton_empty, h.unionₛ_eq], _⟩
  · rintro t₁ ⟨h₁, -⟩ t₂ ⟨h₂, -⟩ x hx
    obtain ⟨t₃, h₃, hs⟩ := h.exists_subset_inter _ h₁ _ h₂ x hx
    exact ⟨t₃, ⟨h₃, Nonempty.ne_empty ⟨x, hs.1⟩⟩, hs⟩
  · rw [h.eq_generateFrom]
    refine' le_antisymm (generateFrom_anti <| diff_subset s _) (le_generateFrom fun t ht => _)
    obtain rfl | he := eq_or_ne t ∅
    · exact @isOpen_empty _ (generateFrom _)
    · exact .basic t ⟨ht, he⟩
#align topological_space.is_topological_basis.diff_empty TopologicalSpace.IsTopologicalBasis.diff_empty

/-- If a family of sets `s` generates the topology, then intersections of finite
subcollections of `s` form a topological basis. -/
theorem isTopologicalBasis_of_subbasis {s : Set (Set α)} (hs : t = generateFrom s) :
    IsTopologicalBasis ((fun f => ⋂₀ f) '' { f : Set (Set α) | f.Finite ∧ f ⊆ s }) := by
  subst t; letI := generateFrom s
  refine' ⟨_, _, le_antisymm (le_generateFrom _) <| generateFrom_anti fun t ht => _⟩
  · rintro _ ⟨t₁, ⟨hft₁, ht₁b⟩, rfl⟩ _ ⟨t₂, ⟨hft₂, ht₂b⟩, rfl⟩ x h
    exact ⟨_, ⟨_, ⟨hft₁.union hft₂, union_subset ht₁b ht₂b⟩, interₛ_union t₁ t₂⟩, h, Subset.rfl⟩
  · rw [unionₛ_image, unionᵢ₂_eq_univ_iff]
    exact fun x => ⟨∅, ⟨finite_empty, empty_subset _⟩, interₛ_empty.substr <| mem_univ x⟩
  · rintro _ ⟨t, ⟨hft, htb⟩, rfl⟩
    apply isOpen_interₛ
    exacts [hft, fun s hs => GenerateOpen.basic _ <| htb hs]
  · rw [← interₛ_singleton t]
    exact ⟨{t}, ⟨finite_singleton t, singleton_subset_iff.2 ht⟩, rfl⟩
#align topological_space.is_topological_basis_of_subbasis TopologicalSpace.isTopologicalBasis_of_subbasis

/-- If a family of open sets `s` is such that every open neighbourhood contains some
member of `s`, then `s` is a topological basis. -/
theorem isTopologicalBasis_of_open_of_nhds {s : Set (Set α)} (h_open : ∀ u ∈ s, IsOpen u)
    (h_nhds : ∀ (a : α) (u : Set α), a ∈ u → IsOpen u → ∃ v ∈ s, a ∈ v ∧ v ⊆ u) :
    IsTopologicalBasis s := by
  refine'
    ⟨fun t₁ ht₁ t₂ ht₂ x hx => h_nhds _ _ hx (IsOpen.inter (h_open _ ht₁) (h_open _ ht₂)), _, _⟩
  · refine' unionₛ_eq_univ_iff.2 fun a => _
    rcases h_nhds a univ trivial isOpen_univ with ⟨u, h₁, h₂, -⟩
    exact ⟨u, h₁, h₂⟩
  · refine' (le_generateFrom h_open).antisymm fun u hu => _
    refine' (@isOpen_iff_nhds α (generateFrom s) u).mpr fun a ha => _
    rcases h_nhds a u ha hu with ⟨v, hvs, hav, hvu⟩
    rw [nhds_generateFrom]
    exact infᵢ₂_le_of_le v ⟨hav, hvs⟩ (le_principal_iff.2 hvu)
#align topological_space.is_topological_basis_of_open_of_nhds TopologicalSpace.isTopologicalBasis_of_open_of_nhds

/-- A set `s` is in the neighbourhood of `a` iff there is some basis set `t`, which
contains `a` and is itself contained in `s`. -/
theorem IsTopologicalBasis.mem_nhds_iff {a : α} {s : Set α} {b : Set (Set α)}
    (hb : IsTopologicalBasis b) : s ∈ 𝓝 a ↔ ∃ t ∈ b, a ∈ t ∧ t ⊆ s := by
  change s ∈ (𝓝 a).sets ↔ ∃ t ∈ b, a ∈ t ∧ t ⊆ s
  rw [hb.eq_generateFrom, nhds_generateFrom, binfᵢ_sets_eq]
  · simp [and_assoc, and_left_comm]
  · rintro s ⟨hs₁, hs₂⟩ t ⟨ht₁, ht₂⟩
    let ⟨u, hu₁, hu₂, hu₃⟩ := hb.1 _ hs₂ _ ht₂ _ ⟨hs₁, ht₁⟩
    exact ⟨u, ⟨hu₂, hu₁⟩, le_principal_iff.2 (hu₃.trans (inter_subset_left _ _)),
      le_principal_iff.2 (hu₃.trans (inter_subset_right _ _))⟩
  · rcases eq_univ_iff_forall.1 hb.unionₛ_eq a with ⟨i, h1, h2⟩
    exact ⟨i, h2, h1⟩
#align topological_space.is_topological_basis.mem_nhds_iff TopologicalSpace.IsTopologicalBasis.mem_nhds_iff

theorem IsTopologicalBasis.isOpen_iff {s : Set α} {b : Set (Set α)} (hb : IsTopologicalBasis b) :
    IsOpen s ↔ ∀ a ∈ s, ∃ t ∈ b, a ∈ t ∧ t ⊆ s := by simp [isOpen_iff_mem_nhds, hb.mem_nhds_iff]
#align topological_space.is_topological_basis.is_open_iff TopologicalSpace.IsTopologicalBasis.isOpen_iff

theorem IsTopologicalBasis.nhds_hasBasis {b : Set (Set α)} (hb : IsTopologicalBasis b) {a : α} :
    (𝓝 a).HasBasis (fun t : Set α => t ∈ b ∧ a ∈ t) fun t => t :=
  ⟨fun s => hb.mem_nhds_iff.trans <| by simp only [exists_prop, and_assoc]⟩
#align topological_space.is_topological_basis.nhds_has_basis TopologicalSpace.IsTopologicalBasis.nhds_hasBasis

protected theorem IsTopologicalBasis.isOpen {s : Set α} {b : Set (Set α)}
    (hb : IsTopologicalBasis b) (hs : s ∈ b) : IsOpen s := by
  rw [hb.eq_generateFrom]
  exact .basic s hs
#align topological_space.is_topological_basis.is_open TopologicalSpace.IsTopologicalBasis.isOpen

protected theorem IsTopologicalBasis.mem_nhds {a : α} {s : Set α} {b : Set (Set α)}
    (hb : IsTopologicalBasis b) (hs : s ∈ b) (ha : a ∈ s) : s ∈ 𝓝 a :=
  (hb.isOpen hs).mem_nhds ha
#align topological_space.is_topological_basis.mem_nhds TopologicalSpace.IsTopologicalBasis.mem_nhds

theorem IsTopologicalBasis.exists_subset_of_mem_open {b : Set (Set α)} (hb : IsTopologicalBasis b)
    {a : α} {u : Set α} (au : a ∈ u) (ou : IsOpen u) : ∃ v ∈ b, a ∈ v ∧ v ⊆ u :=
  hb.mem_nhds_iff.1 <| IsOpen.mem_nhds ou au
#align topological_space.is_topological_basis.exists_subset_of_mem_open TopologicalSpace.IsTopologicalBasis.exists_subset_of_mem_open

/-- Any open set is the union of the basis sets contained in it. -/
theorem IsTopologicalBasis.open_eq_unionₛ' {B : Set (Set α)} (hB : IsTopologicalBasis B) {u : Set α}
    (ou : IsOpen u) : u = ⋃₀ { s ∈ B | s ⊆ u } :=
  ext fun _a =>
    ⟨fun ha =>
      let ⟨b, hb, ab, bu⟩ := hB.exists_subset_of_mem_open ha ou
      ⟨b, ⟨hb, bu⟩, ab⟩,
      fun ⟨_b, ⟨_, bu⟩, ab⟩ => bu ab⟩
#align topological_space.is_topological_basis.open_eq_sUnion' TopologicalSpace.IsTopologicalBasis.open_eq_unionₛ'

-- porting note: use `∃ S, S ⊆ B ∧ _` instead of `∃ S (_ : S ⊆ B), _`
theorem IsTopologicalBasis.open_eq_unionₛ {B : Set (Set α)} (hB : IsTopologicalBasis B) {u : Set α}
    (ou : IsOpen u) : ∃ S, S ⊆ B ∧ u = ⋃₀ S :=
  ⟨{ s ∈ B | s ⊆ u }, fun _ h => h.1, hB.open_eq_unionₛ' ou⟩
#align topological_space.is_topological_basis.open_eq_sUnion TopologicalSpace.IsTopologicalBasis.open_eq_unionₛ

-- porting note: use `∃ S, S ⊆ B ∧ _` instead of `∃ S (_ : S ⊆ B), _`
theorem IsTopologicalBasis.open_iff_eq_unionₛ {B : Set (Set α)} (hB : IsTopologicalBasis B)
    {u : Set α} : IsOpen u ↔ ∃ S, S ⊆ B ∧ u = ⋃₀ S :=
  ⟨hB.open_eq_unionₛ, fun ⟨_S, hSB, hu⟩ => hu.symm ▸ isOpen_unionₛ fun _s hs => hB.isOpen (hSB hs)⟩
#align topological_space.is_topological_basis.open_iff_eq_sUnion TopologicalSpace.IsTopologicalBasis.open_iff_eq_unionₛ

theorem IsTopologicalBasis.open_eq_unionᵢ {B : Set (Set α)} (hB : IsTopologicalBasis B) {u : Set α}
    (ou : IsOpen u) : ∃ (β : Type u) (f : β → Set α), (u = ⋃ i, f i) ∧ ∀ i, f i ∈ B :=
  ⟨↥({ s ∈ B | s ⊆ u }), (↑), by
    rw [← unionₛ_eq_unionᵢ]
    apply hB.open_eq_unionₛ' ou, fun s => And.left s.2⟩
#align topological_space.is_topological_basis.open_eq_Union TopologicalSpace.IsTopologicalBasis.open_eq_unionᵢ

/-- A point `a` is in the closure of `s` iff all basis sets containing `a` intersect `s`. -/
theorem IsTopologicalBasis.mem_closure_iff {b : Set (Set α)} (hb : IsTopologicalBasis b) {s : Set α}
    {a : α} : a ∈ closure s ↔ ∀ o ∈ b, a ∈ o → (o ∩ s).Nonempty :=
  (mem_closure_iff_nhds_basis' hb.nhds_hasBasis).trans <| by simp only [and_imp]
#align topological_space.is_topological_basis.mem_closure_iff TopologicalSpace.IsTopologicalBasis.mem_closure_iff

/-- A set is dense iff it has non-trivial intersection with all basis sets. -/
theorem IsTopologicalBasis.dense_iff {b : Set (Set α)} (hb : IsTopologicalBasis b) {s : Set α} :
    Dense s ↔ ∀ o ∈ b, Set.Nonempty o → (o ∩ s).Nonempty := by
  simp only [Dense, hb.mem_closure_iff]
  exact ⟨fun h o hb ⟨a, ha⟩ => h a o hb ha, fun h a o hb ha => h o hb ⟨a, ha⟩⟩
#align topological_space.is_topological_basis.dense_iff TopologicalSpace.IsTopologicalBasis.dense_iff

theorem IsTopologicalBasis.isOpenMap_iff {β} [TopologicalSpace β] {B : Set (Set α)}
    (hB : IsTopologicalBasis B) {f : α → β} : IsOpenMap f ↔ ∀ s ∈ B, IsOpen (f '' s) := by
  refine' ⟨fun H o ho => H _ (hB.isOpen ho), fun hf o ho => _⟩
  rw [hB.open_eq_unionₛ' ho, unionₛ_eq_unionᵢ, image_unionᵢ]
  exact isOpen_unionᵢ fun s => hf s s.2.1
#align topological_space.is_topological_basis.is_open_map_iff TopologicalSpace.IsTopologicalBasis.isOpenMap_iff

theorem IsTopologicalBasis.exists_nonempty_subset {B : Set (Set α)} (hb : IsTopologicalBasis B)
    {u : Set α} (hu : u.Nonempty) (ou : IsOpen u) : ∃ v ∈ B, Set.Nonempty v ∧ v ⊆ u := by
  cases' hu with x hx
  rw [hb.open_eq_unionₛ' ou, mem_unionₛ] at hx
  rcases hx with ⟨v, hv, hxv⟩
  exact ⟨v, hv.1, ⟨x, hxv⟩, hv.2⟩
#align topological_space.is_topological_basis.exists_nonempty_subset TopologicalSpace.IsTopologicalBasis.exists_nonempty_subset

theorem isTopologicalBasis_opens : IsTopologicalBasis { U : Set α | IsOpen U } :=
  isTopologicalBasis_of_open_of_nhds (by tauto) (by tauto)
#align topological_space.is_topological_basis_opens TopologicalSpace.isTopologicalBasis_opens

-- porting note: Lean 4 can't handle ambigous notation here
protected theorem IsTopologicalBasis.prod {β} [TopologicalSpace β] {B₁ : Set (Set α)}
    {B₂ : Set (Set β)} (h₁ : IsTopologicalBasis B₁) (h₂ : IsTopologicalBasis B₂) :
    IsTopologicalBasis (image2 Set.prod B₁ B₂) := by
  refine' isTopologicalBasis_of_open_of_nhds _ _
  · rintro _ ⟨u₁, u₂, hu₁, hu₂, rfl⟩
    exact (h₁.isOpen hu₁).prod (h₂.isOpen hu₂)
  · rintro ⟨a, b⟩ u hu uo
    rcases(h₁.nhds_hasBasis.prod_nhds h₂.nhds_hasBasis).mem_iff.1 (IsOpen.mem_nhds uo hu) with
      ⟨⟨s, t⟩, ⟨⟨hs, ha⟩, ht, hb⟩, hu⟩
    exact ⟨s ×ˢ t, mem_image2_of_mem hs ht, ⟨ha, hb⟩, hu⟩
#align topological_space.is_topological_basis.prod TopologicalSpace.IsTopologicalBasis.prod

protected theorem IsTopologicalBasis.inducing {β} [TopologicalSpace β] {f : α → β} {T : Set (Set β)}
    (hf : Inducing f) (h : IsTopologicalBasis T) : IsTopologicalBasis ((preimage f) '' T) := by
  refine' isTopologicalBasis_of_open_of_nhds _ _
  · rintro _ ⟨V, hV, rfl⟩
    rw [hf.isOpen_iff]
    refine' ⟨V, h.isOpen hV, rfl⟩
  · intro a U ha hU
    rw [hf.isOpen_iff] at hU
    obtain ⟨V, hV, rfl⟩ := hU
    obtain ⟨S, hS, rfl⟩ := h.open_eq_unionₛ hV
    obtain ⟨W, hW, ha⟩ := ha
    refine' ⟨f ⁻¹' W, ⟨_, hS hW, rfl⟩, ha, Set.preimage_mono <| Set.subset_unionₛ_of_mem hW⟩
#align topological_space.is_topological_basis.inducing TopologicalSpace.IsTopologicalBasis.inducing

theorem isTopologicalBasis_of_cover {ι} {U : ι → Set α} (Uo : ∀ i, IsOpen (U i))
    (Uc : (⋃ i, U i) = univ) {b : ∀ i, Set (Set (U i))} (hb : ∀ i, IsTopologicalBasis (b i)) :
    IsTopologicalBasis (⋃ i : ι, image ((↑) : U i → α) '' b i) := by
  refine' isTopologicalBasis_of_open_of_nhds (fun u hu => _) _
  · simp only [mem_unionᵢ, mem_image] at hu
    rcases hu with ⟨i, s, sb, rfl⟩
    exact (Uo i).isOpenMap_subtype_val _ ((hb i).isOpen sb)
  · intro a u ha uo
    rcases unionᵢ_eq_univ_iff.1 Uc a with ⟨i, hi⟩
    lift a to ↥(U i) using hi
    rcases(hb i).exists_subset_of_mem_open ha (uo.preimage continuous_subtype_val) with
      ⟨v, hvb, hav, hvu⟩
    exact ⟨(↑) '' v, mem_unionᵢ.2 ⟨i, mem_image_of_mem _ hvb⟩, mem_image_of_mem _ hav,
      image_subset_iff.2 hvu⟩
#align topological_space.is_topological_basis_of_cover TopologicalSpace.isTopologicalBasis_of_cover

protected theorem IsTopologicalBasis.continuous {β : Type _} [TopologicalSpace β] {B : Set (Set β)}
    (hB : IsTopologicalBasis B) (f : α → β) (hf : ∀ s ∈ B, IsOpen (f ⁻¹' s)) : Continuous f := by
  rw [hB.eq_generateFrom]; exact continuous_generateFrom hf
#align topological_space.is_topological_basis.continuous TopologicalSpace.IsTopologicalBasis.continuous

variable (α)

/-- A separable space is one with a countable dense subset, available through
`TopologicalSpace.exists_countable_dense`. If `α` is also known to be nonempty, then
`TopologicalSpace.denseSeq` provides a sequence `ℕ → α` with dense range, see
`TopologicalSpace.denseRange_denseSeq`.

If `α` is a uniform space with countably generated uniformity filter (e.g., an `EMetricSpace`), then
this condition is equivalent to `TopologicalSpace.SecondCountableTopology α`. In this case the
latter should be used as a typeclass argument in theorems because Lean can automatically deduce
`TopologicalSpace.SeparableSpace` from `TopologicalSpace.SecondCountableTopology` but it can't
deduce `TopologicalSpace.SecondCountableTopology` from `TopologicalSpace.SeparableSpace`.

Porting note: TODO: the previous paragraph describes the state of the art in Lean 3. We can have
instance cycles in Lean 4 but we might want to postpone adding them till after the port. -/
class SeparableSpace : Prop where
  /-- There exists a countable dense set. -/
  exists_countable_dense : ∃ s : Set α, s.Countable ∧ Dense s
#align topological_space.separable_space TopologicalSpace.SeparableSpace

theorem exists_countable_dense [SeparableSpace α] : ∃ s : Set α, s.Countable ∧ Dense s :=
  SeparableSpace.exists_countable_dense
#align topological_space.exists_countable_dense TopologicalSpace.exists_countable_dense

/-- A nonempty separable space admits a sequence with dense range. Instead of running `cases` on the
conclusion of this lemma, you might want to use `TopologicalSpace.denseSeq` and
`TopologicalSpace.denseRange_denseSeq`.

If `α` might be empty, then `TopologicalSpace.exists_countable_dense` is the main way to use
separability of `α`. -/
theorem exists_dense_seq [SeparableSpace α] [Nonempty α] : ∃ u : ℕ → α, DenseRange u := by
  obtain ⟨s : Set α, hs, s_dense⟩ := exists_countable_dense α
  cases' Set.countable_iff_exists_subset_range.mp hs with u hu
  exact ⟨u, s_dense.mono hu⟩
#align topological_space.exists_dense_seq TopologicalSpace.exists_dense_seq

/-- A dense sequence in a non-empty separable topological space.

If `α` might be empty, then `TopologicalSpace.exists_countable_dense` is the main way to use
separability of `α`. -/
def denseSeq [SeparableSpace α] [Nonempty α] : ℕ → α :=
  Classical.choose (exists_dense_seq α)
#align topological_space.dense_seq TopologicalSpace.denseSeq

/-- The sequence `TopologicalSpace.denseSeq α` has dense range. -/
@[simp]
theorem denseRange_denseSeq [SeparableSpace α] [Nonempty α] : DenseRange (denseSeq α) :=
  Classical.choose_spec (exists_dense_seq α)
#align topological_space.dense_range_dense_seq TopologicalSpace.denseRange_denseSeq

variable {α}

instance (priority := 100) Countable.to_separableSpace [Countable α] : SeparableSpace α
    where exists_countable_dense := ⟨Set.univ, Set.countable_univ, dense_univ⟩
#align topological_space.countable.to_separable_space TopologicalSpace.Countable.to_separableSpace

theorem separableSpace_of_denseRange {ι : Type _} [Countable ι] (u : ι → α) (hu : DenseRange u) :
    SeparableSpace α :=
  ⟨⟨range u, countable_range u, hu⟩⟩
#align topological_space.separable_space_of_dense_range TopologicalSpace.separableSpace_of_denseRange

/-- In a separable space, a family of nonempty disjoint open sets is countable. -/
theorem _root_.Set.PairwiseDisjoint.countable_of_isOpen [SeparableSpace α] {ι : Type _}
    {s : ι → Set α} {a : Set ι} (h : a.PairwiseDisjoint s) (ha : ∀ i ∈ a, IsOpen (s i))
    (h'a : ∀ i ∈ a, (s i).Nonempty) : a.Countable := by
  rcases exists_countable_dense α with ⟨u, ⟨u_encodable⟩, u_dense⟩
  have : ∀ i : a, ∃ y, y ∈ s i ∩ u := fun i =>
    dense_iff_inter_open.1 u_dense (s i) (ha i i.2) (h'a i i.2)
  choose f hfs hfu using this
  lift f to a → u using hfu
  have f_inj : Injective f := by
    refine' injective_iff_pairwise_ne.mpr
      ((h.subtype _ _).mono fun i j hij hfij => hij.le_bot ⟨hfs i, _⟩)
    simp only [congr_arg Subtype.val hfij, hfs j]
  exact ⟨@Encodable.ofInj _ _ u_encodable f f_inj⟩
#align set.pairwise_disjoint.countable_of_is_open Set.PairwiseDisjoint.countable_of_isOpen

/-- In a separable space, a family of disjoint sets with nonempty interiors is countable. -/
theorem _root_.Set.PairwiseDisjoint.countable_of_nonempty_interior [SeparableSpace α] {ι : Type _}
    {s : ι → Set α} {a : Set ι} (h : a.PairwiseDisjoint s)
    (ha : ∀ i ∈ a, (interior (s i)).Nonempty) : a.Countable :=
  (h.mono fun _ => interior_subset).countable_of_isOpen (fun _ _ => isOpen_interior) ha
#align set.pairwise_disjoint.countable_of_nonempty_interior Set.PairwiseDisjoint.countable_of_nonempty_interior

/-- A set `s` in a topological space is separable if it is contained in the closure of a countable
set `c`. Beware that this definition does not require that `c` is contained in `s` (to express the
latter, use `TopologicalSpace.SeparableSpace s` or
`TopologicalSpace.IsSeparable (univ : Set s))`. In metric spaces, the two definitions are
equivalent, see `TopologicalSpace.IsSeparable.SeparableSpace`. -/
def IsSeparable (s : Set α) :=
  ∃ c : Set α, c.Countable ∧ s ⊆ closure c
#align topological_space.is_separable TopologicalSpace.IsSeparable

theorem IsSeparable.mono {s u : Set α} (hs : IsSeparable s) (hu : u ⊆ s) : IsSeparable u := by
  rcases hs with ⟨c, c_count, hs⟩
  exact ⟨c, c_count, hu.trans hs⟩
#align topological_space.is_separable.mono TopologicalSpace.IsSeparable.mono

theorem IsSeparable.union {s u : Set α} (hs : IsSeparable s) (hu : IsSeparable u) :
    IsSeparable (s ∪ u) := by
  rcases hs with ⟨cs, cs_count, hcs⟩
  rcases hu with ⟨cu, cu_count, hcu⟩
  refine' ⟨cs ∪ cu, cs_count.union cu_count, _⟩
  exact
    union_subset (hcs.trans (closure_mono (subset_union_left _ _)))
      (hcu.trans (closure_mono (subset_union_right _ _)))
#align topological_space.is_separable.union TopologicalSpace.IsSeparable.union

theorem IsSeparable.closure {s : Set α} (hs : IsSeparable s) : IsSeparable (closure s) := by
  rcases hs with ⟨c, c_count, hs⟩
  exact ⟨c, c_count, by simpa using closure_mono hs⟩
#align topological_space.is_separable.closure TopologicalSpace.IsSeparable.closure

theorem isSeparable_unionᵢ {ι : Type _} [Countable ι] {s : ι → Set α}
    (hs : ∀ i, IsSeparable (s i)) : IsSeparable (⋃ i, s i) := by
  choose c hc h'c using hs
  refine' ⟨⋃ i, c i, countable_unionᵢ hc, unionᵢ_subset_iff.2 fun i => _⟩
  exact (h'c i).trans (closure_mono (subset_unionᵢ _ i))
#align topological_space.is_separable_Union TopologicalSpace.isSeparable_unionᵢ

theorem _root_.Set.Countable.isSeparable {s : Set α} (hs : s.Countable) : IsSeparable s :=
  ⟨s, hs, subset_closure⟩
#align set.countable.is_separable Set.Countable.isSeparable

theorem _root_.Set.Finite.isSeparable {s : Set α} (hs : s.Finite) : IsSeparable s :=
  hs.countable.isSeparable
#align set.finite.is_separable Set.Finite.isSeparable

theorem isSeparable_univ_iff : IsSeparable (univ : Set α) ↔ SeparableSpace α := by
  constructor
  · rintro ⟨c, c_count, hc⟩
    refine' ⟨⟨c, c_count, by rwa [dense_iff_closure_eq, ← univ_subset_iff]⟩⟩
  · intro h
    rcases exists_countable_dense α with ⟨c, c_count, hc⟩
    exact ⟨c, c_count, by rwa [univ_subset_iff, ← dense_iff_closure_eq]⟩
#align topological_space.is_separable_univ_iff TopologicalSpace.isSeparable_univ_iff

theorem isSeparable_of_separableSpace [h : SeparableSpace α] (s : Set α) : IsSeparable s :=
  IsSeparable.mono (isSeparable_univ_iff.2 h) (subset_univ _)
#align topological_space.is_separable_of_separable_space TopologicalSpace.isSeparable_of_separableSpace

theorem IsSeparable.image {β : Type _} [TopologicalSpace β] {s : Set α} (hs : IsSeparable s)
    {f : α → β} (hf : Continuous f) : IsSeparable (f '' s) := by
  rcases hs with ⟨c, c_count, hc⟩
  refine' ⟨f '' c, c_count.image _, _⟩
  rw [image_subset_iff]
  exact hc.trans (closure_subset_preimage_closure_image hf)
#align topological_space.is_separable.image TopologicalSpace.IsSeparable.image

theorem isSeparable_of_separableSpace_subtype (s : Set α) [SeparableSpace s] : IsSeparable s := by
  have : IsSeparable (((↑) : s → α) '' (univ : Set s)) :=
    (isSeparable_of_separableSpace _).image continuous_subtype_val
  simpa only [image_univ, Subtype.range_val_subtype] using this
#align topological_space.is_separable_of_separable_space_subtype TopologicalSpace.isSeparable_of_separableSpace_subtype

end TopologicalSpace

open TopologicalSpace

theorem isTopologicalBasis_pi {ι : Type _} {X : ι → Type _} [∀ i, TopologicalSpace (X i)]
    {T : ∀ i, Set (Set (X i))} (cond : ∀ i, IsTopologicalBasis (T i)) :
    IsTopologicalBasis { S | ∃ (U : ∀ i, Set (X i)) (F : Finset ι),
      (∀ i, i ∈ F → U i ∈ T i) ∧ S = (F : Set ι).pi U } := by
  refine' isTopologicalBasis_of_open_of_nhds _ _
  · rintro _ ⟨U, F, h1, rfl⟩
    apply isOpen_set_pi F.finite_toSet
    intro i hi
    exact (cond i).isOpen (h1 i hi)
  · intro a U ha hU
    obtain ⟨I, t, hta, htU⟩ : ∃ (I : Finset ι) (t : ∀ i : ι, Set (X i)),
        (∀ i, t i ∈ 𝓝 (a i)) ∧ Set.pi (↑I) t ⊆ U := by
      rw [← Filter.mem_pi', ← nhds_pi]
      exact hU.mem_nhds ha
    have : ∀ i, ∃ V ∈ T i, a i ∈ V ∧ V ⊆ t i := fun i => (cond i).mem_nhds_iff.1 (hta i)
    choose V hVT haV hVt using this
    exact ⟨_, ⟨V, I, fun i _ => hVT i, rfl⟩, fun i _ => haV i, (pi_mono fun i _ => hVt i).trans htU⟩
#align is_topological_basis_pi isTopologicalBasis_pi

theorem isTopologicalBasis_infᵢ {β : Type _} {ι : Type _} {X : ι → Type _}
    [t : ∀ i, TopologicalSpace (X i)] {T : ∀ i, Set (Set (X i))}
    (cond : ∀ i, IsTopologicalBasis (T i)) (f : ∀ i, β → X i) :
    @IsTopologicalBasis β (⨅ i, induced (f i) (t i))
      { S | ∃ (U : ∀ i, Set (X i)) (F : Finset ι),
        (∀ i, i ∈ F → U i ∈ T i) ∧ S = ⋂ (i) (_hi : i ∈ F), f i ⁻¹' U i } := by
  letI := ⨅ i, induced (f i) (t i)
  convert (isTopologicalBasis_pi cond).inducing (inducing_infᵢ_to_pi f)
  ext V
  constructor
  · rintro ⟨U, F, h1, rfl⟩
    refine' ⟨(F : Set ι).pi U, ⟨U, F, h1, rfl⟩, _⟩
    simp_rw [pi_def, Set.preimage_interᵢ]
    rfl
  · rintro ⟨U, ⟨U, F, h1, rfl⟩, rfl⟩
    refine' ⟨U, F, h1, _⟩
    simp_rw [pi_def, Set.preimage_interᵢ]
    rfl
#align is_topological_basis_infi isTopologicalBasis_infᵢ

theorem isTopologicalBasis_singletons (α : Type _) [TopologicalSpace α] [DiscreteTopology α] :
    IsTopologicalBasis { s | ∃ x : α, (s : Set α) = {x} } :=
  isTopologicalBasis_of_open_of_nhds (fun _ _ => isOpen_discrete _) fun x _ hx _ =>
    ⟨{x}, ⟨x, rfl⟩, mem_singleton x, singleton_subset_iff.2 hx⟩
#align is_topological_basis_singletons isTopologicalBasis_singletons

/-- If `α` is a separable space and `f : α → β` is a continuous map with dense range, then `β` is
a separable space as well. E.g., the completion of a separable uniform space is separable. -/
protected theorem DenseRange.separableSpace {α β : Type _} [TopologicalSpace α] [SeparableSpace α]
    [TopologicalSpace β] {f : α → β} (h : DenseRange f) (h' : Continuous f) : SeparableSpace β :=
  let ⟨s, s_cnt, s_dense⟩ := exists_countable_dense α
  ⟨⟨f '' s, Countable.image s_cnt f, h.dense_image h' s_dense⟩⟩
#align dense_range.separable_space DenseRange.separableSpace

-- porting note: use `∃ t, t ⊆ s ∧ _` instead of `∃ t (_ : t ⊆ s), _`
theorem Dense.exists_countable_dense_subset {α : Type _} [TopologicalSpace α] {s : Set α}
    [SeparableSpace s] (hs : Dense s) : ∃ t, t ⊆ s ∧ t.Countable ∧ Dense t :=
  let ⟨t, htc, htd⟩ := exists_countable_dense s
  ⟨(↑) '' t, image_subset_iff.2 fun _ _ => mem_preimage.2 <| Subtype.coe_prop _, htc.image (↑),
    hs.denseRange_val.dense_image continuous_subtype_val htd⟩
#align dense.exists_countable_dense_subset Dense.exists_countable_dense_subsetₓ

-- porting note: use `∃ t, t ⊆ s ∧ _` instead of `∃ t (_ : t ⊆ s), _`
/-- Let `s` be a dense set in a topological space `α` with partial order structure. If `s` is a
separable space (e.g., if `α` has a second countable topology), then there exists a countable
dense subset `t ⊆ s` such that `t` contains bottom/top element of `α` when they exist and belong
to `s`. For a dense subset containing neither bot nor top elements, see
`Dense.exists_countable_dense_subset_no_bot_top`. -/
theorem Dense.exists_countable_dense_subset_bot_top {α : Type _} [TopologicalSpace α]
    [PartialOrder α] {s : Set α} [SeparableSpace s] (hs : Dense s) :
    ∃ t, t ⊆ s ∧ t.Countable ∧ Dense t ∧ (∀ x, IsBot x → x ∈ s → x ∈ t) ∧
      ∀ x, IsTop x → x ∈ s → x ∈ t := by
  rcases hs.exists_countable_dense_subset with ⟨t, hts, htc, htd⟩
  refine' ⟨(t ∪ ({ x | IsBot x } ∪ { x | IsTop x })) ∩ s, _, _, _, _, _⟩
  exacts[inter_subset_right _ _,
    (htc.union ((countable_isBot α).union (countable_isTop α))).mono (inter_subset_left _ _),
    htd.mono (subset_inter (subset_union_left _ _) hts), fun x hx hxs => ⟨Or.inr <| Or.inl hx, hxs⟩,
    fun x hx hxs => ⟨Or.inr <| Or.inr hx, hxs⟩]
#align dense.exists_countable_dense_subset_bot_top Dense.exists_countable_dense_subset_bot_top

instance separableSpace_univ {α : Type _} [TopologicalSpace α] [SeparableSpace α] :
    SeparableSpace (univ : Set α) :=
  (Equiv.Set.univ α).symm.surjective.denseRange.separableSpace (continuous_id.subtype_mk _)
#align separable_space_univ separableSpace_univ

/-- If `α` is a separable topological space with a partial order, then there exists a countable
dense set `s : Set α` that contains those of both bottom and top elements of `α` that actually
exist. For a dense set containing neither bot nor top elements, see
`exists_countable_dense_no_bot_top`. -/
theorem exists_countable_dense_bot_top (α : Type _) [TopologicalSpace α] [SeparableSpace α]
    [PartialOrder α] :
    ∃ s : Set α, s.Countable ∧ Dense s ∧ (∀ x, IsBot x → x ∈ s) ∧ ∀ x, IsTop x → x ∈ s := by
  simpa using dense_univ.exists_countable_dense_subset_bot_top
#align exists_countable_dense_bot_top exists_countable_dense_bot_top

namespace TopologicalSpace

universe u

variable (α : Type u) [t : TopologicalSpace α]

/-- A first-countable space is one in which every point has a
  countable neighborhood basis. -/
class FirstCountableTopology : Prop where
  /-- The filter `𝓝 a` is countably generated for all points `a`. -/
  nhds_generated_countable : ∀ a : α, (𝓝 a).IsCountablyGenerated
#align topological_space.first_countable_topology TopologicalSpace.FirstCountableTopology

attribute [instance] FirstCountableTopology.nhds_generated_countable

namespace FirstCountableTopology

variable {α}

/-- In a first-countable space, a cluster point `x` of a sequence
is the limit of some subsequence. -/
theorem tendsto_subseq [FirstCountableTopology α] {u : ℕ → α} {x : α}
    (hx : MapClusterPt x atTop u) : ∃ ψ : ℕ → ℕ, StrictMono ψ ∧ Tendsto (u ∘ ψ) atTop (𝓝 x) :=
  subseq_tendsto_of_neBot hx
#align topological_space.first_countable_topology.tendsto_subseq TopologicalSpace.FirstCountableTopology.tendsto_subseq

end FirstCountableTopology

variable {α}

instance {β} [TopologicalSpace β] [FirstCountableTopology α] [FirstCountableTopology β] :
    FirstCountableTopology (α × β) :=
  ⟨fun ⟨x, y⟩ => by rw [nhds_prod_eq]; infer_instance⟩

section Pi

instance {ι : Type _} {π : ι → Type _} [Countable ι] [∀ i, TopologicalSpace (π i)]
    [∀ i, FirstCountableTopology (π i)] : FirstCountableTopology (∀ i, π i) :=
  ⟨fun f => by rw [nhds_pi]; infer_instance⟩

end Pi

instance isCountablyGenerated_nhdsWithin (x : α) [IsCountablyGenerated (𝓝 x)] (s : Set α) :
    IsCountablyGenerated (𝓝[s] x) :=
  Inf.isCountablyGenerated _ _
#align topological_space.is_countably_generated_nhds_within TopologicalSpace.isCountablyGenerated_nhdsWithin

variable (α)

/-- A second-countable space is one with a countable basis. -/
class SecondCountableTopology : Prop where
  /-- There exists a countable set of sets that generates the topology. -/
  is_open_generated_countable : ∃ b : Set (Set α), b.Countable ∧ t = TopologicalSpace.generateFrom b
#align topological_space.second_countable_topology TopologicalSpace.SecondCountableTopology

variable {α}

protected theorem IsTopologicalBasis.secondCountableTopology {b : Set (Set α)}
    (hb : IsTopologicalBasis b) (hc : b.Countable) : SecondCountableTopology α :=
  ⟨⟨b, hc, hb.eq_generateFrom⟩⟩
#align topological_space.is_topological_basis.second_countable_topology TopologicalSpace.IsTopologicalBasis.secondCountableTopology

lemma SecondCountableTopology.mk' {b : Set (Set α)} (hc : b.Countable) :
    @SecondCountableTopology α (generateFrom b) :=
  @SecondCountableTopology.mk α (generateFrom b) ⟨b, hc, rfl⟩

variable (α)

theorem exists_countable_basis [SecondCountableTopology α] :
    ∃ b : Set (Set α), b.Countable ∧ ∅ ∉ b ∧ IsTopologicalBasis b := by
  obtain ⟨b, hb₁, hb₂⟩ := @SecondCountableTopology.is_open_generated_countable α _ _
  refine' ⟨_, _, not_mem_diff_of_mem _, (isTopologicalBasis_of_subbasis hb₂).diff_empty⟩
  exacts [((countable_setOf_finite_subset hb₁).image _).mono (diff_subset _ _), rfl]
#align topological_space.exists_countable_basis TopologicalSpace.exists_countable_basis

/-- A countable topological basis of `α`. -/
def countableBasis [SecondCountableTopology α] : Set (Set α) :=
  (exists_countable_basis α).choose
#align topological_space.countable_basis TopologicalSpace.countableBasis

theorem countable_countableBasis [SecondCountableTopology α] : (countableBasis α).Countable :=
  (exists_countable_basis α).choose_spec.1
#align topological_space.countable_countable_basis TopologicalSpace.countable_countableBasis

instance encodableCountableBasis [SecondCountableTopology α] : Encodable (countableBasis α) :=
  (countable_countableBasis α).toEncodable
#align topological_space.encodable_countable_basis TopologicalSpace.encodableCountableBasis

theorem empty_nmem_countableBasis [SecondCountableTopology α] : ∅ ∉ countableBasis α :=
  (exists_countable_basis α).choose_spec.2.1
#align topological_space.empty_nmem_countable_basis TopologicalSpace.empty_nmem_countableBasis

theorem isBasis_countableBasis [SecondCountableTopology α] :
    IsTopologicalBasis (countableBasis α) :=
  (exists_countable_basis α).choose_spec.2.2
#align topological_space.is_basis_countable_basis TopologicalSpace.isBasis_countableBasis

theorem eq_generateFrom_countableBasis [SecondCountableTopology α] :
    ‹TopologicalSpace α› = generateFrom (countableBasis α) :=
  (isBasis_countableBasis α).eq_generateFrom
#align topological_space.eq_generate_from_countable_basis TopologicalSpace.eq_generateFrom_countableBasis

variable {α}

theorem isOpen_of_mem_countableBasis [SecondCountableTopology α] {s : Set α}
    (hs : s ∈ countableBasis α) : IsOpen s :=
  (isBasis_countableBasis α).isOpen hs
#align topological_space.is_open_of_mem_countable_basis TopologicalSpace.isOpen_of_mem_countableBasis

theorem nonempty_of_mem_countableBasis [SecondCountableTopology α] {s : Set α}
    (hs : s ∈ countableBasis α) : s.Nonempty :=
  nonempty_iff_ne_empty.2 <| ne_of_mem_of_not_mem hs <| empty_nmem_countableBasis α
#align topological_space.nonempty_of_mem_countable_basis TopologicalSpace.nonempty_of_mem_countableBasis

variable (α)

-- see Note [lower instance priority]
instance (priority := 100) SecondCountableTopology.to_firstCountableTopology
    [SecondCountableTopology α] : FirstCountableTopology α :=
  ⟨fun _ => HasCountableBasis.isCountablyGenerated <|
      ⟨(isBasis_countableBasis α).nhds_hasBasis,
        (countable_countableBasis α).mono <| inter_subset_left _ _⟩⟩
#align topological_space.second_countable_topology.to_first_countable_topology TopologicalSpace.SecondCountableTopology.to_firstCountableTopology

/-- If `β` is a second-countable space, then its induced topology via
`f` on `α` is also second-countable. -/
theorem secondCountableTopology_induced (β) [t : TopologicalSpace β] [SecondCountableTopology β]
    (f : α → β) : @SecondCountableTopology α (t.induced f) := by
  rcases @SecondCountableTopology.is_open_generated_countable β _ _ with ⟨b, hb, eq⟩
  letI := t.induced f
  refine' { is_open_generated_countable := ⟨preimage f '' b, hb.image _, _⟩ }
  rw [eq, induced_generateFrom_eq]
#align topological_space.second_countable_topology_induced TopologicalSpace.secondCountableTopology_induced

variable {α}

instance Subtype.secondCountableTopology (s : Set α) [SecondCountableTopology α] :
    SecondCountableTopology s :=
  secondCountableTopology_induced s α (↑)
#align topological_space.subtype.second_countable_topology TopologicalSpace.Subtype.secondCountableTopology

lemma secondCountableTopology_infᵢ {ι} [Countable ι] {t : ι → TopologicalSpace α}
    (ht : ∀ i, @SecondCountableTopology α (t i)) : @SecondCountableTopology α (⨅ i, t i) := by
  rw [funext fun i => @eq_generateFrom_countableBasis α (t i) (ht i), ← generateFrom_unionᵢ]
  exact SecondCountableTopology.mk' <|
    countable_unionᵢ fun i => @countable_countableBasis _ (t i) (ht i)

-- TODO: more fine grained instances for first_countable_topology, separable_space, t2_space, ...
instance {β : Type _} [TopologicalSpace β] [SecondCountableTopology α] [SecondCountableTopology β] :
    SecondCountableTopology (α × β) :=
  ((isBasis_countableBasis α).prod (isBasis_countableBasis β)).secondCountableTopology <|
    (countable_countableBasis α).image2 (countable_countableBasis β) _

instance {ι : Type _} {π : ι → Type _} [Countable ι] [∀ a, TopologicalSpace (π a)]
    [∀ a, SecondCountableTopology (π a)] : SecondCountableTopology (∀ a, π a) :=
  secondCountableTopology_infᵢ fun _ => secondCountableTopology_induced _ _ _

-- see Note [lower instance priority]
instance (priority := 100) SecondCountableTopology.to_separableSpace [SecondCountableTopology α] :
    SeparableSpace α := by
  choose p hp using fun s : countableBasis α => nonempty_of_mem_countableBasis s.2
  exact
    ⟨⟨range p, countable_range _,
        (isBasis_countableBasis α).dense_iff.2 fun o ho _ => ⟨p ⟨o, ho⟩, hp _, mem_range_self _⟩⟩⟩
#align topological_space.second_countable_topology.to_separable_space TopologicalSpace.SecondCountableTopology.to_separableSpace

/-- A countable open cover induces a second-countable topology if all open covers
are themselves second countable. -/
theorem secondCountableTopology_of_countable_cover {ι} [Encodable ι] {U : ι → Set α}
    [∀ i, SecondCountableTopology (U i)] (Uo : ∀ i, IsOpen (U i)) (hc : (⋃ i, U i) = univ) :
    SecondCountableTopology α :=
  haveI : IsTopologicalBasis (⋃ i, image ((↑) : U i → α) '' countableBasis (U i)) :=
    isTopologicalBasis_of_cover Uo hc fun i => isBasis_countableBasis (U i)
  this.secondCountableTopology (countable_unionᵢ fun _ => (countable_countableBasis _).image _)
#align topological_space.second_countable_topology_of_countable_cover TopologicalSpace.secondCountableTopology_of_countable_cover

/-- In a second-countable space, an open set, given as a union of open sets,
is equal to the union of countably many of those sets. -/
theorem isOpen_unionᵢ_countable [SecondCountableTopology α] {ι} (s : ι → Set α)
    (H : ∀ i, IsOpen (s i)) : ∃ T : Set ι, T.Countable ∧ (⋃ i ∈ T, s i) = ⋃ i, s i := by
  let B := { b ∈ countableBasis α | ∃ i, b ⊆ s i }
  choose f hf using fun b : B => b.2.2
  haveI : Encodable B := ((countable_countableBasis α).mono (sep_subset _ _)).toEncodable
  refine' ⟨_, countable_range f, (unionᵢ₂_subset_unionᵢ _ _).antisymm (unionₛ_subset _)⟩
  rintro _ ⟨i, rfl⟩ x xs
  rcases (isBasis_countableBasis α).exists_subset_of_mem_open xs (H _) with ⟨b, hb, xb, bs⟩
  exact ⟨_, ⟨_, rfl⟩, _, ⟨⟨⟨_, hb, _, bs⟩, rfl⟩, rfl⟩, hf _ xb⟩
#align topological_space.is_open_Union_countable TopologicalSpace.isOpen_unionᵢ_countable

theorem isOpen_unionₛ_countable [SecondCountableTopology α] (S : Set (Set α))
    (H : ∀ s ∈ S, IsOpen s) : ∃ T : Set (Set α), T.Countable ∧ T ⊆ S ∧ ⋃₀ T = ⋃₀ S :=
  let ⟨T, cT, hT⟩ := isOpen_unionᵢ_countable (fun s : S => s.1) fun s => H s.1 s.2
  ⟨Subtype.val '' T, cT.image _, image_subset_iff.2 fun ⟨_, h⟩ _ => h, by
    rwa [unionₛ_image, unionₛ_eq_unionᵢ]⟩
#align topological_space.is_open_sUnion_countable TopologicalSpace.isOpen_unionₛ_countable

/-- In a topological space with second countable topology, if `f` is a function that sends each
point `x` to a neighborhood of `x`, then for some countable set `s`, the neighborhoods `f x`,
`x ∈ s`, cover the whole space. -/
theorem countable_cover_nhds [SecondCountableTopology α] {f : α → Set α} (hf : ∀ x, f x ∈ 𝓝 x) :
    ∃ s : Set α, s.Countable ∧ (⋃ x ∈ s, f x) = univ := by
  rcases isOpen_unionᵢ_countable (fun x => interior (f x)) fun x => isOpen_interior with
    ⟨s, hsc, hsU⟩
  suffices : (⋃ x ∈ s, interior (f x)) = univ
  exact ⟨s, hsc, flip eq_univ_of_subset this <| unionᵢ₂_mono fun _ _ => interior_subset⟩
  simp only [hsU, eq_univ_iff_forall, mem_unionᵢ]
  exact fun x => ⟨x, mem_interior_iff_mem_nhds.2 (hf x)⟩
#align topological_space.countable_cover_nhds TopologicalSpace.countable_cover_nhds

-- porting note: use `∃ t, t ⊆ s ∧ _` instead of `∃ t (_ : t ⊆ s), _`
theorem countable_cover_nhdsWithin [SecondCountableTopology α] {f : α → Set α} {s : Set α}
    (hf : ∀ x ∈ s, f x ∈ 𝓝[s] x) : ∃ t, t ⊆ s ∧ t.Countable ∧ s ⊆ ⋃ x ∈ t, f x := by
  have : ∀ x : s, (↑) ⁻¹' f x ∈ 𝓝 x := fun x => preimage_coe_mem_nhds_subtype.2 (hf x x.2)
  rcases countable_cover_nhds this with ⟨t, htc, htU⟩
  refine' ⟨(↑) '' t, Subtype.coe_image_subset _ _, htc.image _, fun x hx => _⟩
  simp only [bunionᵢ_image, eq_univ_iff_forall, ← preimage_unionᵢ, mem_preimage] at htU⊢
  exact htU ⟨x, hx⟩
#align topological_space.countable_cover_nhds_within TopologicalSpace.countable_cover_nhdsWithin

section Sigma

variable {ι : Type _} {E : ι → Type _} [∀ i, TopologicalSpace (E i)]

/-- In a disjoint union space `Σ i, E i`, one can form a topological basis by taking the union of
topological bases on each of the parts of the space. -/
theorem IsTopologicalBasis.sigma {s : ∀ i : ι, Set (Set (E i))}
    (hs : ∀ i, IsTopologicalBasis (s i)) :
    IsTopologicalBasis (⋃ i : ι, (fun u => (Sigma.mk i '' u : Set (Σi, E i))) '' s i) := by
  apply isTopologicalBasis_of_open_of_nhds
  · intro u hu
    obtain ⟨i, t, ts, rfl⟩ : ∃ (i : ι) (t : Set (E i)), t ∈ s i ∧ Sigma.mk i '' t = u := by
      simpa only [mem_unionᵢ, mem_image] using hu
    exact isOpenMap_sigmaMk _ ((hs i).isOpen ts)
  · rintro ⟨i, x⟩ u hxu u_open
    have hx : x ∈ Sigma.mk i ⁻¹' u := hxu
    obtain ⟨v, vs, xv, hv⟩ : ∃ (v : Set (E i)), v ∈ s i ∧ x ∈ v ∧ v ⊆ Sigma.mk i ⁻¹' u :=
      (hs i).exists_subset_of_mem_open hx (isOpen_sigma_iff.1 u_open i)
    exact
      ⟨Sigma.mk i '' v, mem_unionᵢ.2 ⟨i, mem_image_of_mem _ vs⟩, mem_image_of_mem _ xv,
        image_subset_iff.2 hv⟩
#align topological_space.is_topological_basis.sigma TopologicalSpace.IsTopologicalBasis.sigma

/-- A countable disjoint union of second countable spaces is second countable. -/
instance [Countable ι] [∀ i, SecondCountableTopology (E i)] : SecondCountableTopology (Σi, E i) :=
  by
  let b := ⋃ i : ι, (fun u => (Sigma.mk i '' u : Set (Σi, E i))) '' countableBasis (E i)
  have A : IsTopologicalBasis b := IsTopologicalBasis.sigma fun i => isBasis_countableBasis _
  have B : b.Countable := countable_unionᵢ fun i => (countable_countableBasis _).image _
  exact A.secondCountableTopology B

end Sigma

section Sum

variable {β : Type _} [TopologicalSpace α] [TopologicalSpace β]

/-- In a sum space `α ⊕ β`, one can form a topological basis by taking the union of
topological bases on each of the two components. -/
theorem IsTopologicalBasis.sum {s : Set (Set α)} (hs : IsTopologicalBasis s) {t : Set (Set β)}
    (ht : IsTopologicalBasis t) :
    IsTopologicalBasis ((fun u => Sum.inl '' u) '' s ∪ (fun u => Sum.inr '' u) '' t) := by
  apply isTopologicalBasis_of_open_of_nhds
  · rintro u (⟨w, hw, rfl⟩ | ⟨w, hw, rfl⟩)
    · exact openEmbedding_inl.isOpenMap w (hs.isOpen hw)
    · exact openEmbedding_inr.isOpenMap w (ht.isOpen hw)
  · rintro (x | x) u hxu u_open
    · obtain ⟨v, vs, xv, vu⟩ : ∃ v ∈ s, x ∈ v ∧ v ⊆ Sum.inl ⁻¹' u :=
        hs.exists_subset_of_mem_open hxu (isOpen_sum_iff.1 u_open).1
      exact ⟨Sum.inl '' v, mem_union_left _ (mem_image_of_mem _ vs), mem_image_of_mem _ xv,
        image_subset_iff.2 vu⟩
    · obtain ⟨v, vs, xv, vu⟩ : ∃ v ∈ t, x ∈ v ∧ v ⊆ Sum.inr ⁻¹' u :=
        ht.exists_subset_of_mem_open hxu (isOpen_sum_iff.1 u_open).2
      exact ⟨Sum.inr '' v, mem_union_right _ (mem_image_of_mem _ vs), mem_image_of_mem _ xv,
        image_subset_iff.2 vu⟩
#align topological_space.is_topological_basis.sum TopologicalSpace.IsTopologicalBasis.sum

/-- A sum type of two second countable spaces is second countable. -/
instance [SecondCountableTopology α] [SecondCountableTopology β] :
    SecondCountableTopology (α ⊕ β) := by
  let b :=
    (fun u => Sum.inl '' u) '' countableBasis α ∪ (fun u => Sum.inr '' u) '' countableBasis β
  have A : IsTopologicalBasis b := (isBasis_countableBasis α).sum (isBasis_countableBasis β)
  have B : b.Countable :=
    (Countable.image (countable_countableBasis _) _).union
      (Countable.image (countable_countableBasis _) _)
  exact A.secondCountableTopology B

end Sum

section Quotient

variable {X : Type _} [TopologicalSpace X] {Y : Type _} [TopologicalSpace Y] {π : X → Y}

/-- The image of a topological basis under an open quotient map is a topological basis. -/
theorem IsTopologicalBasis.quotientMap {V : Set (Set X)} (hV : IsTopologicalBasis V)
    (h' : QuotientMap π) (h : IsOpenMap π) : IsTopologicalBasis (Set.image π '' V) := by
  apply isTopologicalBasis_of_open_of_nhds
  · rintro - ⟨U, U_in_V, rfl⟩
    apply h U (hV.isOpen U_in_V)
  · intro y U y_in_U U_open
    obtain ⟨x, rfl⟩ := h'.surjective y
    let W := π ⁻¹' U
    have x_in_W : x ∈ W := y_in_U
    have W_open : IsOpen W := U_open.preimage h'.continuous
    obtain ⟨Z, Z_in_V, x_in_Z, Z_in_W⟩ := hV.exists_subset_of_mem_open x_in_W W_open
    have πZ_in_U : π '' Z ⊆ U := (Set.image_subset _ Z_in_W).trans (image_preimage_subset π U)
    exact ⟨π '' Z, ⟨Z, Z_in_V, rfl⟩, ⟨x, x_in_Z, rfl⟩, πZ_in_U⟩
#align topological_space.is_topological_basis.quotient_map TopologicalSpace.IsTopologicalBasis.quotientMap

/-- A second countable space is mapped by an open quotient map to a second countable space. -/
theorem _root_.QuotientMap.secondCountableTopology [SecondCountableTopology X] (h' : QuotientMap π)
    (h : IsOpenMap π) : SecondCountableTopology Y where
  is_open_generated_countable := by
    obtain ⟨V, V_countable, -, V_generates⟩ := exists_countable_basis X
    exact ⟨Set.image π '' V, V_countable.image (Set.image π),
      (V_generates.quotientMap h' h).eq_generateFrom⟩
#align topological_space.quotient_map.second_countable_topology QuotientMap.secondCountableTopology

variable {S : Setoid X}

/-- The image of a topological basis "downstairs" in an open quotient is a topological basis. -/
theorem IsTopologicalBasis.quotient {V : Set (Set X)} (hV : IsTopologicalBasis V)
    (h : IsOpenMap (Quotient.mk' : X → Quotient S)) :
    IsTopologicalBasis (Set.image (Quotient.mk' : X → Quotient S) '' V) :=
  hV.quotientMap quotientMap_quotient_mk' h
#align topological_space.is_topological_basis.quotient TopologicalSpace.IsTopologicalBasis.quotient

/-- An open quotient of a second countable space is second countable. -/

end Quotient

end TopologicalSpace

open TopologicalSpace

variable {α β : Type _} [TopologicalSpace α] [TopologicalSpace β] {f : α → β}

protected theorem Inducing.secondCountableTopology [SecondCountableTopology β] (hf : Inducing f) :
    SecondCountableTopology α := by
  rw [hf.1]
  exact secondCountableTopology_induced α β f
#align inducing.second_countable_topology Inducing.secondCountableTopology

protected theorem Embedding.secondCountableTopology [SecondCountableTopology β] (hf : Embedding f) :
    SecondCountableTopology α :=
  hf.1.secondCountableTopology
#align embedding.second_countable_topology Embedding.secondCountableTopology


/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import data.prod
import data.subtype
import logic.function.basic
import logic.unique

/-!
# Nontrivial types

A type is *nontrivial* if it contains at least two elements. This is useful in particular for rings
(where it is equivalent to the fact that zero is different from one) and for vector spaces
(where it is equivalent to the fact that the dimension is positive).

We introduce a typeclass `nontrivial` formalizing this property.
-/

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

open_locale classical

/-- Predicate typeclass for expressing that a type is not reduced to a single element. In rings,
this is equivalent to `0 ≠ 1`. In vector spaces, this is equivalent to positive dimension. -/
class nontrivial (α : Type*) : Prop :=
(exists_pair_ne : ∃ (x y : α), x ≠ y)

lemma nontrivial_iff : nontrivial α ↔ ∃ (x y : α), x ≠ y :=
⟨λ h, h.exists_pair_ne, λ h, ⟨h⟩⟩

lemma exists_pair_ne (α : Type*) [nontrivial α] : ∃ (x y : α), x ≠ y :=
nontrivial.exists_pair_ne

-- See Note [decidable namespace]
protected lemma decidable.exists_ne [nontrivial α] [decidable_eq α] (x : α) : ∃ y, y ≠ x :=
begin
  rcases exists_pair_ne α with ⟨y, y', h⟩,
  by_cases hx : x = y,
  { rw ← hx at h,
    exact ⟨y', h.symm⟩ },
  { exact ⟨y, ne.symm hx⟩ }
end

lemma exists_ne [nontrivial α] (x : α) : ∃ y, y ≠ x :=
by classical; exact decidable.exists_ne x

-- `x` and `y` are explicit here, as they are often needed to guide typechecking of `h`.
lemma nontrivial_of_ne (x y : α) (h : x ≠ y) : nontrivial α :=
⟨⟨x, y, h⟩⟩

-- `x` and `y` are explicit here, as they are often needed to guide typechecking of `h`.
lemma nontrivial_of_lt [preorder α] (x y : α) (h : x < y) : nontrivial α :=
⟨⟨x, y, ne_of_lt h⟩⟩

lemma exists_pair_lt (α : Type*) [nontrivial α] [linear_order α] : ∃ (x y : α), x < y :=
begin
  rcases exists_pair_ne α with ⟨x, y, hxy⟩,
  cases lt_or_gt_of_ne hxy;
  exact ⟨_, _, h⟩
end

lemma nontrivial_iff_lt [linear_order α] : nontrivial α ↔ ∃ (x y : α), x < y :=
⟨λ h, @exists_pair_lt α h _, λ ⟨x, y, h⟩, nontrivial_of_lt x y h⟩

lemma nontrivial_iff_exists_ne (x : α) : nontrivial α ↔ ∃ y, y ≠ x :=
⟨λ h, @exists_ne α h x, λ ⟨y, hy⟩, nontrivial_of_ne _ _ hy⟩

lemma subtype.nontrivial_iff_exists_ne (p : α → Prop) (x : subtype p) :
  nontrivial (subtype p) ↔ ∃ (y : α) (hy : p y), y ≠ x :=
by simp only [nontrivial_iff_exists_ne x, subtype.exists, ne.def, subtype.ext_iff, subtype.coe_mk]

instance : nontrivial Prop := ⟨⟨true, false, true_ne_false⟩⟩

/--
See Note [lower instance priority]

Note that since this and `nonempty_of_inhabited` are the most "obvious" way to find a nonempty
instance if no direct instance can be found, we give this a higher priority than the usual `100`.
-/
@[priority 500]
instance nontrivial.to_nonempty [nontrivial α] : nonempty α :=
let ⟨x, _⟩ := exists_pair_ne α in ⟨x⟩

attribute [instance, priority 500] nonempty_of_inhabited

/-- An inhabited type is either nontrivial, or has a unique element. -/
noncomputable def nontrivial_psum_unique (α : Type*) [inhabited α] :
  psum (nontrivial α) (unique α) :=
if h : nontrivial α then psum.inl h else psum.inr
{ default := default,
  uniq := λ (x : α),
  begin
    change x = default,
    contrapose! h,
    use [x, default]
  end }

lemma subsingleton_iff : subsingleton α ↔ ∀ (x y : α), x = y :=
⟨by { introsI h, exact subsingleton.elim }, λ h, ⟨h⟩⟩

lemma not_nontrivial_iff_subsingleton : ¬(nontrivial α) ↔ subsingleton α :=
by { rw [nontrivial_iff, subsingleton_iff], push_neg, refl }

lemma not_subsingleton (α) [h : nontrivial α] : ¬subsingleton α :=
let ⟨⟨x, y, hxy⟩⟩ := h in λ ⟨h'⟩, hxy $ h' x y

/-- A type is either a subsingleton or nontrivial. -/
lemma subsingleton_or_nontrivial (α : Type*) : subsingleton α ∨ nontrivial α :=
by { rw [← not_nontrivial_iff_subsingleton, or_comm], exact classical.em _ }

lemma false_of_nontrivial_of_subsingleton (α : Type*) [nontrivial α] [subsingleton α] : false :=
let ⟨x, y, h⟩ := exists_pair_ne α in h $ subsingleton.elim x y

instance option.nontrivial [nonempty α] : nontrivial (option α) :=
by { inhabit α, use [none, some default] }

/-- Pushforward a `nontrivial` instance along an injective function. -/
protected lemma function.injective.nontrivial [nontrivial α]
  {f : α → β} (hf : function.injective f) : nontrivial β :=
let ⟨x, y, h⟩ := exists_pair_ne α in ⟨⟨f x, f y, hf.ne h⟩⟩

/-- Pullback a `nontrivial` instance along a surjective function. -/
protected lemma function.surjective.nontrivial [nontrivial β]
  {f : α → β} (hf : function.surjective f) : nontrivial α :=
begin
  rcases exists_pair_ne β with ⟨x, y, h⟩,
  rcases hf x with ⟨x', hx'⟩,
  rcases hf y with ⟨y', hy'⟩,
  have : x' ≠ y', by { contrapose! h, rw [← hx', ← hy', h] },
  exact ⟨⟨x', y', this⟩⟩
end

/-- An injective function from a nontrivial type has an argument at
which it does not take a given value. -/
protected lemma function.injective.exists_ne [nontrivial α] {f : α → β}
  (hf : function.injective f) (y : β) : ∃ x, f x ≠ y :=
begin
  rcases exists_pair_ne α with ⟨x₁, x₂, hx⟩,
  by_cases h : f x₂ = y,
  { exact ⟨x₁, (hf.ne_iff' h).2 hx⟩ },
  { exact ⟨x₂, h⟩ }
end

instance nontrivial_prod_right [nonempty α] [nontrivial β] : nontrivial (α × β) :=
prod.snd_surjective.nontrivial

instance nontrivial_prod_left [nontrivial α] [nonempty β] : nontrivial (α × β) :=
prod.fst_surjective.nontrivial

namespace pi

variables {I : Type*} {f : I → Type*}

/-- A pi type is nontrivial if it's nonempty everywhere and nontrivial somewhere. -/
lemma nontrivial_at (i' : I) [inst : Π i, nonempty (f i)] [nontrivial (f i')] :
  nontrivial (Π i : I, f i) :=
by classical; exact
(function.update_injective (λ i, classical.choice (inst i)) i').nontrivial

/--
As a convenience, provide an instance automatically if `(f default)` is nontrivial.

If a different index has the non-trivial type, then use `haveI := nontrivial_at that_index`.
-/
instance nontrivial [inhabited I] [inst : Π i, nonempty (f i)] [nontrivial (f default)] :
  nontrivial (Π i : I, f i) := nontrivial_at default

end pi

instance function.nontrivial [h : nonempty α] [nontrivial β] : nontrivial (α → β) :=
h.elim $ λ a, pi.nontrivial_at a

mk_simp_attribute nontriviality "Simp lemmas for `nontriviality` tactic"

protected lemma subsingleton.le [preorder α] [subsingleton α] (x y : α) : x ≤ y :=
le_of_eq (subsingleton.elim x y)

attribute [nontriviality] eq_iff_true_of_subsingleton subsingleton.le

namespace tactic

/--
Tries to generate a `nontrivial α` instance by performing case analysis on
`subsingleton_or_nontrivial α`,
attempting to discharge the subsingleton branch using lemmas with `@[nontriviality]` attribute,
including `subsingleton.le` and `eq_iff_true_of_subsingleton`.
-/
meta def nontriviality_by_elim (α : expr) (lems : interactive.parse simp_arg_list) : tactic unit :=
do
  alternative ← to_expr ``(subsingleton_or_nontrivial %%α),
  n ← get_unused_name "_inst",
  tactic.cases alternative [n, n],
  (solve1 $ do
    reset_instance_cache,
    apply_instance <|>
      interactive.simp none none ff lems [`nontriviality] (interactive.loc.ns [none])) <|>
      fail format!"Could not prove goal assuming `subsingleton {α}`",
  reset_instance_cache

/--
Tries to generate a `nontrivial α` instance using `nontrivial_of_ne` or `nontrivial_of_lt`
and local hypotheses.
-/
meta def nontriviality_by_assumption (α : expr) : tactic unit :=
do
  n ← get_unused_name "_inst",
  to_expr ``(nontrivial %%α) >>= assert n,
  apply_instance <|> `[solve_by_elim [nontrivial_of_ne, nontrivial_of_lt]],
  reset_instance_cache

end tactic

namespace tactic.interactive

open tactic

setup_tactic_parser

/--
Attempts to generate a `nontrivial α` hypothesis.

The tactic first looks for an instance using `apply_instance`.

If the goal is an (in)equality, the type `α` is inferred from the goal.
Otherwise, the type needs to be specified in the tactic invocation, as `nontriviality α`.

The `nontriviality` tactic will first look for strict inequalities amongst the hypotheses,
and use these to derive the `nontrivial` instance directly.

Otherwise, it will perform a case split on `subsingleton α ∨ nontrivial α`, and attempt to discharge
the `subsingleton` goal using `simp [lemmas] with nontriviality`, where `[lemmas]` is a list of
additional `simp` lemmas that can be passed to `nontriviality` using the syntax
`nontriviality α using [lemmas]`.

```
example {R : Type} [ordered_ring R] {a : R} (h : 0 < a) : 0 < a :=
begin
  nontriviality, -- There is now a `nontrivial R` hypothesis available.
  assumption,
end
```

```
example {R : Type} [comm_ring R] {r s : R} : r * s = s * r :=
begin
  nontriviality, -- There is now a `nontrivial R` hypothesis available.
  apply mul_comm,
end
```

```
example {R : Type} [ordered_ring R] {a : R} (h : 0 < a) : (2 : ℕ) ∣ 4 :=
begin
  nontriviality R, -- there is now a `nontrivial R` hypothesis available.
  dec_trivial
end
```

```
def myeq {α : Type} (a b : α) : Prop := a = b

example {α : Type} (a b : α) (h : a = b) : myeq a b :=
begin
  success_if_fail { nontriviality α }, -- Fails
  nontriviality α using [myeq], -- There is now a `nontrivial α` hypothesis available
  assumption
end
```
-/
meta def nontriviality (t : parse texpr?)
  (lems : parse (tk "using" *> simp_arg_list <|> pure [])) :
  tactic unit :=
do
  α ← match t with
  | some α := to_expr α
  | none :=
    (do t ← mk_mvar, e ← to_expr ``(@eq %%t _ _), target >>= unify e, return t) <|>
    (do t ← mk_mvar, e ← to_expr ``(@has_le.le %%t _ _ _), target >>= unify e, return t) <|>
    (do t ← mk_mvar, e ← to_expr ``(@ne %%t _ _), target >>= unify e, return t) <|>
    (do t ← mk_mvar, e ← to_expr ``(@has_lt.lt %%t _ _ _), target >>= unify e, return t) <|>
    fail "The goal is not an (in)equality, so you'll need to specify the desired `nontrivial α`
      instance by invoking `nontriviality α`."
  end,
  nontriviality_by_assumption α <|> nontriviality_by_elim α lems

add_tactic_doc
{ name                     := "nontriviality",
  category                 := doc_category.tactic,
  decl_names               := [`tactic.interactive.nontriviality],
  tags                     := ["logic", "type class"] }

end tactic.interactive

namespace bool

instance : nontrivial bool := ⟨⟨tt,ff, tt_eq_ff_eq_false⟩⟩

end bool


/-
Copyright (c) 2023 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module measure_theory.integral.peak_function
! leanprover-community/mathlib commit 59694bd07f0a39c5beccba34bd9f413a160782bf
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Integral.SetIntegral
import Mathbin.MeasureTheory.Function.LocallyIntegrable

/-!
# Integrals against peak functions

A sequence of peak functions is a sequence of functions with average one concentrating around
a point `x₀`. Given such a sequence `φₙ`, then `∫ φₙ g` tends to `g x₀` in many situations, with
a whole zoo of possible assumptions on `φₙ` and `g`. This file is devoted to such results.

## Main results

* `tendsto_set_integral_peak_smul_of_integrable_on_of_continuous_within_at`: If a sequence of peak
  functions `φᵢ` converges uniformly to zero away from a point `x₀`, and
  `g` is integrable and continuous at `x₀`, then `∫ φᵢ • g` converges to `g x₀`.
* `tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_continuous_on`:
  If a continuous function `c` realizes its maximum at a unique point `x₀` in a compact set `s`,
  then the sequence of functions `(c x) ^ n / ∫ (c x) ^ n` is a sequence of peak functions
  concentrating around `x₀`. Therefore, `∫ (c x) ^ n * g / ∫ (c x) ^ n` converges to `g x₀`
  if `g` is continuous on `s`.

Note that there are related results about convolution with respect to peak functions in the file
`analysis.convolution`, such as `convolution_tendsto_right` there.
-/

open Set Filter MeasureTheory MeasureTheory.Measure TopologicalSpace Metric

open Topology ENNReal

/-- This lemma exists for finsets, but not for sets currently. porting note: move to
data.set.basic after the port. -/
theorem Set.disjoint_sdiff_inter {α : Type _} (s t : Set α) : Disjoint (s \ t) (s ∩ t) :=
  disjoint_of_subset_right (inter_subset_right _ _) disjoint_sdiff_left
#align set.disjoint_sdiff_inter Set.disjoint_sdiff_inter

open Set

variable {α E ι : Type _} {hm : MeasurableSpace α} {μ : Measure α} [TopologicalSpace α]
  [BorelSpace α] [NormedAddCommGroup E] [NormedSpace ℝ E] {g : α → E} {l : Filter ι} {x₀ : α}
  {s : Set α} {φ : ι → α → ℝ}

/-- If a sequence of peak functions `φᵢ` converges uniformly to zero away from a point `x₀`, and
`g` is integrable and continuous at `x₀`, then `φᵢ • g` is eventually integrable. -/
theorem integrableOn_peak_smul_of_integrableOn_of_continuousWithinAt (hs : MeasurableSet s)
    (hlφ : ∀ u : Set α, IsOpen u → x₀ ∈ u → TendstoUniformlyOn φ 0 l (s \ u))
    (hiφ : ∀ᶠ i in l, (∫ x in s, φ i x ∂μ) = 1) (hmg : IntegrableOn g s μ)
    (hcg : ContinuousWithinAt g s x₀) : ∀ᶠ i in l, IntegrableOn (fun x => φ i x • g x) s μ :=
  by
  obtain ⟨u, u_open, x₀u, hu⟩ : ∃ u, IsOpen u ∧ x₀ ∈ u ∧ ∀ x ∈ u ∩ s, g x ∈ ball (g x₀) 1
  exact mem_nhdsWithin.1 (hcg (ball_mem_nhds _ zero_lt_one))
  filter_upwards [tendsto_uniformly_on_iff.1 (hlφ u u_open x₀u) 1 zero_lt_one, hiφ]with i hi h'i
  have A : integrable_on (fun x => φ i x • g x) (s \ u) μ :=
    by
    apply integrable.smul_of_top_right (hmg.mono (diff_subset _ _) le_rfl)
    apply
      mem_ℒp_top_of_bound
        ((integrable_of_integral_eq_one h'i).AeStronglyMeasurable.monoSet (diff_subset _ _)) 1
    filter_upwards [self_mem_ae_restrict (hs.diff u_open.measurable_set)]with x hx
    simpa only [Pi.zero_apply, dist_zero_left] using (hi x hx).le
  have B : integrable_on (fun x => φ i x • g x) (s ∩ u) μ :=
    by
    apply integrable.smul_of_top_left
    · exact integrable_on.mono_set (integrable_of_integral_eq_one h'i) (inter_subset_left _ _)
    · apply
        mem_ℒp_top_of_bound (hmg.mono_set (inter_subset_left _ _)).AeStronglyMeasurable (‖g x₀‖ + 1)
      filter_upwards [self_mem_ae_restrict (hs.inter u_open.measurable_set)]with x hx
      rw [inter_comm] at hx
      exact (norm_lt_of_mem_ball (hu x hx)).le
  convert A.union B
  simp only [diff_union_inter]
#align integrable_on_peak_smul_of_integrable_on_of_continuous_within_at integrableOn_peak_smul_of_integrableOn_of_continuousWithinAt

variable [CompleteSpace E]

/-- If a sequence of peak functions `φᵢ` converges uniformly to zero away from a point `x₀`, and
`g` is integrable and continuous at `x₀`, then `∫ φᵢ • g` converges to `x₀`. Auxiliary lemma
where one assumes additionally `g x₀ = 0`. -/
theorem tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt_aux
    (hs : MeasurableSet s) (hnφ : ∀ᶠ i in l, ∀ x ∈ s, 0 ≤ φ i x)
    (hlφ : ∀ u : Set α, IsOpen u → x₀ ∈ u → TendstoUniformlyOn φ 0 l (s \ u))
    (hiφ : ∀ᶠ i in l, (∫ x in s, φ i x ∂μ) = 1) (hmg : IntegrableOn g s μ) (h'g : g x₀ = 0)
    (hcg : ContinuousWithinAt g s x₀) : Tendsto (fun i : ι => ∫ x in s, φ i x • g x ∂μ) l (𝓝 0) :=
  by
  refine' Metric.tendsto_nhds.2 fun ε εpos => _
  obtain ⟨δ, hδ, δpos⟩ : ∃ δ, (δ * ∫ x in s, ‖g x‖ ∂μ) + δ < ε ∧ 0 < δ :=
    by
    have A :
      tendsto (fun δ => (δ * ∫ x in s, ‖g x‖ ∂μ) + δ) (𝓝[>] 0) (𝓝 ((0 * ∫ x in s, ‖g x‖ ∂μ) + 0)) :=
      by
      apply tendsto.mono_left _ nhdsWithin_le_nhds
      exact (tendsto_id.mul tendsto_const_nhds).add tendsto_id
    rw [MulZeroClass.zero_mul, zero_add] at A
    exact (((tendsto_order.1 A).2 ε εpos).And self_mem_nhdsWithin).exists
  suffices ∀ᶠ i in l, ‖∫ x in s, φ i x • g x ∂μ‖ ≤ (δ * ∫ x in s, ‖g x‖ ∂μ) + δ
    by
    filter_upwards [this]with i hi
    simp only [dist_zero_right]
    exact hi.trans_lt hδ
  obtain ⟨u, u_open, x₀u, hu⟩ : ∃ u, IsOpen u ∧ x₀ ∈ u ∧ ∀ x ∈ u ∩ s, g x ∈ ball (g x₀) δ
  exact mem_nhdsWithin.1 (hcg (ball_mem_nhds _ δpos))
  filter_upwards [tendsto_uniformly_on_iff.1 (hlφ u u_open x₀u) δ δpos, hiφ, hnφ,
    integrableOn_peak_smul_of_integrableOn_of_continuousWithinAt hs hlφ hiφ hmg
      hcg]with i hi h'i hφpos h''i
  have B : ‖∫ x in s ∩ u, φ i x • g x ∂μ‖ ≤ δ :=
    calc
      ‖∫ x in s ∩ u, φ i x • g x ∂μ‖ ≤ ∫ x in s ∩ u, ‖φ i x • g x‖ ∂μ :=
        norm_integral_le_integral_norm _
      _ ≤ ∫ x in s ∩ u, ‖φ i x‖ * δ ∂μ :=
        by
        refine' set_integral_mono_on _ _ (hs.inter u_open.measurable_set) fun x hx => _
        · exact integrable_on.mono_set h''i.norm (inter_subset_left _ _)
        ·
          exact
            integrable_on.mono_set ((integrable_of_integral_eq_one h'i).norm.mul_const _)
              (inter_subset_left _ _)
        rw [norm_smul]
        apply mul_le_mul_of_nonneg_left _ (norm_nonneg _)
        rw [inter_comm, h'g] at hu
        exact (mem_ball_zero_iff.1 (hu x hx)).le
      _ ≤ ∫ x in s, ‖φ i x‖ * δ ∂μ := by
        apply set_integral_mono_set
        · exact (integrable_of_integral_eq_one h'i).norm.mul_const _
        · exact eventually_of_forall fun x => mul_nonneg (norm_nonneg _) δpos.le
        · apply eventually_of_forall
          exact inter_subset_left s u
      _ = ∫ x in s, φ i x * δ ∂μ :=
        by
        apply set_integral_congr hs fun x hx => _
        rw [Real.norm_of_nonneg (hφpos _ hx)]
      _ = δ := by rw [integral_mul_right, h'i, one_mul]

  have C : ‖∫ x in s \ u, φ i x • g x ∂μ‖ ≤ δ * ∫ x in s, ‖g x‖ ∂μ :=
    calc
      ‖∫ x in s \ u, φ i x • g x ∂μ‖ ≤ ∫ x in s \ u, ‖φ i x • g x‖ ∂μ :=
        norm_integral_le_integral_norm _
      _ ≤ ∫ x in s \ u, δ * ‖g x‖ ∂μ :=
        by
        refine' set_integral_mono_on _ _ (hs.diff u_open.measurable_set) fun x hx => _
        · exact integrable_on.mono_set h''i.norm (diff_subset _ _)
        · exact integrable_on.mono_set (hmg.norm.const_mul _) (diff_subset _ _)
        rw [norm_smul]
        apply mul_le_mul_of_nonneg_right _ (norm_nonneg _)
        simpa only [Pi.zero_apply, dist_zero_left] using (hi x hx).le
      _ ≤ δ * ∫ x in s, ‖g x‖ ∂μ := by
        rw [integral_mul_left]
        apply mul_le_mul_of_nonneg_left (set_integral_mono_set hmg.norm _ _) δpos.le
        · exact eventually_of_forall fun x => norm_nonneg _
        · apply eventually_of_forall
          exact diff_subset s u

  calc
    ‖∫ x in s, φ i x • g x ∂μ‖ = ‖(∫ x in s \ u, φ i x • g x ∂μ) + ∫ x in s ∩ u, φ i x • g x ∂μ‖ :=
      by
      conv_lhs => rw [← diff_union_inter s u]
      rw [integral_union (disjoint_sdiff_inter _ _) (hs.inter u_open.measurable_set)
          (h''i.mono_set (diff_subset _ _)) (h''i.mono_set (inter_subset_left _ _))]
    _ ≤ ‖∫ x in s \ u, φ i x • g x ∂μ‖ + ‖∫ x in s ∩ u, φ i x • g x ∂μ‖ := (norm_add_le _ _)
    _ ≤ (δ * ∫ x in s, ‖g x‖ ∂μ) + δ := add_le_add C B

#align tendsto_set_integral_peak_smul_of_integrable_on_of_continuous_within_at_aux tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt_aux

/- If a sequence of peak functions `φᵢ` converges uniformly to zero away from a point `x₀`, and
`g` is integrable and continuous at `x₀`, then `∫ φᵢ • g` converges to `x₀`. -/
theorem tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt (hs : MeasurableSet s)
    (h's : μ s ≠ ∞) (hnφ : ∀ᶠ i in l, ∀ x ∈ s, 0 ≤ φ i x)
    (hlφ : ∀ u : Set α, IsOpen u → x₀ ∈ u → TendstoUniformlyOn φ 0 l (s \ u))
    (hiφ : (fun i => ∫ x in s, φ i x ∂μ) =ᶠ[l] 1) (hmg : IntegrableOn g s μ)
    (hcg : ContinuousWithinAt g s x₀) :
    Tendsto (fun i : ι => ∫ x in s, φ i x • g x ∂μ) l (𝓝 (g x₀)) :=
  by
  let h := g - fun y => g x₀
  have A :
    tendsto (fun i : ι => (∫ x in s, φ i x • h x ∂μ) + (∫ x in s, φ i x ∂μ) • g x₀) l
      (𝓝 (0 + (1 : ℝ) • g x₀)) :=
    by
    refine' tendsto.add _ (tendsto.smul (tendsto_const_nhds.congr' hiφ.symm) tendsto_const_nhds)
    apply tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt_aux hs hnφ hlφ hiφ
    · apply integrable.sub hmg
      apply integrable_on_const.2
      simp only [h's.lt_top, or_true_iff]
    · simp only [h, Pi.sub_apply, sub_self]
    · exact hcg.sub continuousWithinAt_const
  simp only [one_smul, zero_add] at A
  refine' tendsto.congr' _ A
  filter_upwards [integrableOn_peak_smul_of_integrableOn_of_continuousWithinAt hs hlφ hiφ hmg hcg,
    hiφ]with i hi h'i
  simp only [h, Pi.sub_apply, smul_sub]
  rw [integral_sub hi, integral_smul_const, sub_add_cancel]
  exact integrable.smul_const (integrable_of_integral_eq_one h'i) _
#align tendsto_set_integral_peak_smul_of_integrable_on_of_continuous_within_at tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt

/-- If a continuous function `c` realizes its maximum at a unique point `x₀` in a compact set `s`,
then the sequence of functions `(c x) ^ n / ∫ (c x) ^ n` is a sequence of peak functions
concentrating around `x₀`. Therefore, `∫ (c x) ^ n * g / ∫ (c x) ^ n` converges to `g x₀` if `g` is
integrable on `s` and continuous at `x₀`.

Version assuming that `μ` gives positive mass to all neighborhoods of `x₀` within `s`.
For a less precise but more usable version, see
`tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_continuous_on`.
 -/
theorem tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_measure_nhds_within_pos
    [MetrizableSpace α] [IsLocallyFiniteMeasure μ] (hs : IsCompact s)
    (hμ : ∀ u, IsOpen u → x₀ ∈ u → 0 < μ (u ∩ s)) {c : α → ℝ} (hc : ContinuousOn c s)
    (h'c : ∀ y ∈ s, y ≠ x₀ → c y < c x₀) (hnc : ∀ x ∈ s, 0 ≤ c x) (hnc₀ : 0 < c x₀) (h₀ : x₀ ∈ s)
    (hmg : IntegrableOn g s μ) (hcg : ContinuousWithinAt g s x₀) :
    Tendsto (fun n : ℕ => (∫ x in s, c x ^ n ∂μ)⁻¹ • ∫ x in s, c x ^ n • g x ∂μ) atTop (𝓝 (g x₀)) :=
  by
  /- We apply the general result
    `tendsto_set_integral_peak_smul_of_integrable_on_of_continuous_within_at` to the sequence of
    peak functions `φₙ = (c x) ^ n / ∫ (c x) ^ n`. The only nontrivial bit is to check that this
    sequence converges uniformly to zero on any set `s \ u` away from `x₀`. By compactness, the
    function `c` is bounded by `t < c x₀` there. Consider `t' ∈ (t, c x₀)`, and a neighborhood `v`
    of `x₀` where `c x ≥ t'`, by continuity. Then `∫ (c x) ^ n` is bounded below by `t' ^ n μ v`.
    It follows that, on `s \ u`, then `φₙ x ≤ t ^ n / (t' ^ n μ v)`, which tends (exponentially fast)
    to zero with `n`. -/
  let φ : ℕ → α → ℝ := fun n x => (∫ x in s, c x ^ n ∂μ)⁻¹ * c x ^ n
  have hnφ : ∀ n, ∀ x ∈ s, 0 ≤ φ n x := by
    intro n x hx
    apply mul_nonneg (inv_nonneg.2 _) (pow_nonneg (hnc x hx) _)
    exact set_integral_nonneg hs.measurable_set fun x hx => pow_nonneg (hnc x hx) _
  have I : ∀ n, integrable_on (fun x => c x ^ n) s μ := fun n =>
    ContinuousOn.integrableOnCompact hs (hc.pow n)
  have J : ∀ n, 0 ≤ᵐ[μ.restrict s] fun x : α => c x ^ n :=
    by
    intro n
    filter_upwards [ae_restrict_mem hs.measurable_set]with x hx
    exact pow_nonneg (hnc x hx) n
  have P : ∀ n, 0 < ∫ x in s, c x ^ n ∂μ := by
    intro n
    refine' (set_integral_pos_iff_support_of_nonneg_ae (J n) (I n)).2 _
    obtain ⟨u, u_open, x₀_u, hu⟩ : ∃ u : Set α, IsOpen u ∧ x₀ ∈ u ∧ u ∩ s ⊆ c ⁻¹' Ioi 0 :=
      _root_.continuous_on_iff.1 hc x₀ h₀ (Ioi (0 : ℝ)) isOpen_Ioi hnc₀
    apply (hμ u u_open x₀_u).trans_le
    exact measure_mono fun x hx => ⟨ne_of_gt (pow_pos (hu hx) _), hx.2⟩
  have hiφ : ∀ n, (∫ x in s, φ n x ∂μ) = 1 := fun n => by
    rw [integral_mul_left, inv_mul_cancel (P n).ne']
  have A : ∀ u : Set α, IsOpen u → x₀ ∈ u → TendstoUniformlyOn φ 0 at_top (s \ u) :=
    by
    intro u u_open x₀u
    obtain ⟨t, t_pos, tx₀, ht⟩ : ∃ t, 0 ≤ t ∧ t < c x₀ ∧ ∀ x ∈ s \ u, c x ≤ t :=
      by
      rcases eq_empty_or_nonempty (s \ u) with (h | h)
      ·
        exact
          ⟨0, le_rfl, hnc₀, by simp only [h, mem_empty_iff_false, IsEmpty.forall_iff, imp_true_iff]⟩
      obtain ⟨x, hx, h'x⟩ : ∃ x ∈ s \ u, ∀ y ∈ s \ u, c y ≤ c x :=
        IsCompact.exists_forall_ge (hs.diff u_open) h (hc.mono (diff_subset _ _))
      refine' ⟨c x, hnc x hx.1, h'c x hx.1 _, h'x⟩
      rintro rfl
      exact hx.2 x₀u
    obtain ⟨t', tt', t'x₀⟩ : ∃ t', t < t' ∧ t' < c x₀ := exists_between tx₀
    have t'_pos : 0 < t' := t_pos.trans_lt tt'
    obtain ⟨v, v_open, x₀_v, hv⟩ : ∃ v : Set α, IsOpen v ∧ x₀ ∈ v ∧ v ∩ s ⊆ c ⁻¹' Ioi t' :=
      _root_.continuous_on_iff.1 hc x₀ h₀ (Ioi t') isOpen_Ioi t'x₀
    have M : ∀ n, ∀ x ∈ s \ u, φ n x ≤ (μ (v ∩ s)).toReal⁻¹ * (t / t') ^ n :=
      by
      intro n x hx
      have B : t' ^ n * (μ (v ∩ s)).toReal ≤ ∫ y in s, c y ^ n ∂μ :=
        calc
          t' ^ n * (μ (v ∩ s)).toReal = ∫ y in v ∩ s, t' ^ n ∂μ := by
            simp only [integral_const, measure.restrict_apply, MeasurableSet.univ, univ_inter,
              Algebra.id.smul_eq_mul, mul_comm]
          _ ≤ ∫ y in v ∩ s, c y ^ n ∂μ :=
            by
            apply set_integral_mono_on _ _ (v_open.measurable_set.inter hs.measurable_set) _
            · apply integrable_on_const.2 (Or.inr _)
              exact lt_of_le_of_lt (measure_mono (inter_subset_right _ _)) hs.measure_lt_top
            · exact (I n).mono (inter_subset_right _ _) le_rfl
            · intro x hx
              exact pow_le_pow_of_le_left t'_pos.le (le_of_lt (hv hx)) _
          _ ≤ ∫ y in s, c y ^ n ∂μ :=
            set_integral_mono_set (I n) (J n) (eventually_of_forall (inter_subset_right _ _))

      simp_rw [φ, ← div_eq_inv_mul, div_pow, div_div]
      apply div_le_div (pow_nonneg t_pos n) _ _ B
      · exact pow_le_pow_of_le_left (hnc _ hx.1) (ht x hx) _
      · apply mul_pos (pow_pos (t_pos.trans_lt tt') _) (ENNReal.toReal_pos (hμ v v_open x₀_v).ne' _)
        have : μ (v ∩ s) ≤ μ s := measure_mono (inter_subset_right _ _)
        exact ne_of_lt (lt_of_le_of_lt this hs.measure_lt_top)
    have N :
      tendsto (fun n => (μ (v ∩ s)).toReal⁻¹ * (t / t') ^ n) at_top
        (𝓝 ((μ (v ∩ s)).toReal⁻¹ * 0)) :=
      by
      apply tendsto.mul tendsto_const_nhds _
      · infer_instance
      apply tendsto_pow_atTop_nhds_0_of_lt_1 (div_nonneg t_pos t'_pos.le)
      exact (div_lt_one t'_pos).2 tt'
    rw [MulZeroClass.mul_zero] at N
    refine' tendsto_uniformly_on_iff.2 fun ε εpos => _
    filter_upwards [(tendsto_order.1 N).2 ε εpos]with n hn x hx
    simp only [Pi.zero_apply, dist_zero_left, Real.norm_of_nonneg (hnφ n x hx.1)]
    exact (M n x hx).trans_lt hn
  have : tendsto (fun i : ℕ => ∫ x : α in s, φ i x • g x ∂μ) at_top (𝓝 (g x₀)) :=
    tendsto_set_integral_peak_smul_of_integrableOn_of_continuousWithinAt hs.measurable_set
      hs.measure_lt_top.ne (eventually_of_forall hnφ) A (eventually_of_forall hiφ) hmg hcg
  convert this
  simp_rw [← smul_smul, integral_smul]
#align tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_measure_nhds_within_pos tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_measure_nhds_within_pos

/-- If a continuous function `c` realizes its maximum at a unique point `x₀` in a compact set `s`,
then the sequence of functions `(c x) ^ n / ∫ (c x) ^ n` is a sequence of peak functions
concentrating around `x₀`. Therefore, `∫ (c x) ^ n * g / ∫ (c x) ^ n` converges to `g x₀` if `g` is
integrable on `s` and continuous at `x₀`.

Version assuming that `μ` gives positive mass to all open sets.
For a less precise but more usable version, see
`tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_continuous_on`.
-/
theorem tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_integrableOn
    [MetrizableSpace α] [IsLocallyFiniteMeasure μ] [IsOpenPosMeasure μ] (hs : IsCompact s)
    {c : α → ℝ} (hc : ContinuousOn c s) (h'c : ∀ y ∈ s, y ≠ x₀ → c y < c x₀)
    (hnc : ∀ x ∈ s, 0 ≤ c x) (hnc₀ : 0 < c x₀) (h₀ : x₀ ∈ closure (interior s))
    (hmg : IntegrableOn g s μ) (hcg : ContinuousWithinAt g s x₀) :
    Tendsto (fun n : ℕ => (∫ x in s, c x ^ n ∂μ)⁻¹ • ∫ x in s, c x ^ n • g x ∂μ) atTop (𝓝 (g x₀)) :=
  by
  have : x₀ ∈ s := by
    rw [← hs.is_closed.closure_eq]
    exact closure_mono interior_subset h₀
  apply
    tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_measure_nhds_within_pos hs _ hc
      h'c hnc hnc₀ this hmg hcg
  intro u u_open x₀_u
  calc
    0 < μ (u ∩ interior s) :=
      (u_open.inter isOpen_interior).measure_pos μ (_root_.mem_closure_iff.1 h₀ u u_open x₀_u)
    _ ≤ μ (u ∩ s) := measure_mono (inter_subset_inter_right _ interior_subset)

#align tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_integrable_on tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_integrableOn

/-- If a continuous function `c` realizes its maximum at a unique point `x₀` in a compact set `s`,
then the sequence of functions `(c x) ^ n / ∫ (c x) ^ n` is a sequence of peak functions
concentrating around `x₀`. Therefore, `∫ (c x) ^ n * g / ∫ (c x) ^ n` converges to `g x₀` if `g` is
continuous on `s`. -/
theorem tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_continuousOn
    [MetrizableSpace α] [IsLocallyFiniteMeasure μ] [IsOpenPosMeasure μ] (hs : IsCompact s)
    {c : α → ℝ} (hc : ContinuousOn c s) (h'c : ∀ y ∈ s, y ≠ x₀ → c y < c x₀)
    (hnc : ∀ x ∈ s, 0 ≤ c x) (hnc₀ : 0 < c x₀) (h₀ : x₀ ∈ closure (interior s))
    (hmg : ContinuousOn g s) :
    Tendsto (fun n : ℕ => (∫ x in s, c x ^ n ∂μ)⁻¹ • ∫ x in s, c x ^ n • g x ∂μ) atTop (𝓝 (g x₀)) :=
  haveI : x₀ ∈ s := by
    rw [← hs.is_closed.closure_eq]
    exact closure_mono interior_subset h₀
  tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_integrableOn hs hc h'c hnc hnc₀ h₀
    (hmg.integrable_on_compact hs) (hmg x₀ this)
#align tendsto_set_integral_pow_smul_of_unique_maximum_of_is_compact_of_continuous_on tendsto_set_integral_pow_smul_of_unique_maximum_of_isCompact_of_continuousOn