/-
Copyright (c) 2018 Jan-David Salchow. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jan-David Salchow, Patrick Massot, Yury Kudryashov

! This file was ported from Lean 3 source module topology.sequences
! 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.Topology.SubsetProperties
import Mathbin.Topology.MetricSpace.Basic

/-!
# Sequences in topological spaces

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

In this file we define sequences in topological spaces and show how they are related to
filters and the topology.

## Main definitions

### Set operation
* `seq_closure s`: sequential closure of a set, the set of limits of sequences of points of `s`;

### Predicates

* `is_seq_closed s`: predicate saying that a set is sequentially closed, i.e., `seq_closure s ⊆ s`;
* `seq_continuous f`: predicate saying that a function is sequentially continuous, i.e.,
  for any sequence `u : ℕ → X` that converges to a point `x`, the sequence `f ∘ u` converges to
  `f x`;
* `is_seq_compact s`: predicate saying that a set is sequentially compact, i.e., every sequence
  taking values in `s` has a converging subsequence.

### Type classes

* `frechet_urysohn_space X`: a typeclass saying that a topological space is a *Fréchet-Urysohn
  space*, i.e., the sequential closure of any set is equal to its closure.
* `sequential_space X`: a typeclass saying that a topological space is a *sequential space*, i.e.,
  any sequentially closed set in this space is closed. This condition is weaker than being a
  Fréchet-Urysohn space.
* `seq_compact_space X`: a typeclass saying that a topological space is sequentially compact, i.e.,
  every sequence in `X` has a converging subsequence.

## Main results

* `seq_closure_subset_closure`: closure of a set includes its sequential closure;
* `is_closed.is_seq_closed`: a closed set is sequentially closed;
* `is_seq_closed.seq_closure_eq`: sequential closure of a sequentially closed set `s` is equal
  to `s`;
* `seq_closure_eq_closure`: in a Fréchet-Urysohn space, the sequential closure of a set is equal to
  its closure;
* `tendsto_nhds_iff_seq_tendsto`, `frechet_urysohn_space.of_seq_tendsto_imp_tendsto`: a topological
  space is a Fréchet-Urysohn space if and only if sequential convergence implies convergence;
* `topological_space.first_countable_topology.frechet_urysohn_space`: every topological space with
  first countable topology is a Fréchet-Urysohn space;
* `frechet_urysohn_space.to_sequential_space`: every Fréchet-Urysohn space is a sequential space;
* `is_seq_compact.is_compact`: a sequentially compact set in a uniform space with countably
  generated uniformity is compact.

## Tags

sequentially closed, sequentially compact, sequential space
-/

open Set Function Filter TopologicalSpace

open Topology Filter

variable {X Y : Type _}

/-! ### Sequential closures, sequential continuity, and sequential spaces. -/

section TopologicalSpace

variable [TopologicalSpace X] [TopologicalSpace Y]

#print seqClosure /-
/-- The sequential closure of a set `s : set X` in a topological space `X` is the set of all `a : X`
which arise as limit of sequences in `s`. Note that the sequential closure of a set is not
guaranteed to be sequentially closed. -/
def seqClosure (s : Set X) : Set X :=
  { a | ∃ x : ℕ → X, (∀ n : ℕ, x n ∈ s) ∧ Tendsto x atTop (𝓝 a) }
#align seq_closure seqClosure
-/

#print subset_seqClosure /-
theorem subset_seqClosure {s : Set X} : s ⊆ seqClosure s := fun p hp =>
  ⟨const ℕ p, fun _ => hp, tendsto_const_nhds⟩
#align subset_seq_closure subset_seqClosure
-/

#print seqClosure_subset_closure /-
/-- The sequential closure of a set is contained in the closure of that set.
The converse is not true. -/
theorem seqClosure_subset_closure {s : Set X} : seqClosure s ⊆ closure s := fun p ⟨x, xM, xp⟩ =>
  mem_closure_of_tendsto xp (univ_mem' xM)
#align seq_closure_subset_closure seqClosure_subset_closure
-/

#print IsSeqClosed /-
/-- A set `s` is sequentially closed if for any converging sequence `x n` of elements of `s`, the
limit belongs to `s` as well. Note that the sequential closure of a set is not guaranteed to be
sequentially closed. -/
def IsSeqClosed (s : Set X) : Prop :=
  ∀ ⦃x : ℕ → X⦄ ⦃p : X⦄, (∀ n, x n ∈ s) → Tendsto x atTop (𝓝 p) → p ∈ s
#align is_seq_closed IsSeqClosed
-/

#print IsSeqClosed.seqClosure_eq /-
/-- The sequential closure of a sequentially closed set is the set itself. -/
theorem IsSeqClosed.seqClosure_eq {s : Set X} (hs : IsSeqClosed s) : seqClosure s = s :=
  Subset.antisymm (fun p ⟨x, hx, hp⟩ => hs hx hp) subset_seqClosure
#align is_seq_closed.seq_closure_eq IsSeqClosed.seqClosure_eq
-/

#print isSeqClosed_of_seqClosure_eq /-
/-- If a set is equal to its sequential closure, then it is sequentially closed. -/
theorem isSeqClosed_of_seqClosure_eq {s : Set X} (hs : seqClosure s = s) : IsSeqClosed s :=
  fun x p hxs hxp => hs ▸ ⟨x, hxs, hxp⟩
#align is_seq_closed_of_seq_closure_eq isSeqClosed_of_seqClosure_eq
-/

#print isSeqClosed_iff /-
/-- A set is sequentially closed iff it is equal to its sequential closure. -/
theorem isSeqClosed_iff {s : Set X} : IsSeqClosed s ↔ seqClosure s = s :=
  ⟨IsSeqClosed.seqClosure_eq, isSeqClosed_of_seqClosure_eq⟩
#align is_seq_closed_iff isSeqClosed_iff
-/

#print IsClosed.isSeqClosed /-
/-- A set is sequentially closed if it is closed. -/
protected theorem IsClosed.isSeqClosed {s : Set X} (hc : IsClosed s) : IsSeqClosed s :=
  fun u x hu hx => hc.mem_of_tendsto hx (eventually_of_forall hu)
#align is_closed.is_seq_closed IsClosed.isSeqClosed
-/

#print FrechetUrysohnSpace /-
/-- A topological space is called a *Fréchet-Urysohn space*, if the sequential closure of any set
is equal to its closure. Since one of the inclusions is trivial, we require only the non-trivial one
in the definition. -/
class FrechetUrysohnSpace (X : Type _) [TopologicalSpace X] : Prop where
  closure_subset_seqClosure : ∀ s : Set X, closure s ⊆ seqClosure s
#align frechet_urysohn_space FrechetUrysohnSpace
-/

#print seqClosure_eq_closure /-
theorem seqClosure_eq_closure [FrechetUrysohnSpace X] (s : Set X) : seqClosure s = closure s :=
  seqClosure_subset_closure.antisymm <| FrechetUrysohnSpace.closure_subset_seqClosure s
#align seq_closure_eq_closure seqClosure_eq_closure
-/

#print mem_closure_iff_seq_limit /-
/-- In a Fréchet-Urysohn space, a point belongs to the closure of a set iff it is a limit
of a sequence taking values in this set. -/
theorem mem_closure_iff_seq_limit [FrechetUrysohnSpace X] {s : Set X} {a : X} :
    a ∈ closure s ↔ ∃ x : ℕ → X, (∀ n : ℕ, x n ∈ s) ∧ Tendsto x atTop (𝓝 a) :=
  by
  rw [← seqClosure_eq_closure]
  rfl
#align mem_closure_iff_seq_limit mem_closure_iff_seq_limit
-/

/- warning: tendsto_nhds_iff_seq_tendsto -> tendsto_nhds_iff_seq_tendsto is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {Y : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.{u2} Y] [_inst_3 : FrechetUrysohnSpace.{u1} X _inst_1] {f : X -> Y} {a : X} {b : Y}, Iff (Filter.Tendsto.{u1, u2} X Y f (nhds.{u1} X _inst_1 a) (nhds.{u2} Y _inst_2 b)) (forall (u : Nat -> X), (Filter.Tendsto.{0, u1} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X _inst_1 a)) -> (Filter.Tendsto.{0, u2} Nat Y (Function.comp.{1, succ u1, succ u2} Nat X Y f u) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u2} Y _inst_2 b)))
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] [_inst_3 : FrechetUrysohnSpace.{u2} X _inst_1] {f : X -> Y} {a : X} {b : Y}, Iff (Filter.Tendsto.{u2, u1} X Y f (nhds.{u2} X _inst_1 a) (nhds.{u1} Y _inst_2 b)) (forall (u : Nat -> X), (Filter.Tendsto.{0, u2} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u2} X _inst_1 a)) -> (Filter.Tendsto.{0, u1} Nat Y (Function.comp.{1, succ u2, succ u1} Nat X Y f u) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} Y _inst_2 b)))
Case conversion may be inaccurate. Consider using '#align tendsto_nhds_iff_seq_tendsto tendsto_nhds_iff_seq_tendstoₓ'. -/
/-- If the domain of a function `f : α → β` is a Fréchet-Urysohn space, then convergence
is equivalent to sequential convergence. See also `filter.tendsto_iff_seq_tendsto` for a version
that works for any pair of filters assuming that the filter in the domain is countably generated.

This property is equivalent to the definition of `frechet_urysohn_space`, see
`frechet_urysohn_space.of_seq_tendsto_imp_tendsto`. -/
theorem tendsto_nhds_iff_seq_tendsto [FrechetUrysohnSpace X] {f : X → Y} {a : X} {b : Y} :
    Tendsto f (𝓝 a) (𝓝 b) ↔ ∀ u : ℕ → X, Tendsto u atTop (𝓝 a) → Tendsto (f ∘ u) atTop (𝓝 b) :=
  by
  refine'
    ⟨fun hf u hu => hf.comp hu, fun h =>
      ((nhds_basis_closeds _).tendsto_iffₓ (nhds_basis_closeds _)).2 _⟩
  rintro s ⟨hbs, hsc⟩
  refine' ⟨closure (f ⁻¹' s), ⟨mt _ hbs, isClosed_closure⟩, fun x => mt fun hx => subset_closure hx⟩
  rw [← seqClosure_eq_closure]
  rintro ⟨u, hus, hu⟩
  exact hsc.mem_of_tendsto (h u hu) (eventually_of_forall hus)
#align tendsto_nhds_iff_seq_tendsto tendsto_nhds_iff_seq_tendsto

#print FrechetUrysohnSpace.of_seq_tendsto_imp_tendsto /-
/-- An alternative construction for `frechet_urysohn_space`: if sequential convergence implies
convergence, then the space is a Fréchet-Urysohn space. -/
theorem FrechetUrysohnSpace.of_seq_tendsto_imp_tendsto
    (h :
      ∀ (f : X → Prop) (a : X),
        (∀ u : ℕ → X, Tendsto u atTop (𝓝 a) → Tendsto (f ∘ u) atTop (𝓝 (f a))) → ContinuousAt f a) :
    FrechetUrysohnSpace X := by
  refine' ⟨fun s x hcx => _⟩
  specialize h (· ∉ s) x
  by_cases hx : x ∈ s; · exact subset_seqClosure hx
  simp_rw [(· ∘ ·), ContinuousAt, hx, not_false_iff, nhds_true, tendsto_pure, eq_true_iff, ←
    mem_compl_iff, eventually_mem_set, ← mem_interior_iff_mem_nhds, interior_compl] at h
  rw [mem_compl_iff, imp_not_comm] at h
  simp only [not_forall, not_eventually, mem_compl_iff, Classical.not_not] at h
  rcases h hcx with ⟨u, hux, hus⟩
  rcases extraction_of_frequently_at_top hus with ⟨φ, φ_mono, hφ⟩
  exact ⟨u ∘ φ, hφ, hux.comp φ_mono.tendsto_at_top⟩
#align frechet_urysohn_space.of_seq_tendsto_imp_tendsto FrechetUrysohnSpace.of_seq_tendsto_imp_tendsto
-/

#print TopologicalSpace.FirstCountableTopology.frechetUrysohnSpace /-
-- see Note [lower instance priority]
/-- Every first-countable space is a Fréchet-Urysohn space. -/
instance (priority := 100) TopologicalSpace.FirstCountableTopology.frechetUrysohnSpace
    [FirstCountableTopology X] : FrechetUrysohnSpace X :=
  FrechetUrysohnSpace.of_seq_tendsto_imp_tendsto fun f a => tendsto_iff_seq_tendsto.2
#align topological_space.first_countable_topology.frechet_urysohn_space TopologicalSpace.FirstCountableTopology.frechetUrysohnSpace
-/

#print SequentialSpace /-
/-- A topological space is said to be a *sequential space* if any sequentially closed set in this
space is closed. This condition is weaker than being a Fréchet-Urysohn space. -/
class SequentialSpace (X : Type _) [TopologicalSpace X] : Prop where
  isClosed_of_seq : ∀ s : Set X, IsSeqClosed s → IsClosed s
#align sequential_space SequentialSpace
-/

#print FrechetUrysohnSpace.to_sequentialSpace /-
-- see Note [lower instance priority]
/-- Every Fréchet-Urysohn space is a sequential space. -/
instance (priority := 100) FrechetUrysohnSpace.to_sequentialSpace [FrechetUrysohnSpace X] :
    SequentialSpace X :=
  ⟨fun s hs => by rw [← closure_eq_iff_isClosed, ← seqClosure_eq_closure, hs.seq_closure_eq]⟩
#align frechet_urysohn_space.to_sequential_space FrechetUrysohnSpace.to_sequentialSpace
-/

#print IsSeqClosed.isClosed /-
/-- In a sequential space, a sequentially closed set is closed. -/
protected theorem IsSeqClosed.isClosed [SequentialSpace X] {s : Set X} (hs : IsSeqClosed s) :
    IsClosed s :=
  SequentialSpace.isClosed_of_seq s hs
#align is_seq_closed.is_closed IsSeqClosed.isClosed
-/

#print isSeqClosed_iff_isClosed /-
/-- In a sequential space, a set is closed iff it's sequentially closed. -/
theorem isSeqClosed_iff_isClosed [SequentialSpace X] {M : Set X} : IsSeqClosed M ↔ IsClosed M :=
  ⟨IsSeqClosed.isClosed, IsClosed.isSeqClosed⟩
#align is_seq_closed_iff_is_closed isSeqClosed_iff_isClosed
-/

#print SeqContinuous /-
/-- A function between topological spaces is sequentially continuous if it commutes with limit of
 convergent sequences. -/
def SeqContinuous (f : X → Y) : Prop :=
  ∀ ⦃x : ℕ → X⦄ ⦃p : X⦄, Tendsto x atTop (𝓝 p) → Tendsto (f ∘ x) atTop (𝓝 (f p))
#align seq_continuous SeqContinuous
-/

#print IsSeqClosed.preimage /-
/-- The preimage of a sequentially closed set under a sequentially continuous map is sequentially
closed. -/
theorem IsSeqClosed.preimage {f : X → Y} {s : Set Y} (hs : IsSeqClosed s) (hf : SeqContinuous f) :
    IsSeqClosed (f ⁻¹' s) := fun x p hx hp => hs hx (hf hp)
#align is_seq_closed.preimage IsSeqClosed.preimage
-/

/- warning: continuous.seq_continuous -> Continuous.seqContinuous is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {Y : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.{u2} Y] {f : X -> Y}, (Continuous.{u1, u2} X Y _inst_1 _inst_2 f) -> (SeqContinuous.{u1, u2} X Y _inst_1 _inst_2 f)
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : X -> Y}, (Continuous.{u2, u1} X Y _inst_1 _inst_2 f) -> (SeqContinuous.{u2, u1} X Y _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align continuous.seq_continuous Continuous.seqContinuousₓ'. -/
-- A continuous function is sequentially continuous.
protected theorem Continuous.seqContinuous {f : X → Y} (hf : Continuous f) : SeqContinuous f :=
  fun x p hx => (hf.Tendsto p).comp hx
#align continuous.seq_continuous Continuous.seqContinuous

/- warning: seq_continuous.continuous -> SeqContinuous.continuous is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {Y : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.{u2} Y] [_inst_3 : SequentialSpace.{u1} X _inst_1] {f : X -> Y}, (SeqContinuous.{u1, u2} X Y _inst_1 _inst_2 f) -> (Continuous.{u1, u2} X Y _inst_1 _inst_2 f)
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] [_inst_3 : SequentialSpace.{u2} X _inst_1] {f : X -> Y}, (SeqContinuous.{u2, u1} X Y _inst_1 _inst_2 f) -> (Continuous.{u2, u1} X Y _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align seq_continuous.continuous SeqContinuous.continuousₓ'. -/
/-- A sequentially continuous function defined on a sequential space is continuous. -/
protected theorem SeqContinuous.continuous [SequentialSpace X] {f : X → Y} (hf : SeqContinuous f) :
    Continuous f :=
  continuous_iff_isClosed.mpr fun s hs => (hs.IsSeqClosed.Preimage hf).IsClosed
#align seq_continuous.continuous SeqContinuous.continuous

/- warning: continuous_iff_seq_continuous -> continuous_iff_seqContinuous is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {Y : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.{u2} Y] [_inst_3 : SequentialSpace.{u1} X _inst_1] {f : X -> Y}, Iff (Continuous.{u1, u2} X Y _inst_1 _inst_2 f) (SeqContinuous.{u1, u2} X Y _inst_1 _inst_2 f)
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] [_inst_3 : SequentialSpace.{u2} X _inst_1] {f : X -> Y}, Iff (Continuous.{u2, u1} X Y _inst_1 _inst_2 f) (SeqContinuous.{u2, u1} X Y _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align continuous_iff_seq_continuous continuous_iff_seqContinuousₓ'. -/
/-- If the domain of a function is a sequential space, then continuity of this function is
equivalent to its sequential continuity. -/
theorem continuous_iff_seqContinuous [SequentialSpace X] {f : X → Y} :
    Continuous f ↔ SeqContinuous f :=
  ⟨Continuous.seqContinuous, SeqContinuous.continuous⟩
#align continuous_iff_seq_continuous continuous_iff_seqContinuous

/- warning: quotient_map.sequential_space -> QuotientMap.sequentialSpace is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {Y : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.{u2} Y] [_inst_3 : SequentialSpace.{u1} X _inst_1] {f : X -> Y}, (QuotientMap.{u1, u2} X Y _inst_1 _inst_2 f) -> (SequentialSpace.{u2} Y _inst_2)
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] [_inst_3 : SequentialSpace.{u2} X _inst_1] {f : X -> Y}, (QuotientMap.{u2, u1} X Y _inst_1 _inst_2 f) -> (SequentialSpace.{u1} Y _inst_2)
Case conversion may be inaccurate. Consider using '#align quotient_map.sequential_space QuotientMap.sequentialSpaceₓ'. -/
theorem QuotientMap.sequentialSpace [SequentialSpace X] {f : X → Y} (hf : QuotientMap f) :
    SequentialSpace Y :=
  ⟨fun s hs => hf.isClosed_preimage.mp <| (hs.Preimage <| hf.Continuous.SeqContinuous).IsClosed⟩
#align quotient_map.sequential_space QuotientMap.sequentialSpace

/-- The quotient of a sequential space is a sequential space. -/
instance [SequentialSpace X] {s : Setoid X} : SequentialSpace (Quotient s) :=
  quotientMap_quot_mk.SequentialSpace

end TopologicalSpace

section SeqCompact

open TopologicalSpace TopologicalSpace.FirstCountableTopology

variable [TopologicalSpace X]

#print IsSeqCompact /-
/-- A set `s` is sequentially compact if every sequence taking values in `s` has a
converging subsequence. -/
def IsSeqCompact (s : Set X) :=
  ∀ ⦃x : ℕ → X⦄, (∀ n, x n ∈ s) → ∃ a ∈ s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a)
#align is_seq_compact IsSeqCompact
-/

#print SeqCompactSpace /-
/-- A space `X` is sequentially compact if every sequence in `X` has a
converging subsequence. -/
@[mk_iff]
class SeqCompactSpace (X : Type _) [TopologicalSpace X] : Prop where
  seq_compact_univ : IsSeqCompact (univ : Set X)
#align seq_compact_space SeqCompactSpace
-/

export SeqCompactSpace (seq_compact_univ)

/- warning: is_seq_compact.subseq_of_frequently_in -> IsSeqCompact.subseq_of_frequently_in is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) -> (Exists.{succ u1} X (fun (a : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) => Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X _inst_1 a)))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) -> (Exists.{succ u1} X (fun (a : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) (Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X _inst_1 a)))))))
Case conversion may be inaccurate. Consider using '#align is_seq_compact.subseq_of_frequently_in IsSeqCompact.subseq_of_frequently_inₓ'. -/
theorem IsSeqCompact.subseq_of_frequently_in {s : Set X} (hs : IsSeqCompact s) {x : ℕ → X}
    (hx : ∃ᶠ n in atTop, x n ∈ s) :
    ∃ a ∈ s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  let ⟨ψ, hψ, huψ⟩ := extraction_of_frequently_atTop hx
  let ⟨a, a_in, φ, hφ, h⟩ := hs huψ
  ⟨a, a_in, ψ ∘ φ, hψ.comp hφ, h⟩
#align is_seq_compact.subseq_of_frequently_in IsSeqCompact.subseq_of_frequently_in

#print SeqCompactSpace.tendsto_subseq /-
theorem SeqCompactSpace.tendsto_subseq [SeqCompactSpace X] (x : ℕ → X) :
    ∃ (a : _)(φ : ℕ → ℕ), StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  let ⟨a, _, φ, mono, h⟩ := seq_compact_univ fun n => mem_univ (x n)
  ⟨a, φ, mono, h⟩
#align seq_compact_space.tendsto_subseq SeqCompactSpace.tendsto_subseq
-/

section FirstCountableTopology

variable [FirstCountableTopology X]

open TopologicalSpace.FirstCountableTopology

#print IsCompact.isSeqCompact /-
protected theorem IsCompact.isSeqCompact {s : Set X} (hs : IsCompact s) : IsSeqCompact s :=
  fun x x_in =>
  let ⟨a, a_in, ha⟩ := hs (tendsto_principal.mpr (eventually_of_forall x_in))
  ⟨a, a_in, tendsto_subseq ha⟩
#align is_compact.is_seq_compact IsCompact.isSeqCompact
-/

/- warning: is_compact.tendsto_subseq' -> IsCompact.tendsto_subseq' is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.FirstCountableTopology.{u1} X _inst_1] {s : Set.{u1} X} {x : Nat -> X}, (IsCompact.{u1} X _inst_1 s) -> (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) -> (Exists.{succ u1} X (fun (a : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) => Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X _inst_1 a))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.FirstCountableTopology.{u1} X _inst_1] {s : Set.{u1} X} {x : Nat -> X}, (IsCompact.{u1} X _inst_1 s) -> (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) -> (Exists.{succ u1} X (fun (a : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) (Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X _inst_1 a))))))
Case conversion may be inaccurate. Consider using '#align is_compact.tendsto_subseq' IsCompact.tendsto_subseq'ₓ'. -/
theorem IsCompact.tendsto_subseq' {s : Set X} {x : ℕ → X} (hs : IsCompact s)
    (hx : ∃ᶠ n in atTop, x n ∈ s) :
    ∃ a ∈ s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  hs.IsSeqCompact.subseq_of_frequently_in hx
#align is_compact.tendsto_subseq' IsCompact.tendsto_subseq'

/- warning: is_compact.tendsto_subseq -> IsCompact.tendsto_subseq is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.FirstCountableTopology.{u1} X _inst_1] {s : Set.{u1} X} {x : Nat -> X}, (IsCompact.{u1} X _inst_1 s) -> (forall (n : Nat), Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (x n) s) -> (Exists.{succ u1} X (fun (a : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) => Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X _inst_1 a))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : TopologicalSpace.FirstCountableTopology.{u1} X _inst_1] {s : Set.{u1} X} {x : Nat -> X}, (IsCompact.{u1} X _inst_1 s) -> (forall (n : Nat), Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (x n) s) -> (Exists.{succ u1} X (fun (a : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) (Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X _inst_1 a))))))
Case conversion may be inaccurate. Consider using '#align is_compact.tendsto_subseq IsCompact.tendsto_subseqₓ'. -/
theorem IsCompact.tendsto_subseq {s : Set X} {x : ℕ → X} (hs : IsCompact s) (hx : ∀ n, x n ∈ s) :
    ∃ a ∈ s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  hs.IsSeqCompact hx
#align is_compact.tendsto_subseq IsCompact.tendsto_subseq

#print FirstCountableTopology.seq_compact_of_compact /-
-- see Note [lower instance priority]
instance (priority := 100) FirstCountableTopology.seq_compact_of_compact [CompactSpace X] :
    SeqCompactSpace X :=
  ⟨isCompact_univ.IsSeqCompact⟩
#align first_countable_topology.seq_compact_of_compact FirstCountableTopology.seq_compact_of_compact
-/

#print CompactSpace.tendsto_subseq /-
theorem CompactSpace.tendsto_subseq [CompactSpace X] (x : ℕ → X) :
    ∃ (a : _)(φ : ℕ → ℕ), StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  SeqCompactSpace.tendsto_subseq x
#align compact_space.tendsto_subseq CompactSpace.tendsto_subseq
-/

end FirstCountableTopology

end SeqCompact

section UniformSpaceSeqCompact

open uniformity

open UniformSpace Prod

variable [UniformSpace X] {s : Set X}

/- warning: is_seq_compact.exists_tendsto_of_frequently_mem -> IsSeqCompact.exists_tendsto_of_frequently_mem is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : UniformSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) s) -> (forall {u : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (u n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) -> (CauchySeq.{u1, 0} X Nat _inst_1 (CanonicallyLinearOrderedAddMonoid.semilatticeSup.{0} Nat Nat.canonicallyLinearOrderedAddMonoid) u) -> (Exists.{succ u1} X (fun (x : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) => Filter.Tendsto.{0, u1} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) x)))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : UniformSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) s) -> (forall {u : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (u n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) -> (CauchySeq.{u1, 0} X Nat _inst_1 (Lattice.toSemilatticeSup.{0} Nat (DistribLattice.toLattice.{0} Nat instDistribLatticeNat)) u) -> (Exists.{succ u1} X (fun (x : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x s) (Filter.Tendsto.{0, u1} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) x)))))
Case conversion may be inaccurate. Consider using '#align is_seq_compact.exists_tendsto_of_frequently_mem IsSeqCompact.exists_tendsto_of_frequently_memₓ'. -/
theorem IsSeqCompact.exists_tendsto_of_frequently_mem (hs : IsSeqCompact s) {u : ℕ → X}
    (hu : ∃ᶠ n in atTop, u n ∈ s) (huc : CauchySeq u) : ∃ x ∈ s, Tendsto u atTop (𝓝 x) :=
  let ⟨x, hxs, φ, φ_mono, hx⟩ := hs.subseq_of_frequently_in hu
  ⟨x, hxs, tendsto_nhds_of_cauchySeq_of_subseq huc φ_mono.tendsto_atTop hx⟩
#align is_seq_compact.exists_tendsto_of_frequently_mem IsSeqCompact.exists_tendsto_of_frequently_mem

/- warning: is_seq_compact.exists_tendsto -> IsSeqCompact.exists_tendsto is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : UniformSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) s) -> (forall {u : Nat -> X}, (forall (n : Nat), Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (u n) s) -> (CauchySeq.{u1, 0} X Nat _inst_1 (CanonicallyLinearOrderedAddMonoid.semilatticeSup.{0} Nat Nat.canonicallyLinearOrderedAddMonoid) u) -> (Exists.{succ u1} X (fun (x : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) => Filter.Tendsto.{0, u1} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) x)))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : UniformSpace.{u1} X] {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) s) -> (forall {u : Nat -> X}, (forall (n : Nat), Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (u n) s) -> (CauchySeq.{u1, 0} X Nat _inst_1 (Lattice.toSemilatticeSup.{0} Nat (DistribLattice.toLattice.{0} Nat instDistribLatticeNat)) u) -> (Exists.{succ u1} X (fun (x : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x s) (Filter.Tendsto.{0, u1} Nat X u (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X _inst_1) x)))))
Case conversion may be inaccurate. Consider using '#align is_seq_compact.exists_tendsto IsSeqCompact.exists_tendstoₓ'. -/
theorem IsSeqCompact.exists_tendsto (hs : IsSeqCompact s) {u : ℕ → X} (hu : ∀ n, u n ∈ s)
    (huc : CauchySeq u) : ∃ x ∈ s, Tendsto u atTop (𝓝 x) :=
  hs.exists_tendsto_of_frequently_mem (frequently_of_forall hu) huc
#align is_seq_compact.exists_tendsto IsSeqCompact.exists_tendsto

#print IsSeqCompact.totallyBounded /-
/-- A sequentially compact set in a uniform space is totally bounded. -/
protected theorem IsSeqCompact.totallyBounded (h : IsSeqCompact s) : TotallyBounded s :=
  by
  intro V V_in
  unfold IsSeqCompact at h
  contrapose! h
  obtain ⟨u, u_in, hu⟩ : ∃ u : ℕ → X, (∀ n, u n ∈ s) ∧ ∀ n m, m < n → u m ∉ ball (u n) V :=
    by
    simp only [not_subset, mem_Union₂, not_exists, exists_prop] at h
    simpa only [forall_and, ball_image_iff, not_and] using seq_of_forall_finite_exists h
  refine' ⟨u, u_in, fun x x_in φ hφ huφ => _⟩
  obtain ⟨N, hN⟩ : ∃ N, ∀ p q, p ≥ N → q ≥ N → (u (φ p), u (φ q)) ∈ V
  exact huφ.cauchy_seq.mem_entourage V_in
  exact hu (φ <| N + 1) (φ N) (hφ <| lt_add_one N) (hN (N + 1) N N.le_succ le_rfl)
#align is_seq_compact.totally_bounded IsSeqCompact.totallyBounded
-/

variable [IsCountablyGenerated (𝓤 X)]

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print IsSeqCompact.isComplete /-
/-- A sequentially compact set in a uniform set with countably generated uniformity filter
is complete. -/
protected theorem IsSeqCompact.isComplete (hs : IsSeqCompact s) : IsComplete s :=
  by
  intro l hl hls
  haveI := hl.1
  rcases exists_antitone_basis (𝓤 X) with ⟨V, hV⟩
  choose W hW hWV using fun n => comp_mem_uniformity_sets (hV.mem n)
  have hWV' : ∀ n, W n ⊆ V n := fun n ⟨x, y⟩ hx =>
    @hWV n (x, y) ⟨x, refl_mem_uniformity <| hW _, hx⟩
  obtain ⟨t, ht_anti, htl, htW, hts⟩ :
    ∃ t : ℕ → Set X, Antitone t ∧ (∀ n, t n ∈ l) ∧ (∀ n, t n ×ˢ t n ⊆ W n) ∧ ∀ n, t n ⊆ s :=
    by
    have : ∀ n, ∃ t ∈ l, t ×ˢ t ⊆ W n ∧ t ⊆ s :=
      by
      rw [le_principal_iff] at hls
      have : ∀ n, W n ∩ s ×ˢ s ∈ l ×ᶠ l := fun n => inter_mem (hl.2 (hW n)) (prod_mem_prod hls hls)
      simpa only [l.basis_sets.prod_self.mem_iff, true_imp_iff, subset_inter_iff,
        prod_self_subset_prod_self, and_assoc] using this
    choose t htl htW hts
    have : ∀ n, (⋂ k ≤ n, t k) ⊆ t n := fun n => Inter₂_subset _ le_rfl
    exact
      ⟨fun n => ⋂ k ≤ n, t k, fun m n h =>
        bInter_subset_bInter_left fun k (hk : k ≤ m) => hk.trans h, fun n =>
        (bInter_mem (finite_le_nat n)).2 fun k hk => htl k, fun n =>
        (prod_mono (this n) (this n)).trans (htW n), fun n => (this n).trans (hts n)⟩
  choose u hu using fun n => Filter.nonempty_of_mem (htl n)
  have huc : CauchySeq u :=
    hV.to_has_basis.cauchy_seq_iff.2 fun N hN =>
      ⟨N, fun m hm n hn => hWV' _ <| @htW N (_, _) ⟨ht_anti hm (hu _), ht_anti hn (hu _)⟩⟩
  rcases hs.exists_tendsto (fun n => hts n (hu n)) huc with ⟨x, hxs, hx⟩
  refine' ⟨x, hxs, (nhds_basis_uniformity' hV.to_has_basis).ge_iff.2 fun N hN => _⟩
  obtain ⟨n, hNn, hn⟩ : ∃ n, N ≤ n ∧ u n ∈ ball x (W N)
  exact ((eventually_ge_at_top N).And (hx <| ball_mem_nhds x (hW N))).exists
  refine' mem_of_superset (htl n) fun y hy => hWV N ⟨u n, _, htW N ⟨_, _⟩⟩
  exacts[hn, ht_anti hNn (hu n), ht_anti hNn hy]
#align is_seq_compact.is_complete IsSeqCompact.isComplete
-/

#print IsSeqCompact.isCompact /-
/-- If `𝓤 β` is countably generated, then any sequentially compact set is compact. -/
protected theorem IsSeqCompact.isCompact (hs : IsSeqCompact s) : IsCompact s :=
  isCompact_iff_totallyBounded_isComplete.2 ⟨hs.TotallyBounded, hs.IsComplete⟩
#align is_seq_compact.is_compact IsSeqCompact.isCompact
-/

#print UniformSpace.isCompact_iff_isSeqCompact /-
/-- A version of Bolzano-Weistrass: in a uniform space with countably generated uniformity filter
(e.g., in a metric space), a set is compact if and only if it is sequentially compact. -/
protected theorem UniformSpace.isCompact_iff_isSeqCompact : IsCompact s ↔ IsSeqCompact s :=
  ⟨fun H => H.IsSeqCompact, fun H => H.IsCompact⟩
#align uniform_space.is_compact_iff_is_seq_compact UniformSpace.isCompact_iff_isSeqCompact
-/

#print UniformSpace.compactSpace_iff_seqCompactSpace /-
theorem UniformSpace.compactSpace_iff_seqCompactSpace : CompactSpace X ↔ SeqCompactSpace X := by
  simp only [← isCompact_univ_iff, seqCompactSpace_iff, UniformSpace.isCompact_iff_isSeqCompact]
#align uniform_space.compact_space_iff_seq_compact_space UniformSpace.compactSpace_iff_seqCompactSpace
-/

end UniformSpaceSeqCompact

section MetricSeqCompact

variable [PseudoMetricSpace X]

open Metric

/- warning: seq_compact.lebesgue_number_lemma_of_metric -> SeqCompact.lebesgue_number_lemma_of_metric is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] {ι : Sort.{u2}} {c : ι -> (Set.{u1} X)} {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s) -> (forall (i : ι), IsOpen.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) (c i)) -> (HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) s (Set.unionᵢ.{u1, u2} X ι (fun (i : ι) => c i))) -> (Exists.{1} Real (fun (δ : Real) => Exists.{0} (GT.gt.{0} Real Real.hasLt δ (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) (fun (H : GT.gt.{0} Real Real.hasLt δ (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) => forall (a : X), (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) -> (Exists.{u2} ι (fun (i : ι) => HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (Metric.ball.{u1} X _inst_1 a δ) (c i))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] {ι : Sort.{u2}} {c : ι -> (Set.{u1} X)} {s : Set.{u1} X}, (IsSeqCompact.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s) -> (forall (i : ι), IsOpen.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) (c i)) -> (HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) s (Set.unionᵢ.{u1, u2} X ι (fun (i : ι) => c i))) -> (Exists.{1} Real (fun (δ : Real) => And (GT.gt.{0} Real Real.instLTReal δ (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) (forall (a : X), (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) -> (Exists.{u2} ι (fun (i : ι) => HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) (Metric.ball.{u1} X _inst_1 a δ) (c i))))))
Case conversion may be inaccurate. Consider using '#align seq_compact.lebesgue_number_lemma_of_metric SeqCompact.lebesgue_number_lemma_of_metricₓ'. -/
theorem SeqCompact.lebesgue_number_lemma_of_metric {ι : Sort _} {c : ι → Set X} {s : Set X}
    (hs : IsSeqCompact s) (hc₁ : ∀ i, IsOpen (c i)) (hc₂ : s ⊆ ⋃ i, c i) :
    ∃ δ > 0, ∀ a ∈ s, ∃ i, ball a δ ⊆ c i :=
  lebesgue_number_lemma_of_metric hs.IsCompact hc₁ hc₂
#align seq_compact.lebesgue_number_lemma_of_metric SeqCompact.lebesgue_number_lemma_of_metric

variable [ProperSpace X] {s : Set X}

/- warning: tendsto_subseq_of_frequently_bounded -> tendsto_subseq_of_frequently_bounded is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] [_inst_2 : ProperSpace.{u1} X _inst_1] {s : Set.{u1} X}, (Metric.Bounded.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) -> (Exists.{succ u1} X (fun (a : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) => Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) a)))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] [_inst_2 : ProperSpace.{u1} X _inst_1] {s : Set.{u1} X}, (Metric.Bounded.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (Filter.Frequently.{0} Nat (fun (n : Nat) => Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (x n) s) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) -> (Exists.{succ u1} X (fun (a : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) (Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) a)))))))
Case conversion may be inaccurate. Consider using '#align tendsto_subseq_of_frequently_bounded tendsto_subseq_of_frequently_boundedₓ'. -/
/-- A version of **Bolzano-Weistrass**: in a proper metric space (eg. $ℝ^n$),
every bounded sequence has a converging subsequence. This version assumes only
that the sequence is frequently in some bounded set. -/
theorem tendsto_subseq_of_frequently_bounded (hs : Bounded s) {x : ℕ → X}
    (hx : ∃ᶠ n in atTop, x n ∈ s) :
    ∃ a ∈ closure s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  have hcs : IsSeqCompact (closure s) := hs.isCompact_closure.IsSeqCompact
  have hu' : ∃ᶠ n in atTop, x n ∈ closure s := hx.mono fun n hn => subset_closure hn
  hcs.subseq_of_frequently_in hu'
#align tendsto_subseq_of_frequently_bounded tendsto_subseq_of_frequently_bounded

/- warning: tendsto_subseq_of_bounded -> tendsto_subseq_of_bounded is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] [_inst_2 : ProperSpace.{u1} X _inst_1] {s : Set.{u1} X}, (Metric.Bounded.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (forall (n : Nat), Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) (x n) s) -> (Exists.{succ u1} X (fun (a : X) => Exists.{0} (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) (fun (H : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) => Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) a)))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} X] [_inst_2 : ProperSpace.{u1} X _inst_1] {s : Set.{u1} X}, (Metric.Bounded.{u1} X _inst_1 s) -> (forall {x : Nat -> X}, (forall (n : Nat), Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) (x n) s) -> (Exists.{succ u1} X (fun (a : X) => And (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a (closure.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) s)) (Exists.{1} (Nat -> Nat) (fun (φ : Nat -> Nat) => And (StrictMono.{0, 0} Nat Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) φ) (Filter.Tendsto.{0, u1} Nat X (Function.comp.{1, 1, succ u1} Nat Nat X x φ) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} X (UniformSpace.toTopologicalSpace.{u1} X (PseudoMetricSpace.toUniformSpace.{u1} X _inst_1)) a)))))))
Case conversion may be inaccurate. Consider using '#align tendsto_subseq_of_bounded tendsto_subseq_of_boundedₓ'. -/
/-- A version of Bolzano-Weistrass: in a proper metric space (eg. $ℝ^n$),
every bounded sequence has a converging subsequence. -/
theorem tendsto_subseq_of_bounded (hs : Bounded s) {x : ℕ → X} (hx : ∀ n, x n ∈ s) :
    ∃ a ∈ closure s, ∃ φ : ℕ → ℕ, StrictMono φ ∧ Tendsto (x ∘ φ) atTop (𝓝 a) :=
  tendsto_subseq_of_frequently_bounded hs <| frequently_of_forall hx
#align tendsto_subseq_of_bounded tendsto_subseq_of_bounded

end MetricSeqCompact


/- ------------------------------------------------------------------------- -|
| @project: riemann_hypothesis                                                |
| @file:    complex.lean                                                      |
| @authors: Brandon H. Gomes, Alex Kontorovich                                |
| @affil:   Rutgers University                                                |
|- ------------------------------------------------------------------------- -/

import .exponential

/-!
-/

namespace riemann_hypothesis --——————————————————————————————————————————————————————————--

/--
-/
class Algebra (α : Type*)
    extends has_zero α, has_one α, has_neg α, has_add α, has_sub α, has_mul α, has_inv α

--———————————————————————————————————————————————————————————————————————————————————————--
variables (ℂ : Type*) (ℝ : Type*)

variables [preorder ℝ] [Algebra ℝ]

variables [Algebra ℂ]

variables [has_lift_t ℝ ℂ]

/--
-/
structure Complex
    := (real_part                : ℂ → ℝ)
       (abs                      : ℂ → ℝ)
       (exp                      : ℂ → ℂ)
       (real_explog              : ExpLog ℝ ℝ)
       (abs_nonneg               : Π z, 0 ≤ abs z)
       (exp_nonzero              : Π z, exp z ≠ 0)
       (exp_homomorphism_zero    : exp 0 = 1)
       (exp_homomorphism         : Π w z, exp (w + z) = exp w * exp z)
       (exp_homomorphism_inv     : Π w z, exp (w - z) = exp w * (exp z)⁻¹)
       (real_part_scaling        : Π x z, real_part (↑x * z) = x * real_part z)
       (abs_exp_is_exp_real_part : Π z, abs (exp z) = real_explog.exp (real_part z))
       (exp_linearization        : Π x, abs x ≤ 1
                                 → Π z, abs (exp (x * z) - (x * z + 1))
                                      ≤ (abs x * abs x) * real_explog.exp (abs z))
       (real_log_bound           : Π x (p : 0 < 1 - x), abs ↑x ≤ 2⁻¹
                                      → abs ↑(real_explog.log (1 - x) p)
                                      ≤ abs ↑x + abs ↑x)

namespace Complex --—————————————————————————————————————————————————————————————————————--
variables {ℂ ℝ} (ℭ : Complex ℂ ℝ)

open algebra

/--
-/
def real_abs (x : ℝ)
    := ℭ.abs (↑x)

/--
-/
def real_exp (x)
    := ℭ.real_explog.exp x

/--
-/
def real_log (x)
    := ℭ.real_explog.log x

/--
-/
def pow (a apos x)
    := ℭ.exp (↑(ℭ.real_explog.log a apos) * x)

/--
-/
def pow_domain_irrel
    (x xpos)
    (y ypos)
    (z)
    : x = y → ℭ.pow x xpos z = ℭ.pow y ypos z :=
    begin
        intros x_eq_y,
        rw [pow, pow],
        rw ExpLog.log_domain_irrel _ _ _ _ _ x_eq_y,
    end

/--
-/
def pow_nonzero (a apos x)
    : ℭ.pow a apos x ≠ 0
    := ℭ.exp_nonzero _

/--
-/
def pow_neg_exponent_inverts
    [has_left_sub_distributivity ℂ]
    [has_mul_zero_is_zero ℂ]
    [has_left_unit ℂ]
    (a apos x)
    : ℭ.pow a apos (0 - x) = (ℭ.pow a apos x)⁻¹ :=
    begin
        rw pow,
        rw has_left_sub_distributivity.eq,
        rw has_mul_zero_is_zero.eq,
        rw exp_homomorphism_inv,
        rw exp_homomorphism_zero,
        rw has_left_unit.eq,
        rw ← pow,
    end

/--
-/
def abs_pow_is_real_pow (x xpos z)
    : ℭ.abs (ℭ.pow x xpos z) = ℭ.real_explog.pow x xpos (ℭ.real_part z)
    := by rw [pow, abs_exp_is_exp_real_part, real_part_scaling, ExpLog.pow]

--———————————————————————————————————————————————————————————————————————————————————————--
local notation `|` z `|` := ℭ.abs z

/--
-/
def one_minus_pow_bound
    [has_lift_t nat ℝ]

    [has_add_le_add ℝ]

    [has_left_unit ℝ]
    [has_right_unit ℝ]

    [has_squared_le_monotonic ℝ]

    [has_inv_mul_right_cancel_self ℝ]

    [has_le_nonneg_mul_preserves_left ℝ]
    [has_le_nonneg_mul_preserves_right ℝ]

    [has_mul_assoc ℝ]

    [has_left_add_distributivity ℝ]
    [has_right_add_distributivity ℝ]

    [has_sub_add_sub_cancel ℂ]
    [has_add_sub_assoc ℂ]
    [has_sub_self_is_zero ℂ]
    [has_zero_right_add_cancel ℂ]

    [has_zero_right_add_cancel ℝ]

    (zero_lt_two : 0 < (2 : ℝ))
    (one_le_two  : 1 ≤ (2 : ℝ))

    (abs_mul      : Π a b, |a * b| = |a| * |b|)
    (abs_triangle : Π a b, |a + b| ≤ |a| + |b|)

    (z) (x : ℝ) (abs_x_le_half : |↑x| ≤ 2⁻¹) (one_minus_x_pos : 0 < 1 - x)

    : | ℭ.pow (1 - x) one_minus_x_pos z  - 1 | ≤ |↑x| * (4 * ℭ.real_exp (|z|) + (|z| + |z|)) :=

    begin
        rw ← has_sub_add_sub_cancel.eq _ (↑(ℭ.real_log (1 - x) one_minus_x_pos) * z + 1) _,

        refine le_trans (abs_triangle _ _) _,

        rw has_add_sub_assoc.eq,
        rw has_sub_self_is_zero.eq,
        rw has_zero_right_add_cancel.eq,
        rw abs_mul,
        rw has_left_add_distributivity.eq,
        rw ← has_mul_assoc.eq,

        let log_bound
            := ℭ.real_log_bound _ one_minus_x_pos abs_x_le_half,

        let abs_x_inequality
            := has_le_nonneg_mul_preserves_left.le (le_of_lt zero_lt_two) abs_x_le_half,

        rw has_inv_mul_right_cancel_self.eq _ (ne_of_lt zero_lt_two).symm
            at abs_x_inequality,

        rw two_mul_lemma at abs_x_inequality,

        refine has_add_le_add.le
            (le_trans
                (ℭ.exp_linearization _ (le_trans log_bound abs_x_inequality) _)
                (has_le_nonneg_mul_preserves_right.le
                    (le_of_lt (ExpLog.exp_positive _ _))
                    (le_trans
                        (has_squared_le_monotonic.le (abs_nonneg _ _) log_bound) _))) _,

        rw ← two_squares_is_four_lemma',

        refine has_le_nonneg_mul_preserves_right.le _ _,

        let zero_le_four
            := has_add_le_add.le (le_of_lt zero_lt_two) (le_of_lt zero_lt_two),

        rw has_zero_right_add_cancel.eq at zero_le_four,

        refine zero_le_four,

        have abs_x_le_one : |↑x| ≤ 1,
        {
            refine le_trans _ abs_x_inequality,

            rw ← two_mul_lemma',
            rw ← has_right_unit.eq (|↑x|),
            rw has_mul_assoc.eq,
            rw has_left_unit.eq,

            refine has_le_nonneg_mul_preserves_left.le (abs_nonneg _ _) one_le_two,
        },

        let square_le_id
            := has_le_nonneg_mul_preserves_right.le (abs_nonneg _ _) abs_x_le_one,

        rw has_left_unit.eq at square_le_id,

        refine square_le_id,

        rw has_left_add_distributivity.eq,
        rw ← has_right_add_distributivity.eq,

        refine has_le_nonneg_mul_preserves_right.le (abs_nonneg _ _) log_bound,
    end

end Complex --———————————————————————————————————————————————————————————————————————————--

end riemann_hypothesis --————————————————————————————————————————————————————————————————--


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

! This file was ported from Lean 3 source module analysis.calculus.local_extr
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Calculus.Deriv
import Mathbin.Data.Polynomial.FieldDivision
import Mathbin.Topology.Algebra.Order.ExtendFrom
import Mathbin.Topology.Algebra.Polynomial
import Mathbin.Topology.LocalExtr

/-!
# Local extrema of smooth functions

## Main definitions

In a real normed space `E` we define `pos_tangent_cone_at (s : set E) (x : E)`.
This would be the same as `tangent_cone_at ℝ≥0 s x` if we had a theory of normed semifields.
This set is used in the proof of Fermat's Theorem (see below), and can be used to formalize
[Lagrange multipliers](https://en.wikipedia.org/wiki/Lagrange_multiplier) and/or
[Karush–Kuhn–Tucker conditions](https://en.wikipedia.org/wiki/Karush–Kuhn–Tucker_conditions).

## Main statements

For each theorem name listed below,
we also prove similar theorems for `min`, `extr` (if applicable)`,
and `(f)deriv` instead of `has_fderiv`.

* `is_local_max_on.has_fderiv_within_at_nonpos` : `f' y ≤ 0` whenever `a` is a local maximum
  of `f` on `s`, `f` has derivative `f'` at `a` within `s`, and `y` belongs to the positive tangent
  cone of `s` at `a`.

* `is_local_max_on.has_fderiv_within_at_eq_zero` : In the settings of the previous theorem, if both
  `y` and `-y` belong to the positive tangent cone, then `f' y = 0`.

* `is_local_max.has_fderiv_at_eq_zero` :
  [Fermat's Theorem](https://en.wikipedia.org/wiki/Fermat's_theorem_(stationary_points)),
  the derivative of a differentiable function at a local extremum point equals zero.

* `exists_has_deriv_at_eq_zero` :
  [Rolle's Theorem](https://en.wikipedia.org/wiki/Rolle's_theorem): given a function `f` continuous
  on `[a, b]` and differentiable on `(a, b)`, there exists `c ∈ (a, b)` such that `f' c = 0`.

## Implementation notes

For each mathematical fact we prove several versions of its formalization:

* for maxima and minima;
* using `has_fderiv*`/`has_deriv*` or `fderiv*`/`deriv*`.

For the `fderiv*`/`deriv*` versions we omit the differentiability condition whenever it is possible
due to the fact that `fderiv` and `deriv` are defined to be zero for non-differentiable functions.

## References

* [Fermat's Theorem](https://en.wikipedia.org/wiki/Fermat's_theorem_(stationary_points));
* [Rolle's Theorem](https://en.wikipedia.org/wiki/Rolle's_theorem);
* [Tangent cone](https://en.wikipedia.org/wiki/Tangent_cone);

## Tags

local extremum, Fermat's Theorem, Rolle's Theorem
-/

universe u v

open Filter Set

open Topology Classical Polynomial

section Module

variable {E : Type u} [NormedAddCommGroup E] [NormedSpace ℝ E] {f : E → ℝ} {a : E} {f' : E →L[ℝ] ℝ}

/-- "Positive" tangent cone to `s` at `x`; the only difference from `tangent_cone_at`
is that we require `c n → ∞` instead of `‖c n‖ → ∞`. One can think about `pos_tangent_cone_at`
as `tangent_cone_at nnreal` but we have no theory of normed semifields yet. -/
def posTangentConeAt (s : Set E) (x : E) : Set E :=
  { y : E |
    ∃ (c : ℕ → ℝ)(d : ℕ → E),
      (∀ᶠ n in atTop, x + d n ∈ s) ∧
        Tendsto c atTop atTop ∧ Tendsto (fun n => c n • d n) atTop (𝓝 y) }
#align pos_tangent_cone_at posTangentConeAt

theorem posTangentConeAt_mono : Monotone fun s => posTangentConeAt s a :=
  by
  rintro s t hst y ⟨c, d, hd, hc, hcd⟩
  exact ⟨c, d, mem_of_superset hd fun h hn => hst hn, hc, hcd⟩
#align pos_tangent_cone_at_mono posTangentConeAt_mono

theorem mem_posTangentConeAt_of_segment_subset {s : Set E} {x y : E} (h : segment ℝ x y ⊆ s) :
    y - x ∈ posTangentConeAt s x :=
  by
  let c := fun n : ℕ => (2 : ℝ) ^ n
  let d := fun n : ℕ => (c n)⁻¹ • (y - x)
  refine' ⟨c, d, Filter.univ_mem' fun n => h _, tendsto_pow_atTop_atTop_of_one_lt one_lt_two, _⟩
  show x + d n ∈ segment ℝ x y
  · rw [segment_eq_image']
    refine' ⟨(c n)⁻¹, ⟨_, _⟩, rfl⟩
    exacts[inv_nonneg.2 (pow_nonneg zero_le_two _), inv_le_one (one_le_pow_of_one_le one_le_two _)]
  show tendsto (fun n => c n • d n) at_top (𝓝 (y - x))
  · convert tendsto_const_nhds
    ext n
    simp only [d, smul_smul]
    rw [mul_inv_cancel, one_smul]
    exact pow_ne_zero _ two_ne_zero
#align mem_pos_tangent_cone_at_of_segment_subset mem_posTangentConeAt_of_segment_subset

theorem mem_posTangentConeAt_of_segment_subset' {s : Set E} {x y : E}
    (h : segment ℝ x (x + y) ⊆ s) : y ∈ posTangentConeAt s x := by
  simpa only [add_sub_cancel'] using mem_posTangentConeAt_of_segment_subset h
#align mem_pos_tangent_cone_at_of_segment_subset' mem_posTangentConeAt_of_segment_subset'

theorem posTangentConeAt_univ : posTangentConeAt univ a = univ :=
  eq_univ_of_forall fun x => mem_posTangentConeAt_of_segment_subset' (subset_univ _)
#align pos_tangent_cone_at_univ posTangentConeAt_univ

/-- If `f` has a local max on `s` at `a`, `f'` is the derivative of `f` at `a` within `s`, and
`y` belongs to the positive tangent cone of `s` at `a`, then `f' y ≤ 0`. -/
theorem IsLocalMaxOn.hasFderivWithinAt_nonpos {s : Set E} (h : IsLocalMaxOn f s a)
    (hf : HasFderivWithinAt f f' s a) {y} (hy : y ∈ posTangentConeAt s a) : f' y ≤ 0 :=
  by
  rcases hy with ⟨c, d, hd, hc, hcd⟩
  have hc' : tendsto (fun n => ‖c n‖) at_top at_top :=
    tendsto_at_top_mono (fun n => le_abs_self _) hc
  refine' le_of_tendsto (hf.lim at_top hd hc' hcd) _
  replace hd : tendsto (fun n => a + d n) at_top (𝓝[s] (a + 0))
  exact
    tendsto_inf.2
      ⟨tendsto_const_nhds.add (tangentConeAt.lim_zero _ hc' hcd), by rwa [tendsto_principal]⟩
  rw [add_zero] at hd
  replace h : ∀ᶠ n in at_top, f (a + d n) ≤ f a
  exact mem_map.1 (hd h)
  replace hc : ∀ᶠ n in at_top, 0 ≤ c n
  exact mem_map.1 (hc (mem_at_top (0 : ℝ)))
  filter_upwards [h, hc]
  simp only [smul_eq_mul, mem_preimage, subset_def]
  intro n hnf hn
  exact mul_nonpos_of_nonneg_of_nonpos hn (sub_nonpos.2 hnf)
#align is_local_max_on.has_fderiv_within_at_nonpos IsLocalMaxOn.hasFderivWithinAt_nonpos

/-- If `f` has a local max on `s` at `a` and `y` belongs to the positive tangent cone
of `s` at `a`, then `f' y ≤ 0`. -/
theorem IsLocalMaxOn.fderivWithin_nonpos {s : Set E} (h : IsLocalMaxOn f s a) {y}
    (hy : y ∈ posTangentConeAt s a) : (fderivWithin ℝ f s a : E → ℝ) y ≤ 0 :=
  if hf : DifferentiableWithinAt ℝ f s a then h.hasFderivWithinAt_nonpos hf.HasFderivWithinAt hy
  else by
    rw [fderivWithin_zero_of_not_differentiableWithinAt hf]
    rfl
#align is_local_max_on.fderiv_within_nonpos IsLocalMaxOn.fderivWithin_nonpos

/-- If `f` has a local max on `s` at `a`, `f'` is a derivative of `f` at `a` within `s`, and
both `y` and `-y` belong to the positive tangent cone of `s` at `a`, then `f' y ≤ 0`. -/
theorem IsLocalMaxOn.hasFderivWithinAt_eq_zero {s : Set E} (h : IsLocalMaxOn f s a)
    (hf : HasFderivWithinAt f f' s a) {y} (hy : y ∈ posTangentConeAt s a)
    (hy' : -y ∈ posTangentConeAt s a) : f' y = 0 :=
  le_antisymm (h.hasFderivWithinAt_nonpos hf hy) <| by
    simpa using h.has_fderiv_within_at_nonpos hf hy'
#align is_local_max_on.has_fderiv_within_at_eq_zero IsLocalMaxOn.hasFderivWithinAt_eq_zero

/-- If `f` has a local max on `s` at `a` and both `y` and `-y` belong to the positive tangent cone
of `s` at `a`, then `f' y = 0`. -/
theorem IsLocalMaxOn.fderivWithin_eq_zero {s : Set E} (h : IsLocalMaxOn f s a) {y}
    (hy : y ∈ posTangentConeAt s a) (hy' : -y ∈ posTangentConeAt s a) :
    (fderivWithin ℝ f s a : E → ℝ) y = 0 :=
  if hf : DifferentiableWithinAt ℝ f s a then
    h.hasFderivWithinAt_eq_zero hf.HasFderivWithinAt hy hy'
  else by
    rw [fderivWithin_zero_of_not_differentiableWithinAt hf]
    rfl
#align is_local_max_on.fderiv_within_eq_zero IsLocalMaxOn.fderivWithin_eq_zero

/-- If `f` has a local min on `s` at `a`, `f'` is the derivative of `f` at `a` within `s`, and
`y` belongs to the positive tangent cone of `s` at `a`, then `0 ≤ f' y`. -/
theorem IsLocalMinOn.hasFderivWithinAt_nonneg {s : Set E} (h : IsLocalMinOn f s a)
    (hf : HasFderivWithinAt f f' s a) {y} (hy : y ∈ posTangentConeAt s a) : 0 ≤ f' y := by
  simpa using h.neg.has_fderiv_within_at_nonpos hf.neg hy
#align is_local_min_on.has_fderiv_within_at_nonneg IsLocalMinOn.hasFderivWithinAt_nonneg

/-- If `f` has a local min on `s` at `a` and `y` belongs to the positive tangent cone
of `s` at `a`, then `0 ≤ f' y`. -/
theorem IsLocalMinOn.fderivWithin_nonneg {s : Set E} (h : IsLocalMinOn f s a) {y}
    (hy : y ∈ posTangentConeAt s a) : (0 : ℝ) ≤ (fderivWithin ℝ f s a : E → ℝ) y :=
  if hf : DifferentiableWithinAt ℝ f s a then h.hasFderivWithinAt_nonneg hf.HasFderivWithinAt hy
  else by
    rw [fderivWithin_zero_of_not_differentiableWithinAt hf]
    rfl
#align is_local_min_on.fderiv_within_nonneg IsLocalMinOn.fderivWithin_nonneg

/-- If `f` has a local max on `s` at `a`, `f'` is a derivative of `f` at `a` within `s`, and
both `y` and `-y` belong to the positive tangent cone of `s` at `a`, then `f' y ≤ 0`. -/
theorem IsLocalMinOn.hasFderivWithinAt_eq_zero {s : Set E} (h : IsLocalMinOn f s a)
    (hf : HasFderivWithinAt f f' s a) {y} (hy : y ∈ posTangentConeAt s a)
    (hy' : -y ∈ posTangentConeAt s a) : f' y = 0 := by
  simpa using h.neg.has_fderiv_within_at_eq_zero hf.neg hy hy'
#align is_local_min_on.has_fderiv_within_at_eq_zero IsLocalMinOn.hasFderivWithinAt_eq_zero

/-- If `f` has a local min on `s` at `a` and both `y` and `-y` belong to the positive tangent cone
of `s` at `a`, then `f' y = 0`. -/
theorem IsLocalMinOn.fderivWithin_eq_zero {s : Set E} (h : IsLocalMinOn f s a) {y}
    (hy : y ∈ posTangentConeAt s a) (hy' : -y ∈ posTangentConeAt s a) :
    (fderivWithin ℝ f s a : E → ℝ) y = 0 :=
  if hf : DifferentiableWithinAt ℝ f s a then
    h.hasFderivWithinAt_eq_zero hf.HasFderivWithinAt hy hy'
  else by
    rw [fderivWithin_zero_of_not_differentiableWithinAt hf]
    rfl
#align is_local_min_on.fderiv_within_eq_zero IsLocalMinOn.fderivWithin_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local minimum equals zero. -/
theorem IsLocalMin.hasFderivAt_eq_zero (h : IsLocalMin f a) (hf : HasFderivAt f f' a) : f' = 0 :=
  by
  ext y
  apply (h.on univ).hasFderivWithinAt_eq_zero hf.has_fderiv_within_at <;>
      rw [posTangentConeAt_univ] <;>
    apply mem_univ
#align is_local_min.has_fderiv_at_eq_zero IsLocalMin.hasFderivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local minimum equals zero. -/
theorem IsLocalMin.fderiv_eq_zero (h : IsLocalMin f a) : fderiv ℝ f a = 0 :=
  if hf : DifferentiableAt ℝ f a then h.hasFderivAt_eq_zero hf.HasFderivAt
  else fderiv_zero_of_not_differentiableAt hf
#align is_local_min.fderiv_eq_zero IsLocalMin.fderiv_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local maximum equals zero. -/
theorem IsLocalMax.hasFderivAt_eq_zero (h : IsLocalMax f a) (hf : HasFderivAt f f' a) : f' = 0 :=
  neg_eq_zero.1 <| h.neg.hasFderivAt_eq_zero hf.neg
#align is_local_max.has_fderiv_at_eq_zero IsLocalMax.hasFderivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local maximum equals zero. -/
theorem IsLocalMax.fderiv_eq_zero (h : IsLocalMax f a) : fderiv ℝ f a = 0 :=
  if hf : DifferentiableAt ℝ f a then h.hasFderivAt_eq_zero hf.HasFderivAt
  else fderiv_zero_of_not_differentiableAt hf
#align is_local_max.fderiv_eq_zero IsLocalMax.fderiv_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local extremum equals zero. -/
theorem IsLocalExtr.hasFderivAt_eq_zero (h : IsLocalExtr f a) : HasFderivAt f f' a → f' = 0 :=
  h.elim IsLocalMin.hasFderivAt_eq_zero IsLocalMax.hasFderivAt_eq_zero
#align is_local_extr.has_fderiv_at_eq_zero IsLocalExtr.hasFderivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local extremum equals zero. -/
theorem IsLocalExtr.fderiv_eq_zero (h : IsLocalExtr f a) : fderiv ℝ f a = 0 :=
  h.elim IsLocalMin.fderiv_eq_zero IsLocalMax.fderiv_eq_zero
#align is_local_extr.fderiv_eq_zero IsLocalExtr.fderiv_eq_zero

end Module

section Real

variable {f : ℝ → ℝ} {f' : ℝ} {a b : ℝ}

/-- **Fermat's Theorem**: the derivative of a function at a local minimum equals zero. -/
theorem IsLocalMin.hasDerivAt_eq_zero (h : IsLocalMin f a) (hf : HasDerivAt f f' a) : f' = 0 := by
  simpa using
    ContinuousLinearMap.ext_iff.1 (h.has_fderiv_at_eq_zero (hasDerivAt_iff_hasFderivAt.1 hf)) 1
#align is_local_min.has_deriv_at_eq_zero IsLocalMin.hasDerivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local minimum equals zero. -/
theorem IsLocalMin.deriv_eq_zero (h : IsLocalMin f a) : deriv f a = 0 :=
  if hf : DifferentiableAt ℝ f a then h.hasDerivAt_eq_zero hf.HasDerivAt
  else deriv_zero_of_not_differentiableAt hf
#align is_local_min.deriv_eq_zero IsLocalMin.deriv_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local maximum equals zero. -/
theorem IsLocalMax.hasDerivAt_eq_zero (h : IsLocalMax f a) (hf : HasDerivAt f f' a) : f' = 0 :=
  neg_eq_zero.1 <| h.neg.hasDerivAt_eq_zero hf.neg
#align is_local_max.has_deriv_at_eq_zero IsLocalMax.hasDerivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local maximum equals zero. -/
theorem IsLocalMax.deriv_eq_zero (h : IsLocalMax f a) : deriv f a = 0 :=
  if hf : DifferentiableAt ℝ f a then h.hasDerivAt_eq_zero hf.HasDerivAt
  else deriv_zero_of_not_differentiableAt hf
#align is_local_max.deriv_eq_zero IsLocalMax.deriv_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local extremum equals zero. -/
theorem IsLocalExtr.hasDerivAt_eq_zero (h : IsLocalExtr f a) : HasDerivAt f f' a → f' = 0 :=
  h.elim IsLocalMin.hasDerivAt_eq_zero IsLocalMax.hasDerivAt_eq_zero
#align is_local_extr.has_deriv_at_eq_zero IsLocalExtr.hasDerivAt_eq_zero

/-- **Fermat's Theorem**: the derivative of a function at a local extremum equals zero. -/
theorem IsLocalExtr.deriv_eq_zero (h : IsLocalExtr f a) : deriv f a = 0 :=
  h.elim IsLocalMin.deriv_eq_zero IsLocalMax.deriv_eq_zero
#align is_local_extr.deriv_eq_zero IsLocalExtr.deriv_eq_zero

end Real

section Rolle

variable (f f' : ℝ → ℝ) {a b : ℝ}

/-- A continuous function on a closed interval with `f a = f b` takes either its maximum
or its minimum value at a point in the interior of the interval. -/
theorem exists_Ioo_extr_on_Icc (hab : a < b) (hfc : ContinuousOn f (Icc a b)) (hfI : f a = f b) :
    ∃ c ∈ Ioo a b, IsExtrOn f (Icc a b) c :=
  by
  have ne : (Icc a b).Nonempty := nonempty_Icc.2 (le_of_lt hab)
  -- Consider absolute min and max points
  obtain ⟨c, cmem, cle⟩ : ∃ c ∈ Icc a b, ∀ x ∈ Icc a b, f c ≤ f x
  exact is_compact_Icc.exists_forall_le Ne hfc
  obtain ⟨C, Cmem, Cge⟩ : ∃ C ∈ Icc a b, ∀ x ∈ Icc a b, f x ≤ f C
  exact is_compact_Icc.exists_forall_ge Ne hfc
  by_cases hc : f c = f a
  · by_cases hC : f C = f a
    · have : ∀ x ∈ Icc a b, f x = f a := fun x hx => le_antisymm (hC ▸ Cge x hx) (hc ▸ cle x hx)
      -- `f` is a constant, so we can take any point in `Ioo a b`
      rcases exists_between hab with ⟨c', hc'⟩
      refine' ⟨c', hc', Or.inl _⟩
      intro x hx
      rw [mem_set_of_eq, this x hx, ← hC]
      exact Cge c' ⟨le_of_lt hc'.1, le_of_lt hc'.2⟩
    · refine' ⟨C, ⟨lt_of_le_of_ne Cmem.1 <| mt _ hC, lt_of_le_of_ne Cmem.2 <| mt _ hC⟩, Or.inr Cge⟩
      exacts[fun h => by rw [h], fun h => by rw [h, hfI]]
  · refine' ⟨c, ⟨lt_of_le_of_ne cmem.1 <| mt _ hc, lt_of_le_of_ne cmem.2 <| mt _ hc⟩, Or.inl cle⟩
    exacts[fun h => by rw [h], fun h => by rw [h, hfI]]
#align exists_Ioo_extr_on_Icc exists_Ioo_extr_on_Icc

/-- A continuous function on a closed interval with `f a = f b` has a local extremum at some
point of the corresponding open interval. -/
theorem exists_local_extr_Ioo (hab : a < b) (hfc : ContinuousOn f (Icc a b)) (hfI : f a = f b) :
    ∃ c ∈ Ioo a b, IsLocalExtr f c :=
  let ⟨c, cmem, hc⟩ := exists_Ioo_extr_on_Icc f hab hfc hfI
  ⟨c, cmem, hc.IsLocalExtr <| Icc_mem_nhds cmem.1 cmem.2⟩
#align exists_local_extr_Ioo exists_local_extr_Ioo

/-- **Rolle's Theorem** `has_deriv_at` version -/
theorem exists_hasDerivAt_eq_zero (hab : a < b) (hfc : ContinuousOn f (Icc a b)) (hfI : f a = f b)
    (hff' : ∀ x ∈ Ioo a b, HasDerivAt f (f' x) x) : ∃ c ∈ Ioo a b, f' c = 0 :=
  let ⟨c, cmem, hc⟩ := exists_local_extr_Ioo f hab hfc hfI
  ⟨c, cmem, hc.hasDerivAt_eq_zero <| hff' c cmem⟩
#align exists_has_deriv_at_eq_zero exists_hasDerivAt_eq_zero

/-- **Rolle's Theorem** `deriv` version -/
theorem exists_deriv_eq_zero (hab : a < b) (hfc : ContinuousOn f (Icc a b)) (hfI : f a = f b) :
    ∃ c ∈ Ioo a b, deriv f c = 0 :=
  let ⟨c, cmem, hc⟩ := exists_local_extr_Ioo f hab hfc hfI
  ⟨c, cmem, hc.deriv_eq_zero⟩
#align exists_deriv_eq_zero exists_deriv_eq_zero

variable {f f'} {l : ℝ}

/-- **Rolle's Theorem**, a version for a function on an open interval: if `f` has derivative `f'`
on `(a, b)` and has the same limit `l` at `𝓝[>] a` and `𝓝[<] b`, then `f' c = 0`
for some `c ∈ (a, b)`.  -/
theorem exists_hasDerivAt_eq_zero' (hab : a < b) (hfa : Tendsto f (𝓝[>] a) (𝓝 l))
    (hfb : Tendsto f (𝓝[<] b) (𝓝 l)) (hff' : ∀ x ∈ Ioo a b, HasDerivAt f (f' x) x) :
    ∃ c ∈ Ioo a b, f' c = 0 :=
  by
  have : ContinuousOn f (Ioo a b) := fun x hx => (hff' x hx).ContinuousAt.ContinuousWithinAt
  have hcont := continuousOn_Icc_extendFrom_Ioo hab.ne this hfa hfb
  obtain ⟨c, hc, hcextr⟩ : ∃ c ∈ Ioo a b, IsLocalExtr (extendFrom (Ioo a b) f) c :=
    by
    apply exists_local_extr_Ioo _ hab hcont
    rw [eq_lim_at_right_extendFrom_Ioo hab hfb]
    exact eq_lim_at_left_extendFrom_Ioo hab hfa
  use c, hc
  apply (hcextr.congr _).hasDerivAt_eq_zero (hff' c hc)
  rw [eventually_eq_iff_exists_mem]
  exact ⟨Ioo a b, Ioo_mem_nhds hc.1 hc.2, extendFrom_extends this⟩
#align exists_has_deriv_at_eq_zero' exists_hasDerivAt_eq_zero'

/-- **Rolle's Theorem**, a version for a function on an open interval: if `f` has the same limit
`l` at `𝓝[>] a` and `𝓝[<] b`, then `deriv f c = 0` for some `c ∈ (a, b)`. This version
does not require differentiability of `f` because we define `deriv f c = 0` whenever `f` is not
differentiable at `c`. -/
theorem exists_deriv_eq_zero' (hab : a < b) (hfa : Tendsto f (𝓝[>] a) (𝓝 l))
    (hfb : Tendsto f (𝓝[<] b) (𝓝 l)) : ∃ c ∈ Ioo a b, deriv f c = 0 :=
  by_cases
    (fun h : ∀ x ∈ Ioo a b, DifferentiableAt ℝ f x =>
      show ∃ c ∈ Ioo a b, deriv f c = 0 from
        exists_hasDerivAt_eq_zero' hab hfa hfb fun x hx => (h x hx).HasDerivAt)
    fun h : ¬∀ x ∈ Ioo a b, DifferentiableAt ℝ f x =>
    have h : ∃ x, x ∈ Ioo a b ∧ ¬DifferentiableAt ℝ f x :=
      by
      push_neg  at h
      exact h
    let ⟨c, hc, hcdiff⟩ := h
    ⟨c, hc, deriv_zero_of_not_differentiableAt hcdiff⟩
#align exists_deriv_eq_zero' exists_deriv_eq_zero'

end Rolle

namespace Polynomial

open BigOperators

/-- The number of roots of a real polynomial `p` is at most the number of roots of its derivative
that are not roots of `p` plus one. -/
theorem card_roots_toFinset_le_card_roots_derivative_diff_roots_succ (p : ℝ[X]) :
    p.roots.toFinset.card ≤ (p.derivative.roots.toFinset \ p.roots.toFinset).card + 1 :=
  by
  cases' eq_or_ne p.derivative 0 with hp' hp'
  · rw [eq_C_of_derivative_eq_zero hp', roots_C, Multiset.toFinset_zero, Finset.card_empty]
    exact zero_le _
  have hp : p ≠ 0 := ne_of_apply_ne derivative (by rwa [derivative_zero])
  refine' Finset.card_le_diff_of_interleaved fun x hx y hy hxy hxy' => _
  rw [Multiset.mem_toFinset, mem_roots hp] at hx hy
  obtain ⟨z, hz1, hz2⟩ :=
    exists_deriv_eq_zero (fun x : ℝ => eval x p) hxy p.continuous_on (hx.trans hy.symm)
  refine' ⟨z, _, hz1⟩
  rwa [Multiset.mem_toFinset, mem_roots hp', is_root, ← p.deriv]
#align polynomial.card_roots_to_finset_le_card_roots_derivative_diff_roots_succ Polynomial.card_roots_toFinset_le_card_roots_derivative_diff_roots_succ

/-- The number of roots of a real polynomial is at most the number of roots of its derivative plus
one. -/
theorem card_roots_toFinset_le_derivative (p : ℝ[X]) :
    p.roots.toFinset.card ≤ p.derivative.roots.toFinset.card + 1 :=
  p.card_roots_toFinset_le_card_roots_derivative_diff_roots_succ.trans <|
    add_le_add_right (Finset.card_mono <| Finset.sdiff_subset _ _) _
#align polynomial.card_roots_to_finset_le_derivative Polynomial.card_roots_toFinset_le_derivative

/-- The number of roots of a real polynomial (counted with multiplicities) is at most the number of
roots of its derivative (counted with multiplicities) plus one. -/
theorem card_roots_le_derivative (p : ℝ[X]) : p.roots.card ≤ p.derivative.roots.card + 1 :=
  calc
    p.roots.card = ∑ x in p.roots.toFinset, p.roots.count x :=
      (Multiset.toFinset_sum_count_eq _).symm
    _ = ∑ x in p.roots.toFinset, p.roots.count x - 1 + 1 :=
      (Eq.symm <|
        Finset.sum_congr rfl fun x hx =>
          tsub_add_cancel_of_le <|
            Nat.succ_le_iff.2 <| Multiset.count_pos.2 <| Multiset.mem_toFinset.1 hx)
    _ = (∑ x in p.roots.toFinset, p.rootMultiplicity x - 1) + p.roots.toFinset.card := by
      simp only [Finset.sum_add_distrib, Finset.card_eq_sum_ones, count_roots]
    _ ≤
        (∑ x in p.roots.toFinset, p.derivative.rootMultiplicity x) +
          ((p.derivative.roots.toFinset \ p.roots.toFinset).card + 1) :=
      (add_le_add
        (Finset.sum_le_sum fun x hx => rootMultiplicity_sub_one_le_derivative_rootMultiplicity _ _)
        p.card_roots_toFinset_le_card_roots_derivative_diff_roots_succ)
    _ ≤
        (∑ x in p.roots.toFinset, p.derivative.roots.count x) +
          ((∑ x in p.derivative.roots.toFinset \ p.roots.toFinset, p.derivative.roots.count x) +
            1) :=
      by
      simp only [← count_roots]
      refine' add_le_add_left (add_le_add_right ((Finset.card_eq_sum_ones _).trans_le _) _) _
      refine' Finset.sum_le_sum fun x hx => Nat.succ_le_iff.2 <| _
      rw [Multiset.count_pos, ← Multiset.mem_toFinset]
      exact (Finset.mem_sdiff.1 hx).1
    _ = p.derivative.roots.card + 1 :=
      by
      rw [← add_assoc, ← Finset.sum_union Finset.disjoint_sdiff, Finset.union_sdiff_self_eq_union, ←
        Multiset.toFinset_sum_count_eq, ← Finset.sum_subset (Finset.subset_union_right _ _)]
      intro x hx₁ hx₂
      simpa only [Multiset.mem_toFinset, Multiset.count_eq_zero] using hx₂

#align polynomial.card_roots_le_derivative Polynomial.card_roots_le_derivative

/-- The number of real roots of a polynomial is at most the number of roots of its derivative plus
one. -/
theorem card_rootSet_le_derivative {F : Type _} [CommRing F] [Algebra F ℝ] (p : F[X]) :
    Fintype.card (p.rootSet ℝ) ≤ Fintype.card (p.derivative.rootSet ℝ) + 1 := by
  simpa only [root_set_def, Finset.coe_sort_coe, Fintype.card_coe, derivative_map] using
    card_roots_to_finset_le_derivative (p.map (algebraMap F ℝ))
#align polynomial.card_root_set_le_derivative Polynomial.card_rootSet_le_derivative

end Polynomial


import data.fintype
import lib.sets
import lib.lists

#print set

example {α} {s : set α} (x : subtype s) : subtype s := x

/-
instance finite_all_unit : finite (λ (_ : unit), true)
:= finite.mk [()] (λ x _, by {cases x, simp})

def finite_sub [fin : finite (all α)] (s : set α) [decidable_pred s] [decidable_eq α] : finite s :=
  let ⟨xs, h₀⟩ := fin in
  ⟨list.filter s xs, λ x h₁, begin
  have h₂ : x ∈ xs := h₀ x true.intro,
  clear h₀,
  induction xs with y ys ih,
  {simp at *, assumption},
  {by_cases eq_hd: y = x,
    {rw [eq_hd, filter_hd],
      unfold has_mem.mem, delta list.mem,
      simp [h₁], assumption },
    {have xys : x ∈ ys := by {
         simp [‹¬y = x›] at h₂,
         cases h₂; simp * at *},
      by_cases s y; simp *}}
  end⟩

class has_ub (s : set ℕ) := (ub : ℕ) (h : ∀ n, s n → n ≤ ub)

instance finite_ub_nat {s : set ℕ} [decidable_pred s] [ub : has_ub s] : finite s :=
  finite.mk (list.filter s (iota (nat.succ ub.ub))) (by {
    intros x sx,
    have h := ub.h,
    specialize h x sx,
    revert h,
    generalize : has_ub.ub s = u,
    intro,

  })

def set_sum {α : Type u} [has_add α] [has_zero α] (s : set α) [f : finite s] : α :=
  finite.cases_on f (λ l _, list.foldl (λ a b, a + b) 0 l)
-/


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

! This file was ported from Lean 3 source module category_theory.sites.whiskering
! leanprover-community/mathlib commit 9f9015c645d85695581237cc761981036be8bd37
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Sites.Sheaf

/-!

In this file we construct the functor `Sheaf J A ⥤ Sheaf J B` between sheaf categories
obtained by composition with a functor `F : A ⥤ B`.

In order for the sheaf condition to be preserved, `F` must preserve the correct limits.
The lemma `presheaf.is_sheaf.comp` says that composition with such an `F` indeed preserves the
sheaf condition.

The functor between sheaf categories is called `Sheaf_compose J F`.
Given a natural transformation `η : F ⟶ G`, we obtain a natural transformation
`Sheaf_compose J F ⟶ Sheaf_compose J G`, which we call `Sheaf_compose_map J η`.

-/

namespace CategoryTheory

open CategoryTheory.Limits

universe v₁ v₂ u₁ u₂ u₃

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

variable {A : Type u₂} [Category.{max v₁ u₁} A]

variable {B : Type u₃} [Category.{max v₁ u₁} B]

variable {J : GrothendieckTopology C}

variable {U : C} (R : Presieve U)

variable (F : A ⥤ B)

namespace GrothendieckTopology.Cover

variable (P : Cᵒᵖ ⥤ A) {X : C} (S : J.cover X)

/-- The multicospan associated to a cover `S : J.cover X` and a presheaf of the form `P ⋙ F`
is isomorphic to the composition of the multicospan associated to `S` and `P`,
composed with `F`. -/
def multicospanComp : (S.index (P ⋙ F)).multicospan ≅ (S.index P).multicospan ⋙ F :=
  NatIso.ofComponents
    (fun t =>
      match t with
      | walking_multicospan.left a => eqToIso rfl
      | walking_multicospan.right b => eqToIso rfl)
    (by
      rintro (a | b) (a | b) (f | f | f)
      any_goals dsimp; erw [Functor.map_id, Functor.map_id, category.id_comp]
      any_goals dsimp; erw [category.comp_id, category.id_comp]; rfl)
#align category_theory.grothendieck_topology.cover.multicospan_comp CategoryTheory.GrothendieckTopology.Cover.multicospanComp

@[simp]
theorem multicospanComp_app_left (a) :
    (S.multicospanComp F P).app (WalkingMulticospan.left a) = eqToIso rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_app_left CategoryTheory.GrothendieckTopology.Cover.multicospanComp_app_left

@[simp]
theorem multicospanComp_app_right (b) :
    (S.multicospanComp F P).app (WalkingMulticospan.right b) = eqToIso rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_app_right CategoryTheory.GrothendieckTopology.Cover.multicospanComp_app_right

@[simp]
theorem multicospanComp_hom_app_left (a) :
    (S.multicospanComp F P).Hom.app (WalkingMulticospan.left a) = eqToHom rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_hom_app_left CategoryTheory.GrothendieckTopology.Cover.multicospanComp_hom_app_left

@[simp]
theorem multicospanComp_hom_app_right (b) :
    (S.multicospanComp F P).Hom.app (WalkingMulticospan.right b) = eqToHom rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_hom_app_right CategoryTheory.GrothendieckTopology.Cover.multicospanComp_hom_app_right

@[simp]
theorem multicospanComp_hom_inv_left (P : Cᵒᵖ ⥤ A) {X : C} (S : J.cover X) (a) :
    (S.multicospanComp F P).inv.app (WalkingMulticospan.left a) = eqToHom rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_hom_inv_left CategoryTheory.GrothendieckTopology.Cover.multicospanComp_hom_inv_left

@[simp]
theorem multicospanComp_hom_inv_right (P : Cᵒᵖ ⥤ A) {X : C} (S : J.cover X) (b) :
    (S.multicospanComp F P).inv.app (WalkingMulticospan.right b) = eqToHom rfl :=
  rfl
#align category_theory.grothendieck_topology.cover.multicospan_comp_hom_inv_right CategoryTheory.GrothendieckTopology.Cover.multicospanComp_hom_inv_right

/-- Mapping the multifork associated to a cover `S : J.cover X` and a presheaf `P` with
respect to a functor `F` is isomorphic (upto a natural isomorphism of the underlying functors)
to the multifork associated to `S` and `P ⋙ F`. -/
def mapMultifork :
    F.mapCone (S.Multifork P) ≅
      (Limits.Cones.postcompose (S.multicospanComp F P).Hom).obj (S.Multifork (P ⋙ F)) :=
  Cones.ext (eqToIso rfl)
    (by
      rintro (a | b)
      · dsimp
        simpa
      · dsimp
        simp
        dsimp [multifork.of_ι]
        simpa)
#align category_theory.grothendieck_topology.cover.map_multifork CategoryTheory.GrothendieckTopology.Cover.mapMultifork

end GrothendieckTopology.Cover

variable [∀ (X : C) (S : J.cover X) (P : Cᵒᵖ ⥤ A), PreservesLimit (S.index P).multicospan F]

theorem Presheaf.IsSheaf.comp {P : Cᵒᵖ ⥤ A} (hP : Presheaf.IsSheaf J P) :
    Presheaf.IsSheaf J (P ⋙ F) :=
  by
  rw [presheaf.is_sheaf_iff_multifork] at hP⊢
  intro X S
  obtain ⟨h⟩ := hP X S
  replace h := is_limit_of_preserves F h
  replace h := limits.is_limit.of_iso_limit h (S.map_multifork F P)
  exact ⟨limits.is_limit.postcompose_hom_equiv (S.multicospan_comp F P) _ h⟩
#align category_theory.presheaf.is_sheaf.comp CategoryTheory.Presheaf.IsSheaf.comp

variable (J)

/-- Composing a sheaf with a functor preserving the appropriate limits yields a functor
between sheaf categories. -/
@[simps]
def sheafCompose : Sheaf J A ⥤ Sheaf J B
    where
  obj G := ⟨G.val ⋙ F, Presheaf.IsSheaf.comp _ G.2⟩
  map G H η := ⟨whiskerRight η.val _⟩
  map_id' G := Sheaf.Hom.ext _ _ <| whiskerRight_id _
  map_comp' G H W f g := Sheaf.Hom.ext _ _ <| whiskerRight_comp _ _ _
#align category_theory.Sheaf_compose CategoryTheory.sheafCompose

end CategoryTheory


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

Countable sets.
-/

import data.equiv.list data.set.finite logic.function data.set.function
noncomputable theory

open function set encodable

open classical (hiding some)
local attribute [instance] prop_decidable
universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

namespace set

/-- Countable sets

A set is countable if there exists an encoding of the set into the natural numbers.
An encoding is an injection with a partial inverse, which can be viewed as a
constructive analogue of countability. (For the most part, theorems about
`countable` will be classical and `encodable` will be constructive.)
-/
def countable (s : set α) : Prop := nonempty (encodable s)

lemma countable_iff_exists_injective {s : set α} :
  countable s ↔ ∃f:s → ℕ, injective f :=
⟨λ ⟨h⟩, by exactI ⟨encode, encode_injective⟩,
 λ ⟨f, h⟩, ⟨⟨f, partial_inv f, partial_inv_left h⟩⟩⟩

lemma countable_iff_exists_inj_on {s : set α} :
  countable s ↔ ∃ f : α → ℕ, inj_on f s :=
countable_iff_exists_injective.trans
⟨λ ⟨f, hf⟩, ⟨λ a, if h : a ∈ s then f ⟨a, h⟩ else 0,
   λ a b as bs h, congr_arg subtype.val $
     hf $ by simpa [as, bs] using h⟩,
 λ ⟨f, hf⟩, ⟨_, inj_on_iff_injective.1 hf⟩⟩

lemma countable_iff_exists_surjective [ne : inhabited α] {s : set α} :
  countable s ↔ ∃f:ℕ → α, s ⊆ range f :=
⟨λ ⟨h⟩, by exactI ⟨λ n, ((decode s n).map subtype.val).iget,
  λ a as, ⟨encode (⟨a, as⟩ : s), by simp [encodek]⟩⟩,
 λ ⟨f, hf⟩, ⟨⟨
  λ x, inv_fun f x.1,
  λ n, if h : f n ∈ s then some ⟨f n, h⟩ else none,
  λ ⟨x, hx⟩, begin
    have := inv_fun_eq (hf hx), dsimp at this ⊢,
    simp [this, hx]
  end⟩⟩⟩

def countable.to_encodable {s : set α} : countable s → encodable s :=
classical.choice

lemma countable_encodable' (s : set α) [H : encodable s] : countable s :=
⟨H⟩

lemma countable_encodable [encodable α] (s : set α) : countable s :=
⟨by apply_instance⟩

lemma exists_surjective_of_countable {s : set α} (hs : s ≠ ∅) (hc : countable s) :
  ∃f:ℕ → α, s = range f :=
begin
  rcases ne_empty_iff_exists_mem.1 hs with ⟨x, hx⟩,
  letI : encodable s := countable.to_encodable hc,
  letI : inhabited s := ⟨⟨x, hx⟩⟩,
  have : countable (univ : set s) := countable_encodable _,
  rcases countable_iff_exists_surjective.1 this with ⟨g, hg⟩,
  have : range g = univ := univ_subset_iff.1 hg,
  use subtype.val ∘ g,
  rw [range_comp, this],
  simp
end

@[simp]

@[simp] lemma countable_singleton (a : α) : countable ({a} : set α) :=
⟨of_equiv _ (equiv.set.singleton a)⟩

lemma countable_subset {s₁ s₂ : set α} (h : s₁ ⊆ s₂) : countable s₂ → countable s₁
| ⟨H⟩ := ⟨@of_inj _ _ H _ (embedding_of_subset h).2⟩

lemma countable_image {s : set α} (f : α → β) (hs : countable s) : countable (f '' s) :=
let f' : s → f '' s := λ⟨a, ha⟩, ⟨f a, mem_image_of_mem f ha⟩ in
have hf' : surjective f', from assume ⟨b, a, ha, hab⟩, ⟨⟨a, ha⟩, subtype.eq hab⟩,
⟨@encodable.of_inj _ _ hs.to_encodable (surj_inv hf') (injective_surj_inv hf')⟩

lemma countable_range [encodable α] (f : α → β) : countable (range f) :=
by rw ← image_univ; exact countable_image _ (countable_encodable _)

lemma countable_of_injective_of_countable_image {s : set α} {f : α → β}
  (hf : inj_on f s) (hs : countable (f '' s)) : countable s :=
let ⟨g, hg⟩ := countable_iff_exists_inj_on.1 hs in
countable_iff_exists_inj_on.2 ⟨g ∘ f, inj_on_comp (maps_to_image _ _) hg hf⟩

lemma countable_Union {t : α → set β} [encodable α] (ht : ∀a, countable (t a)) :
  countable (⋃a, t a) :=
by haveI := (λ a, (ht a).to_encodable);
   rw Union_eq_range_sigma; apply countable_range

lemma countable_bUnion {s : set α} {t : α → set β} (hs : countable s) (ht : ∀a∈s, countable (t a)) :
  countable (⋃a∈s, t a) :=
begin
  rw bUnion_eq_Union,
  haveI := hs.to_encodable,
  exact countable_Union (by simpa using ht)
end

lemma countable_sUnion {s : set (set α)} (hs : countable s) (h : ∀a∈s, countable a) :
  countable (⋃₀ s) :=
by rw sUnion_eq_bUnion; exact countable_bUnion hs h

lemma countable_Union_Prop {p : Prop} {t : p → set β} (ht : ∀h:p, countable (t h)) :
  countable (⋃h:p, t h) :=
by by_cases p; simp [h, ht]

lemma countable_union {s₁ s₂ : set α} (h₁ : countable s₁) (h₂ : countable s₂) : countable (s₁ ∪ s₂) :=
by rw union_eq_Union; exact
countable_Union (bool.forall_bool.2 ⟨h₂, h₁⟩)

lemma countable_insert {s : set α} {a : α} (h : countable s) : countable (insert a s) :=
by rw [set.insert_eq]; from countable_union (countable_singleton _) h

lemma countable_finite {s : set α} : finite s → countable s
| ⟨h⟩ := nonempty_of_trunc (by exactI trunc_encodable_of_fintype s)

lemma countable_set_of_finite_subset {s : set α} : countable s →
  countable {t | finite t ∧ t ⊆ s} | ⟨h⟩ :=
begin
  resetI,
  refine countable_subset _ (countable_range
    (λ t : finset s, {a | ∃ h:a ∈ s, subtype.mk a h ∈ t})),
  rintro t ⟨⟨ht⟩, ts⟩,
  refine ⟨finset.univ.map (embedding_of_subset ts),
    set.ext $ λ a, _⟩,
  simp, split,
  { rintro ⟨as, b, bt, e⟩,
    cases congr_arg subtype.val e, exact bt },
  { exact λ h, ⟨ts h, _, h, rfl⟩ }
end

lemma countable_pi {π : α → Type*} [fintype α] {s : Πa, set (π a)} (hs : ∀a, countable (s a)) :
  countable {f : Πa, π a | ∀a, f a ∈ s a} :=
countable_subset
  (show {f : Πa, π a | ∀a, f a ∈ s a} ⊆ range (λf : Πa, s a, λa, (f a).1), from
    assume f hf, ⟨λa, ⟨f a, hf a⟩, funext $ assume a, rfl⟩) $
have trunc (encodable (Π (a : α), s a)), from
  @encodable.fintype_pi α _ _ _ (assume a, (hs a).to_encodable),
trunc.induction_on this $ assume h,
@countable_range _ _ h _

lemma countable_prod {s : set α} {t : set β} (hs : countable s) (ht : countable t) :
  countable (set.prod s t) :=
begin
  haveI : encodable s := hs.to_encodable,
  haveI : encodable t := ht.to_encodable,
  haveI : encodable (s × t) := by apply_instance,
  have : range (λp, ⟨p.1, p.2⟩ : s × t → α × β) = set.prod s t,
  { ext z,
    rcases z with ⟨x, y⟩,
    simp only [exists_prop, set.mem_range, set_coe.exists, prod.mk.inj_iff,
               set.prod_mk_mem_set_prod_eq, subtype.coe_mk, prod.exists],
    split,
    { rintros ⟨x', x's, y', y't, x'x, y'y⟩,
      simp [x'x.symm, y'y.symm, x's, y't] },
    { rintros ⟨xs, yt⟩,
      exact ⟨x, xs, y, yt, rfl, rfl⟩ }},
  rw ← this,
  exact countable_range _
end

end set