/-
Copyright (c) 2022 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 topology.filter
! leanprover-community/mathlib commit b6da1a0b3e7cd83b1f744c49ce48ef8c6307d2f6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.Filter.Lift
import Mathbin.Topology.Separation
import Mathbin.Data.Set.Intervals.Monotone

/-!
# Topology on the set of filters on a type

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

In this file introduce topology on `filter α`. It is generated by the sets
`set.Iic (𝓟 s) = {l : filter α | s ∈ l}`, `s : set α`. A set `s : set (filter α)` is open if and
only if it is a union of a family of these basic open sets, see `filter.is_open_iff`.

This topology has the following important properties.

* If `X` is a topological space, then the map `𝓝 : X → filter X` is a topology inducing map.

* In particular, it is a continuous map, so `𝓝 ∘ f` tends to `𝓝 (𝓝 a)` whenever `f` tends to `𝓝 a`.

* If `X` is an ordered topological space with order topology and no max element, then `𝓝 ∘ f` tends
  to `𝓝 filter.at_top` whenever `f` tends to `filter.at_top`.

* It turns `filter X` into a T₀ space and the order on `filter X` is the dual of the
  `specialization_order (filter X)`.

## Tags

filter, topological space
-/

open Set Filter TopologicalSpace

open Filter Topology

variable {ι : Sort _} {α β X Y : Type _}

namespace Filter

/-- Topology on `filter α` is generated by the sets `set.Iic (𝓟 s) = {l : filter α | s ∈ l}`,
`s : set α`. A set `s : set (filter α)` is open if and only if it is a union of a family of these
basic open sets, see `filter.is_open_iff`. -/
instance : TopologicalSpace (Filter α) :=
  generateFrom <| range <| Iic ∘ 𝓟

/- warning: filter.is_open_Iic_principal -> Filter.isOpen_Iic_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, IsOpen.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α s))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, IsOpen.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α s))
Case conversion may be inaccurate. Consider using '#align filter.is_open_Iic_principal Filter.isOpen_Iic_principalₓ'. -/
theorem isOpen_Iic_principal {s : Set α} : IsOpen (Iic (𝓟 s)) :=
  GenerateOpen.basic _ (mem_range_self _)
#align filter.is_open_Iic_principal Filter.isOpen_Iic_principal

/- warning: filter.is_open_set_of_mem -> Filter.isOpen_setOf_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, IsOpen.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (setOf.{u1} (Filter.{u1} α) (fun (l : Filter.{u1} α) => Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, IsOpen.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (setOf.{u1} (Filter.{u1} α) (fun (l : Filter.{u1} α) => Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l))
Case conversion may be inaccurate. Consider using '#align filter.is_open_set_of_mem Filter.isOpen_setOf_memₓ'. -/
theorem isOpen_setOf_mem {s : Set α} : IsOpen { l : Filter α | s ∈ l } := by
  simpa only [Iic_principal] using is_open_Iic_principal
#align filter.is_open_set_of_mem Filter.isOpen_setOf_mem

/- warning: filter.is_topological_basis_Iic_principal -> Filter.isTopologicalBasis_Iic_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, TopologicalSpace.IsTopologicalBasis.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Set.range.{u1, succ u1} (Set.{u1} (Filter.{u1} α)) (Set.{u1} α) (Function.comp.{succ u1, succ u1, succ u1} (Set.{u1} α) (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.principal.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}}, TopologicalSpace.IsTopologicalBasis.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Set.range.{u1, succ u1} (Set.{u1} (Filter.{u1} α)) (Set.{u1} α) (Function.comp.{succ u1, succ u1, succ u1} (Set.{u1} α) (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (Filter.principal.{u1} α)))
Case conversion may be inaccurate. Consider using '#align filter.is_topological_basis_Iic_principal Filter.isTopologicalBasis_Iic_principalₓ'. -/
theorem isTopologicalBasis_Iic_principal :
    IsTopologicalBasis (range (Iic ∘ 𝓟 : Set α → Set (Filter α))) :=
  { exists_subset_inter := by
      rintro _ ⟨s, rfl⟩ _ ⟨t, rfl⟩ l hl
      exact ⟨Iic (𝓟 s) ∩ Iic (𝓟 t), ⟨s ∩ t, by simp⟩, hl, subset.rfl⟩
    unionₛ_eq := unionₛ_eq_univ_iff.2 fun l => ⟨Iic ⊤, ⟨univ, congr_arg Iic principal_univ⟩, le_top⟩
    eq_generateFrom := rfl }
#align filter.is_topological_basis_Iic_principal Filter.isTopologicalBasis_Iic_principal

/- warning: filter.is_open_iff -> Filter.isOpen_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)}, Iff (IsOpen.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) s) (Exists.{succ u1} (Set.{u1} (Set.{u1} α)) (fun (T : Set.{u1} (Set.{u1} α)) => Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) s (Set.unionᵢ.{u1, succ u1} (Filter.{u1} α) (Set.{u1} α) (fun (t : Set.{u1} α) => Set.unionᵢ.{u1, 0} (Filter.{u1} α) (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) t T) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) t T) => Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α t))))))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)}, Iff (IsOpen.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) s) (Exists.{succ u1} (Set.{u1} (Set.{u1} α)) (fun (T : Set.{u1} (Set.{u1} α)) => Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) s (Set.unionᵢ.{u1, succ u1} (Filter.{u1} α) (Set.{u1} α) (fun (t : Set.{u1} α) => Set.unionᵢ.{u1, 0} (Filter.{u1} α) (Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) t T) (fun (H : Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) t T) => Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α t))))))
Case conversion may be inaccurate. Consider using '#align filter.is_open_iff Filter.isOpen_iffₓ'. -/
theorem isOpen_iff {s : Set (Filter α)} : IsOpen s ↔ ∃ T : Set (Set α), s = ⋃ t ∈ T, Iic (𝓟 t) :=
  isTopologicalBasis_Iic_principal.open_iff_eq_unionₛ.trans <| by
    simp only [exists_subset_range_iff, sUnion_image]
#align filter.is_open_iff Filter.isOpen_iff

/- warning: filter.nhds_eq -> Filter.nhds_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l) (Filter.lift'.{u1, u1} α (Filter.{u1} α) l (Function.comp.{succ u1, succ u1, succ u1} (Set.{u1} α) (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.principal.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l) (Filter.lift'.{u1, u1} α (Filter.{u1} α) l (Function.comp.{succ u1, succ u1, succ u1} (Set.{u1} α) (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (Filter.principal.{u1} α)))
Case conversion may be inaccurate. Consider using '#align filter.nhds_eq Filter.nhds_eqₓ'. -/
theorem nhds_eq (l : Filter α) : 𝓝 l = l.lift' (Iic ∘ 𝓟) :=
  nhds_generateFrom.trans <| by
    simp only [mem_set_of_eq, and_comm' (l ∈ _), infᵢ_and, infᵢ_range, Filter.lift', Filter.lift,
      (· ∘ ·), mem_Iic, le_principal_iff]
#align filter.nhds_eq Filter.nhds_eq

/- warning: filter.nhds_eq' -> Filter.nhds_eq' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l) (Filter.lift'.{u1, u1} α (Filter.{u1} α) l (fun (s : Set.{u1} α) => setOf.{u1} (Filter.{u1} α) (fun (l' : Filter.{u1} α) => Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l')))
but is expected to have type
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l) (Filter.lift'.{u1, u1} α (Filter.{u1} α) l (fun (s : Set.{u1} α) => setOf.{u1} (Filter.{u1} α) (fun (l' : Filter.{u1} α) => Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l')))
Case conversion may be inaccurate. Consider using '#align filter.nhds_eq' Filter.nhds_eq'ₓ'. -/
theorem nhds_eq' (l : Filter α) : 𝓝 l = l.lift' fun s => { l' | s ∈ l' } := by
  simpa only [(· ∘ ·), Iic_principal] using nhds_eq l
#align filter.nhds_eq' Filter.nhds_eq'

/- warning: filter.tendsto_nhds -> Filter.tendsto_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {la : Filter.{u1} α} {lb : Filter.{u2} β} {f : α -> (Filter.{u2} β)}, Iff (Filter.Tendsto.{u1, u2} α (Filter.{u2} β) f la (nhds.{u2} (Filter.{u2} β) (Filter.topologicalSpace.{u2} β) lb)) (forall (s : Set.{u2} β), (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) s lb) -> (Filter.Eventually.{u1} α (fun (a : α) => Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) s (f a)) la))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {la : Filter.{u2} α} {lb : Filter.{u1} β} {f : α -> (Filter.{u1} β)}, Iff (Filter.Tendsto.{u2, u1} α (Filter.{u1} β) f la (nhds.{u1} (Filter.{u1} β) (Filter.instTopologicalSpaceFilter.{u1} β) lb)) (forall (s : Set.{u1} β), (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) s lb) -> (Filter.Eventually.{u2} α (fun (a : α) => Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) s (f a)) la))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_nhds Filter.tendsto_nhdsₓ'. -/
protected theorem tendsto_nhds {la : Filter α} {lb : Filter β} {f : α → Filter β} :
    Tendsto f la (𝓝 lb) ↔ ∀ s ∈ lb, ∀ᶠ a in la, s ∈ f a := by
  simp only [nhds_eq', tendsto_lift', mem_set_of_eq]
#align filter.tendsto_nhds Filter.tendsto_nhds

/- warning: filter.has_basis.nhds -> Filter.HasBasis.nhds is a dubious translation:
lean 3 declaration is
  forall {ι : Sort.{u1}} {α : Type.{u2}} {l : Filter.{u2} α} {p : ι -> Prop} {s : ι -> (Set.{u2} α)}, (Filter.HasBasis.{u2, u1} α ι l p s) -> (Filter.HasBasis.{u2, u1} (Filter.{u2} α) ι (nhds.{u2} (Filter.{u2} α) (Filter.topologicalSpace.{u2} α) l) p (fun (i : ι) => Set.Iic.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.partialOrder.{u2} α)) (Filter.principal.{u2} α (s i))))
but is expected to have type
  forall {ι : Sort.{u1}} {α : Type.{u2}} {l : Filter.{u2} α} {p : ι -> Prop} {s : ι -> (Set.{u2} α)}, (Filter.HasBasis.{u2, u1} α ι l p s) -> (Filter.HasBasis.{u2, u1} (Filter.{u2} α) ι (nhds.{u2} (Filter.{u2} α) (Filter.instTopologicalSpaceFilter.{u2} α) l) p (fun (i : ι) => Set.Iic.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.instPartialOrderFilter.{u2} α)) (Filter.principal.{u2} α (s i))))
Case conversion may be inaccurate. Consider using '#align filter.has_basis.nhds Filter.HasBasis.nhdsₓ'. -/
theorem HasBasis.nhds {l : Filter α} {p : ι → Prop} {s : ι → Set α} (h : HasBasis l p s) :
    HasBasis (𝓝 l) p fun i => Iic (𝓟 (s i)) :=
  by
  rw [nhds_eq]
  exact h.lift' monotone_principal.Iic
#align filter.has_basis.nhds Filter.HasBasis.nhds

/-- Neighborhoods of a countably generated filter is a countably generated filter. -/
instance {l : Filter α} [IsCountablyGenerated l] : IsCountablyGenerated (𝓝 l) :=
  let ⟨b, hb⟩ := l.exists_antitone_basis
  HasCountableBasis.isCountablyGenerated <| ⟨hb.nhds, Set.to_countable _⟩

/- warning: filter.has_basis.nhds' -> Filter.HasBasis.nhds' is a dubious translation:
lean 3 declaration is
  forall {ι : Sort.{u1}} {α : Type.{u2}} {l : Filter.{u2} α} {p : ι -> Prop} {s : ι -> (Set.{u2} α)}, (Filter.HasBasis.{u2, u1} α ι l p s) -> (Filter.HasBasis.{u2, u1} (Filter.{u2} α) ι (nhds.{u2} (Filter.{u2} α) (Filter.topologicalSpace.{u2} α) l) p (fun (i : ι) => setOf.{u2} (Filter.{u2} α) (fun (l' : Filter.{u2} α) => Membership.Mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (Filter.hasMem.{u2} α) (s i) l')))
but is expected to have type
  forall {ι : Sort.{u1}} {α : Type.{u2}} {l : Filter.{u2} α} {p : ι -> Prop} {s : ι -> (Set.{u2} α)}, (Filter.HasBasis.{u2, u1} α ι l p s) -> (Filter.HasBasis.{u2, u1} (Filter.{u2} α) ι (nhds.{u2} (Filter.{u2} α) (Filter.instTopologicalSpaceFilter.{u2} α) l) p (fun (i : ι) => setOf.{u2} (Filter.{u2} α) (fun (l' : Filter.{u2} α) => Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) (s i) l')))
Case conversion may be inaccurate. Consider using '#align filter.has_basis.nhds' Filter.HasBasis.nhds'ₓ'. -/
theorem HasBasis.nhds' {l : Filter α} {p : ι → Prop} {s : ι → Set α} (h : HasBasis l p s) :
    HasBasis (𝓝 l) p fun i => { l' | s i ∈ l' } := by simpa only [Iic_principal] using h.nhds
#align filter.has_basis.nhds' Filter.HasBasis.nhds'

/- warning: filter.mem_nhds_iff -> Filter.mem_nhds_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α} {S : Set.{u1} (Filter.{u1} α)}, Iff (Membership.Mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (Filter.hasMem.{u1} (Filter.{u1} α)) S (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) => HasSubset.Subset.{u1} (Set.{u1} (Filter.{u1} α)) (Set.hasSubset.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α t)) S)))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α} {S : Set.{u1} (Filter.{u1} α)}, Iff (Membership.mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (instMembershipSetFilter.{u1} (Filter.{u1} α)) S (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l) (HasSubset.Subset.{u1} (Set.{u1} (Filter.{u1} α)) (Set.instHasSubsetSet.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α t)) S)))
Case conversion may be inaccurate. Consider using '#align filter.mem_nhds_iff Filter.mem_nhds_iffₓ'. -/
theorem mem_nhds_iff {l : Filter α} {S : Set (Filter α)} : S ∈ 𝓝 l ↔ ∃ t ∈ l, Iic (𝓟 t) ⊆ S :=
  l.basis_sets.nhds.mem_iff
#align filter.mem_nhds_iff Filter.mem_nhds_iff

/- warning: filter.mem_nhds_iff' -> Filter.mem_nhds_iff' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α} {S : Set.{u1} (Filter.{u1} α)}, Iff (Membership.Mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (Filter.hasMem.{u1} (Filter.{u1} α)) S (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) => forall {{l' : Filter.{u1} α}}, (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l') -> (Membership.Mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasMem.{u1} (Filter.{u1} α)) l' S))))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α} {S : Set.{u1} (Filter.{u1} α)}, Iff (Membership.mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (instMembershipSetFilter.{u1} (Filter.{u1} α)) S (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l) (forall {{l' : Filter.{u1} α}}, (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l') -> (Membership.mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instMembershipSet.{u1} (Filter.{u1} α)) l' S))))
Case conversion may be inaccurate. Consider using '#align filter.mem_nhds_iff' Filter.mem_nhds_iff'ₓ'. -/
theorem mem_nhds_iff' {l : Filter α} {S : Set (Filter α)} :
    S ∈ 𝓝 l ↔ ∃ t ∈ l, ∀ ⦃l' : Filter α⦄, t ∈ l' → l' ∈ S :=
  l.basis_sets.nhds'.mem_iff
#align filter.mem_nhds_iff' Filter.mem_nhds_iff'

/- warning: filter.nhds_bot -> Filter.nhds_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α)))) (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} (Filter.{u1} α) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))
but is expected to have type
  forall {α : Type.{u1}}, Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toBot.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α)))) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} (Filter.{u1} α) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toBot.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))))
Case conversion may be inaccurate. Consider using '#align filter.nhds_bot Filter.nhds_botₓ'. -/
@[simp]
theorem nhds_bot : 𝓝 (⊥ : Filter α) = pure ⊥ := by simp [nhds_eq, lift'_bot monotone_principal.Iic]
#align filter.nhds_bot Filter.nhds_bot

/- warning: filter.nhds_top -> Filter.nhds_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Top.top.{u1} (Filter.{u1} α) (Filter.hasTop.{u1} α))) (Top.top.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.hasTop.{u1} (Filter.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}}, Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Top.top.{u1} (Filter.{u1} α) (Filter.instTopFilter.{u1} α))) (Top.top.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instTopFilter.{u1} (Filter.{u1} α)))
Case conversion may be inaccurate. Consider using '#align filter.nhds_top Filter.nhds_topₓ'. -/
@[simp]
theorem nhds_top : 𝓝 (⊤ : Filter α) = ⊤ := by simp [nhds_eq]
#align filter.nhds_top Filter.nhds_top

/- warning: filter.nhds_principal -> Filter.nhds_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Set.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Filter.principal.{u1} α s)) (Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α s)))
but is expected to have type
  forall {α : Type.{u1}} (s : Set.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Filter.principal.{u1} α s)) (Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α s)))
Case conversion may be inaccurate. Consider using '#align filter.nhds_principal Filter.nhds_principalₓ'. -/
@[simp]
theorem nhds_principal (s : Set α) : 𝓝 (𝓟 s) = 𝓟 (Iic (𝓟 s)) :=
  (hasBasis_principal s).nhds.eq_of_same_basis (hasBasis_principal _)
#align filter.nhds_principal Filter.nhds_principal

/- warning: filter.nhds_pure -> Filter.nhds_pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (x : α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} α x)) (Filter.principal.{u1} (Filter.{u1} α) (Insert.insert.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasInsert.{u1} (Filter.{u1} α)) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) (Singleton.singleton.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasSingleton.{u1} (Filter.{u1} α)) (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} α x))))
but is expected to have type
  forall {α : Type.{u1}} (x : α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} α x)) (Filter.principal.{u1} (Filter.{u1} α) (Insert.insert.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instInsertSet.{u1} (Filter.{u1} α)) (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toBot.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) (Singleton.singleton.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instSingletonSet.{u1} (Filter.{u1} α)) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} α x))))
Case conversion may be inaccurate. Consider using '#align filter.nhds_pure Filter.nhds_pureₓ'. -/
@[simp]
theorem nhds_pure (x : α) : 𝓝 (pure x : Filter α) = 𝓟 {⊥, pure x} := by
  rw [← principal_singleton, nhds_principal, principal_singleton, Iic_pure]
#align filter.nhds_pure Filter.nhds_pure

/- warning: filter.nhds_infi -> Filter.nhds_infᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Sort.{u1}} {α : Type.{u2}} (f : ι -> (Filter.{u2} α)), Eq.{succ u2} (Filter.{u2} (Filter.{u2} α)) (nhds.{u2} (Filter.{u2} α) (Filter.topologicalSpace.{u2} α) (infᵢ.{u2, u1} (Filter.{u2} α) (ConditionallyCompleteLattice.toHasInf.{u2} (Filter.{u2} α) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} α) (Filter.completeLattice.{u2} α))) ι (fun (i : ι) => f i))) (infᵢ.{u2, u1} (Filter.{u2} (Filter.{u2} α)) (ConditionallyCompleteLattice.toHasInf.{u2} (Filter.{u2} (Filter.{u2} α)) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} (Filter.{u2} α)) (Filter.completeLattice.{u2} (Filter.{u2} α)))) ι (fun (i : ι) => nhds.{u2} (Filter.{u2} α) (Filter.topologicalSpace.{u2} α) (f i)))
but is expected to have type
  forall {ι : Sort.{u1}} {α : Type.{u2}} (f : ι -> (Filter.{u2} α)), Eq.{succ u2} (Filter.{u2} (Filter.{u2} α)) (nhds.{u2} (Filter.{u2} α) (Filter.instTopologicalSpaceFilter.{u2} α) (infᵢ.{u2, u1} (Filter.{u2} α) (ConditionallyCompleteLattice.toInfSet.{u2} (Filter.{u2} α) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} α) (Filter.instCompleteLatticeFilter.{u2} α))) ι (fun (i : ι) => f i))) (infᵢ.{u2, u1} (Filter.{u2} (Filter.{u2} α)) (ConditionallyCompleteLattice.toInfSet.{u2} (Filter.{u2} (Filter.{u2} α)) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} (Filter.{u2} α)) (Filter.instCompleteLatticeFilter.{u2} (Filter.{u2} α)))) ι (fun (i : ι) => nhds.{u2} (Filter.{u2} α) (Filter.instTopologicalSpaceFilter.{u2} α) (f i)))
Case conversion may be inaccurate. Consider using '#align filter.nhds_infi Filter.nhds_infᵢₓ'. -/
@[simp]
theorem nhds_infᵢ (f : ι → Filter α) : 𝓝 (⨅ i, f i) = ⨅ i, 𝓝 (f i) :=
  by
  simp only [nhds_eq]
  apply lift'_infi_of_map_univ <;> simp
#align filter.nhds_infi Filter.nhds_infᵢ

/- warning: filter.nhds_inf -> Filter.nhds_inf is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l₁ : Filter.{u1} α) (l₂ : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Inf.inf.{u1} (Filter.{u1} α) (Filter.hasInf.{u1} α) l₁ l₂)) (Inf.inf.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.hasInf.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l₁) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l₂))
but is expected to have type
  forall {α : Type.{u1}} (l₁ : Filter.{u1} α) (l₂ : Filter.{u1} α), Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Inf.inf.{u1} (Filter.{u1} α) (Filter.instInfFilter.{u1} α) l₁ l₂)) (Inf.inf.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instInfFilter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l₁) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l₂))
Case conversion may be inaccurate. Consider using '#align filter.nhds_inf Filter.nhds_infₓ'. -/
@[simp]
theorem nhds_inf (l₁ l₂ : Filter α) : 𝓝 (l₁ ⊓ l₂) = 𝓝 l₁ ⊓ 𝓝 l₂ := by
  simpa only [infᵢ_bool_eq] using nhds_infᵢ fun b => cond b l₁ l₂
#align filter.nhds_inf Filter.nhds_inf

/- warning: filter.monotone_nhds -> Filter.monotone_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Monotone.{u1, u1} (Filter.{u1} α) (Filter.{u1} (Filter.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.partialOrder.{u1} (Filter.{u1} α))) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α))
but is expected to have type
  forall {α : Type.{u1}}, Monotone.{u1, u1} (Filter.{u1} α) (Filter.{u1} (Filter.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instPartialOrderFilter.{u1} (Filter.{u1} α))) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α))
Case conversion may be inaccurate. Consider using '#align filter.monotone_nhds Filter.monotone_nhdsₓ'. -/
theorem monotone_nhds : Monotone (𝓝 : Filter α → Filter (Filter α)) :=
  Monotone.of_map_inf nhds_inf
#align filter.monotone_nhds Filter.monotone_nhds

/- warning: filter.Inter_nhds -> Filter.interₛ_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) (Set.interₛ.{u1} (Filter.{u1} α) (setOf.{u1} (Set.{u1} (Filter.{u1} α)) (fun (s : Set.{u1} (Filter.{u1} α)) => Membership.Mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (Filter.hasMem.{u1} (Filter.{u1} α)) s (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l)))) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) l)
but is expected to have type
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) (Set.interₛ.{u1} (Filter.{u1} α) (setOf.{u1} (Set.{u1} (Filter.{u1} α)) (fun (s : Set.{u1} (Filter.{u1} α)) => Membership.mem.{u1, u1} (Set.{u1} (Filter.{u1} α)) (Filter.{u1} (Filter.{u1} α)) (instMembershipSetFilter.{u1} (Filter.{u1} α)) s (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l)))) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) l)
Case conversion may be inaccurate. Consider using '#align filter.Inter_nhds Filter.interₛ_nhdsₓ'. -/
theorem interₛ_nhds (l : Filter α) : ⋂₀ { s | s ∈ 𝓝 l } = Iic l := by
  simp only [nhds_eq, sInter_lift'_sets monotone_principal.Iic, Iic, le_principal_iff, ←
    set_of_forall, ← Filter.le_def]
#align filter.Inter_nhds Filter.interₛ_nhds

/- warning: filter.nhds_mono -> Filter.nhds_mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l₁ : Filter.{u1} α} {l₂ : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} (Filter.{u1} α)) (Preorder.toLE.{u1} (Filter.{u1} (Filter.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.partialOrder.{u1} (Filter.{u1} α)))) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l₁) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l₂)) (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l₁ l₂)
but is expected to have type
  forall {α : Type.{u1}} {l₁ : Filter.{u1} α} {l₂ : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} (Filter.{u1} α)) (Preorder.toLE.{u1} (Filter.{u1} (Filter.{u1} α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instPartialOrderFilter.{u1} (Filter.{u1} α)))) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l₁) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l₂)) (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l₁ l₂)
Case conversion may be inaccurate. Consider using '#align filter.nhds_mono Filter.nhds_monoₓ'. -/
@[simp]
theorem nhds_mono {l₁ l₂ : Filter α} : 𝓝 l₁ ≤ 𝓝 l₂ ↔ l₁ ≤ l₂ :=
  by
  refine' ⟨fun h => _, fun h => monotone_nhds h⟩
  rw [← Iic_subset_Iic, ← Inter_nhds, ← Inter_nhds]
  exact sInter_subset_sInter h
#align filter.nhds_mono Filter.nhds_mono

/- warning: filter.mem_interior -> Filter.mem_interior is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)} {l : Filter.{u1} α}, Iff (Membership.Mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasMem.{u1} (Filter.{u1} α)) l (interior.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) s)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) => HasSubset.Subset.{u1} (Set.{u1} (Filter.{u1} α)) (Set.hasSubset.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α t)) s)))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)} {l : Filter.{u1} α}, Iff (Membership.mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instMembershipSet.{u1} (Filter.{u1} α)) l (interior.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) s)) (Exists.{succ u1} (Set.{u1} α) (fun (t : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l) (HasSubset.Subset.{u1} (Set.{u1} (Filter.{u1} α)) (Set.instHasSubsetSet.{u1} (Filter.{u1} α)) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α t)) s)))
Case conversion may be inaccurate. Consider using '#align filter.mem_interior Filter.mem_interiorₓ'. -/
protected theorem mem_interior {s : Set (Filter α)} {l : Filter α} :
    l ∈ interior s ↔ ∃ t ∈ l, Iic (𝓟 t) ⊆ s := by rw [mem_interior_iff_mem_nhds, mem_nhds_iff]
#align filter.mem_interior Filter.mem_interior

/- warning: filter.mem_closure -> Filter.mem_closure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)} {l : Filter.{u1} α}, Iff (Membership.Mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasMem.{u1} (Filter.{u1} α)) l (closure.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) s)) (forall (t : Set.{u1} α), (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l) -> (Exists.{succ u1} (Filter.{u1} α) (fun (l' : Filter.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasMem.{u1} (Filter.{u1} α)) l' s) (fun (H : Membership.Mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasMem.{u1} (Filter.{u1} α)) l' s) => Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t l'))))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} (Filter.{u1} α)} {l : Filter.{u1} α}, Iff (Membership.mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instMembershipSet.{u1} (Filter.{u1} α)) l (closure.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) s)) (forall (t : Set.{u1} α), (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l) -> (Exists.{succ u1} (Filter.{u1} α) (fun (l' : Filter.{u1} α) => And (Membership.mem.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instMembershipSet.{u1} (Filter.{u1} α)) l' s) (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t l'))))
Case conversion may be inaccurate. Consider using '#align filter.mem_closure Filter.mem_closureₓ'. -/
protected theorem mem_closure {s : Set (Filter α)} {l : Filter α} :
    l ∈ closure s ↔ ∀ t ∈ l, ∃ l' ∈ s, t ∈ l' := by
  simp only [closure_eq_compl_interior_compl, Filter.mem_interior, mem_compl_iff, not_exists,
    not_forall, Classical.not_not, exists_prop, not_and, and_comm', subset_def, mem_Iic,
    le_principal_iff]
#align filter.mem_closure Filter.mem_closure

/- warning: filter.closure_singleton -> Filter.closure_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) (closure.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Singleton.singleton.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.hasSingleton.{u1} (Filter.{u1} α)) l)) (Set.Ici.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) l)
but is expected to have type
  forall {α : Type.{u1}} (l : Filter.{u1} α), Eq.{succ u1} (Set.{u1} (Filter.{u1} α)) (closure.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Singleton.singleton.{u1, u1} (Filter.{u1} α) (Set.{u1} (Filter.{u1} α)) (Set.instSingletonSet.{u1} (Filter.{u1} α)) l)) (Set.Ici.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) l)
Case conversion may be inaccurate. Consider using '#align filter.closure_singleton Filter.closure_singletonₓ'. -/
@[simp]
protected theorem closure_singleton (l : Filter α) : closure {l} = Ici l :=
  by
  ext l'
  simp [Filter.mem_closure, Filter.le_def]
#align filter.closure_singleton Filter.closure_singleton

/- warning: filter.specializes_iff_le -> Filter.specializes_iff_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l₁ : Filter.{u1} α} {l₂ : Filter.{u1} α}, Iff (Specializes.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) l₁ l₂) (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l₁ l₂)
but is expected to have type
  forall {α : Type.{u1}} {l₁ : Filter.{u1} α} {l₂ : Filter.{u1} α}, Iff (Specializes.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) l₁ l₂) (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l₁ l₂)
Case conversion may be inaccurate. Consider using '#align filter.specializes_iff_le Filter.specializes_iff_leₓ'. -/
@[simp]
theorem specializes_iff_le {l₁ l₂ : Filter α} : l₁ ⤳ l₂ ↔ l₁ ≤ l₂ := by
  simp only [specializes_iff_closure_subset, Filter.closure_singleton, Ici_subset_Ici]
#align filter.specializes_iff_le Filter.specializes_iff_le

instance : T0Space (Filter α) :=
  ⟨fun x y h =>
    (specializes_iff_le.1 h.Specializes).antisymm (specializes_iff_le.1 h.symm.Specializes)⟩

/- warning: filter.nhds_at_top -> Filter.nhds_atTop is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α], Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Filter.atTop.{u1} α _inst_1)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} α)) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Filter.{u1} α)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.completeLattice.{u1} (Filter.{u1} α)))) α (fun (x : α) => Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α (Set.Ici.{u1} α _inst_1 x)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α], Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Filter.atTop.{u1} α _inst_1)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} α)) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Filter.{u1} α)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instCompleteLatticeFilter.{u1} (Filter.{u1} α)))) α (fun (x : α) => Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α (Set.Ici.{u1} α _inst_1 x)))))
Case conversion may be inaccurate. Consider using '#align filter.nhds_at_top Filter.nhds_atTopₓ'. -/
theorem nhds_atTop [Preorder α] : 𝓝 atTop = ⨅ x : α, 𝓟 (Iic (𝓟 (Ici x))) := by
  simp only [at_top, nhds_infᵢ, nhds_principal]
#align filter.nhds_at_top Filter.nhds_atTop

/- warning: filter.tendsto_nhds_at_top_iff -> Filter.tendsto_nhds_atTop_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] {l : Filter.{u1} α} {f : α -> (Filter.{u2} β)}, Iff (Filter.Tendsto.{u1, u2} α (Filter.{u2} β) f l (nhds.{u2} (Filter.{u2} β) (Filter.topologicalSpace.{u2} β) (Filter.atTop.{u2} β _inst_1))) (forall (y : β), Filter.Eventually.{u1} α (fun (a : α) => Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) (Set.Ici.{u2} β _inst_1 y) (f a)) l)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] {l : Filter.{u1} α} {f : α -> (Filter.{u2} β)}, Iff (Filter.Tendsto.{u1, u2} α (Filter.{u2} β) f l (nhds.{u2} (Filter.{u2} β) (Filter.instTopologicalSpaceFilter.{u2} β) (Filter.atTop.{u2} β _inst_1))) (forall (y : β), Filter.Eventually.{u1} α (fun (a : α) => Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) (Set.Ici.{u2} β _inst_1 y) (f a)) l)
Case conversion may be inaccurate. Consider using '#align filter.tendsto_nhds_at_top_iff Filter.tendsto_nhds_atTop_iffₓ'. -/
protected theorem tendsto_nhds_atTop_iff [Preorder β] {l : Filter α} {f : α → Filter β} :
    Tendsto f l (𝓝 atTop) ↔ ∀ y, ∀ᶠ a in l, Ici y ∈ f a := by
  simp only [nhds_at_top, tendsto_infi, tendsto_principal, mem_Iic, le_principal_iff]
#align filter.tendsto_nhds_at_top_iff Filter.tendsto_nhds_atTop_iff

/- warning: filter.nhds_at_bot -> Filter.nhds_atBot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α], Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.topologicalSpace.{u1} α) (Filter.atBot.{u1} α _inst_1)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} α)) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Filter.{u1} α)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.completeLattice.{u1} (Filter.{u1} α)))) α (fun (x : α) => Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α)) (Filter.principal.{u1} α (Set.Iic.{u1} α _inst_1 x)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α], Eq.{succ u1} (Filter.{u1} (Filter.{u1} α)) (nhds.{u1} (Filter.{u1} α) (Filter.instTopologicalSpaceFilter.{u1} α) (Filter.atBot.{u1} α _inst_1)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} α)) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Filter.{u1} α)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} α)) (Filter.instCompleteLatticeFilter.{u1} (Filter.{u1} α)))) α (fun (x : α) => Filter.principal.{u1} (Filter.{u1} α) (Set.Iic.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α)) (Filter.principal.{u1} α (Set.Iic.{u1} α _inst_1 x)))))
Case conversion may be inaccurate. Consider using '#align filter.nhds_at_bot Filter.nhds_atBotₓ'. -/
theorem nhds_atBot [Preorder α] : 𝓝 atBot = ⨅ x : α, 𝓟 (Iic (𝓟 (Iic x))) :=
  @nhds_atTop αᵒᵈ _
#align filter.nhds_at_bot Filter.nhds_atBot

/- warning: filter.tendsto_nhds_at_bot_iff -> Filter.tendsto_nhds_atBot_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] {l : Filter.{u1} α} {f : α -> (Filter.{u2} β)}, Iff (Filter.Tendsto.{u1, u2} α (Filter.{u2} β) f l (nhds.{u2} (Filter.{u2} β) (Filter.topologicalSpace.{u2} β) (Filter.atBot.{u2} β _inst_1))) (forall (y : β), Filter.Eventually.{u1} α (fun (a : α) => Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) (Set.Iic.{u2} β _inst_1 y) (f a)) l)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] {l : Filter.{u1} α} {f : α -> (Filter.{u2} β)}, Iff (Filter.Tendsto.{u1, u2} α (Filter.{u2} β) f l (nhds.{u2} (Filter.{u2} β) (Filter.instTopologicalSpaceFilter.{u2} β) (Filter.atBot.{u2} β _inst_1))) (forall (y : β), Filter.Eventually.{u1} α (fun (a : α) => Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) (Set.Iic.{u2} β _inst_1 y) (f a)) l)
Case conversion may be inaccurate. Consider using '#align filter.tendsto_nhds_at_bot_iff Filter.tendsto_nhds_atBot_iffₓ'. -/
protected theorem tendsto_nhds_atBot_iff [Preorder β] {l : Filter α} {f : α → Filter β} :
    Tendsto f l (𝓝 atBot) ↔ ∀ y, ∀ᶠ a in l, Iic y ∈ f a :=
  @Filter.tendsto_nhds_atTop_iff α βᵒᵈ _ _ _
#align filter.tendsto_nhds_at_bot_iff Filter.tendsto_nhds_atBot_iff

variable [TopologicalSpace X]

/- warning: filter.nhds_nhds -> Filter.nhds_nhds is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] (x : X), Eq.{succ u1} (Filter.{u1} (Filter.{u1} X)) (nhds.{u1} (Filter.{u1} X) (Filter.topologicalSpace.{u1} X) (nhds.{u1} X _inst_1 x)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.completeLattice.{u1} (Filter.{u1} X)))) (Set.{u1} X) (fun (s : Set.{u1} X) => infᵢ.{u1, 0} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.completeLattice.{u1} (Filter.{u1} X)))) (IsOpen.{u1} X _inst_1 s) (fun (hs : IsOpen.{u1} X _inst_1 s) => infᵢ.{u1, 0} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.completeLattice.{u1} (Filter.{u1} X)))) (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) (fun (hx : Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x s) => Filter.principal.{u1} (Filter.{u1} X) (Set.Iic.{u1} (Filter.{u1} X) (PartialOrder.toPreorder.{u1} (Filter.{u1} X) (Filter.partialOrder.{u1} X)) (Filter.principal.{u1} X s))))))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] (x : X), Eq.{succ u1} (Filter.{u1} (Filter.{u1} X)) (nhds.{u1} (Filter.{u1} X) (Filter.instTopologicalSpaceFilter.{u1} X) (nhds.{u1} X _inst_1 x)) (infᵢ.{u1, succ u1} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.instCompleteLatticeFilter.{u1} (Filter.{u1} X)))) (Set.{u1} X) (fun (s : Set.{u1} X) => infᵢ.{u1, 0} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.instCompleteLatticeFilter.{u1} (Filter.{u1} X)))) (IsOpen.{u1} X _inst_1 s) (fun (hs : IsOpen.{u1} X _inst_1 s) => infᵢ.{u1, 0} (Filter.{u1} (Filter.{u1} X)) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} (Filter.{u1} X)) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} (Filter.{u1} X)) (Filter.instCompleteLatticeFilter.{u1} (Filter.{u1} X)))) (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x s) (fun (hx : Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x s) => Filter.principal.{u1} (Filter.{u1} X) (Set.Iic.{u1} (Filter.{u1} X) (PartialOrder.toPreorder.{u1} (Filter.{u1} X) (Filter.instPartialOrderFilter.{u1} X)) (Filter.principal.{u1} X s))))))
Case conversion may be inaccurate. Consider using '#align filter.nhds_nhds Filter.nhds_nhdsₓ'. -/
theorem nhds_nhds (x : X) : 𝓝 (𝓝 x) = ⨅ (s : Set X) (hs : IsOpen s) (hx : x ∈ s), 𝓟 (Iic (𝓟 s)) :=
  by simp only [(nhds_basis_opens x).nhds.eq_binfᵢ, infᵢ_and, @infᵢ_comm _ (_ ∈ _)]
#align filter.nhds_nhds Filter.nhds_nhds

/- warning: filter.inducing_nhds -> Filter.inducing_nhds is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X], Inducing.{u1, u1} X (Filter.{u1} X) _inst_1 (Filter.topologicalSpace.{u1} X) (nhds.{u1} X _inst_1)
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X], Inducing.{u1, u1} X (Filter.{u1} X) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} X) (nhds.{u1} X _inst_1)
Case conversion may be inaccurate. Consider using '#align filter.inducing_nhds Filter.inducing_nhdsₓ'. -/
theorem inducing_nhds : Inducing (𝓝 : X → Filter X) :=
  inducing_iff_nhds.2 fun x =>
    (nhds_def' _).trans <| by
      simp (config := { contextual := true }) only [nhds_nhds, comap_infi, comap_principal,
        Iic_principal, preimage_set_of_eq, ← mem_interior_iff_mem_nhds, set_of_mem_eq,
        IsOpen.interior_eq]
#align filter.inducing_nhds Filter.inducing_nhds

/- warning: filter.continuous_nhds -> Filter.continuous_nhds is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X], Continuous.{u1, u1} X (Filter.{u1} X) _inst_1 (Filter.topologicalSpace.{u1} X) (nhds.{u1} X _inst_1)
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X], Continuous.{u1, u1} X (Filter.{u1} X) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} X) (nhds.{u1} X _inst_1)
Case conversion may be inaccurate. Consider using '#align filter.continuous_nhds Filter.continuous_nhdsₓ'. -/
@[continuity]
theorem continuous_nhds : Continuous (𝓝 : X → Filter X) :=
  inducing_nhds.Continuous
#align filter.continuous_nhds Filter.continuous_nhds

/- warning: filter.tendsto.nhds -> Filter.Tendsto.nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : α -> X} {l : Filter.{u1} α} {x : X}, (Filter.Tendsto.{u1, u2} α X f l (nhds.{u2} X _inst_1 x)) -> (Filter.Tendsto.{u1, u2} α (Filter.{u2} X) (Function.comp.{succ u1, succ u2, succ u2} α X (Filter.{u2} X) (nhds.{u2} X _inst_1) f) l (nhds.{u2} (Filter.{u2} X) (Filter.topologicalSpace.{u2} X) (nhds.{u2} X _inst_1 x)))
but is expected to have type
  forall {α : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : α -> X} {l : Filter.{u2} α} {x : X}, (Filter.Tendsto.{u2, u1} α X f l (nhds.{u1} X _inst_1 x)) -> (Filter.Tendsto.{u2, u1} α (Filter.{u1} X) (Function.comp.{succ u2, succ u1, succ u1} α X (Filter.{u1} X) (nhds.{u1} X _inst_1) f) l (nhds.{u1} (Filter.{u1} X) (Filter.instTopologicalSpaceFilter.{u1} X) (nhds.{u1} X _inst_1 x)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.nhds Filter.Tendsto.nhdsₓ'. -/
protected theorem Tendsto.nhds {f : α → X} {l : Filter α} {x : X} (h : Tendsto f l (𝓝 x)) :
    Tendsto (𝓝 ∘ f) l (𝓝 (𝓝 x)) :=
  (continuous_nhds.Tendsto _).comp h
#align filter.tendsto.nhds Filter.Tendsto.nhds

end Filter

variable [TopologicalSpace X] [TopologicalSpace Y] {f : X → Y} {x : X} {s : Set X}

/- warning: continuous_within_at.nhds -> ContinuousWithinAt.nhds 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} {x : X} {s : Set.{u1} X}, (ContinuousWithinAt.{u1, u2} X Y _inst_1 _inst_2 f s x) -> (ContinuousWithinAt.{u1, u2} X (Filter.{u2} Y) _inst_1 (Filter.topologicalSpace.{u2} Y) (Function.comp.{succ u1, succ u2, succ u2} X Y (Filter.{u2} Y) (nhds.{u2} Y _inst_2) f) s x)
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} {x : X} {s : Set.{u2} X}, (ContinuousWithinAt.{u2, u1} X Y _inst_1 _inst_2 f s x) -> (ContinuousWithinAt.{u2, u1} X (Filter.{u1} Y) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} Y) (Function.comp.{succ u2, succ u1, succ u1} X Y (Filter.{u1} Y) (nhds.{u1} Y _inst_2) f) s x)
Case conversion may be inaccurate. Consider using '#align continuous_within_at.nhds ContinuousWithinAt.nhdsₓ'. -/
theorem ContinuousWithinAt.nhds (h : ContinuousWithinAt f s x) : ContinuousWithinAt (𝓝 ∘ f) s x :=
  h.nhds
#align continuous_within_at.nhds ContinuousWithinAt.nhds

/- warning: continuous_at.nhds -> ContinuousAt.nhds 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} {x : X}, (ContinuousAt.{u1, u2} X Y _inst_1 _inst_2 f x) -> (ContinuousAt.{u1, u2} X (Filter.{u2} Y) _inst_1 (Filter.topologicalSpace.{u2} Y) (Function.comp.{succ u1, succ u2, succ u2} X Y (Filter.{u2} Y) (nhds.{u2} Y _inst_2) f) x)
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} {x : X}, (ContinuousAt.{u2, u1} X Y _inst_1 _inst_2 f x) -> (ContinuousAt.{u2, u1} X (Filter.{u1} Y) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} Y) (Function.comp.{succ u2, succ u1, succ u1} X Y (Filter.{u1} Y) (nhds.{u1} Y _inst_2) f) x)
Case conversion may be inaccurate. Consider using '#align continuous_at.nhds ContinuousAt.nhdsₓ'. -/
theorem ContinuousAt.nhds (h : ContinuousAt f x) : ContinuousAt (𝓝 ∘ f) x :=
  h.nhds
#align continuous_at.nhds ContinuousAt.nhds

/- warning: continuous_on.nhds -> ContinuousOn.nhds 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} {s : Set.{u1} X}, (ContinuousOn.{u1, u2} X Y _inst_1 _inst_2 f s) -> (ContinuousOn.{u1, u2} X (Filter.{u2} Y) _inst_1 (Filter.topologicalSpace.{u2} Y) (Function.comp.{succ u1, succ u2, succ u2} X Y (Filter.{u2} Y) (nhds.{u2} Y _inst_2) f) s)
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} {s : Set.{u2} X}, (ContinuousOn.{u2, u1} X Y _inst_1 _inst_2 f s) -> (ContinuousOn.{u2, u1} X (Filter.{u1} Y) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} Y) (Function.comp.{succ u2, succ u1, succ u1} X Y (Filter.{u1} Y) (nhds.{u1} Y _inst_2) f) s)
Case conversion may be inaccurate. Consider using '#align continuous_on.nhds ContinuousOn.nhdsₓ'. -/
theorem ContinuousOn.nhds (h : ContinuousOn f s) : ContinuousOn (𝓝 ∘ f) s := fun x hx =>
  (h x hx).nhds
#align continuous_on.nhds ContinuousOn.nhds

/- warning: continuous.nhds -> Continuous.nhds 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) -> (Continuous.{u1, u2} X (Filter.{u2} Y) _inst_1 (Filter.topologicalSpace.{u2} Y) (Function.comp.{succ u1, succ u2, succ u2} X Y (Filter.{u2} Y) (nhds.{u2} Y _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) -> (Continuous.{u2, u1} X (Filter.{u1} Y) _inst_1 (Filter.instTopologicalSpaceFilter.{u1} Y) (Function.comp.{succ u2, succ u1, succ u1} X Y (Filter.{u1} Y) (nhds.{u1} Y _inst_2) f))
Case conversion may be inaccurate. Consider using '#align continuous.nhds Continuous.nhdsₓ'. -/
theorem Continuous.nhds (h : Continuous f) : Continuous (𝓝 ∘ f) :=
  Filter.continuous_nhds.comp h
#align continuous.nhds Continuous.nhds


variable U: Type
variables P Q: U -> Prop

example (P1 : exists x, P x \/ Q x)
        (P2 : forall x, P x -> Q x)
        : exists x, Q x  :=

obtain (z : U)(H1 : P z \/ Q z), from P1,
have H2: P z -> Q z, from P2 z,
have Hq: Q z, from or.elim H1
    (assume H : P z,
        show Q z, from H2 H)
    (assume H : Q z,
        show Q z, from H),
exists.intro z Hq.


import data.set.basic

variable α: Type*
variables A B: set α

open set

-- requires classical logic
theorem Q4a: compl (A ∩ B) = (compl A) ∪ (compl B) :=
ext $ λ x,
  ⟨λ hi, classical.by_cases
    (λ ha, or.inr (λ hb, hi ⟨ha, hb⟩))
    (λ hn, or.inl hn),
   λ hu, or.elim hu
    (λ hac ⟨ha, hb⟩, hac ha)
    (λ hbc ⟨ha, hb⟩, hbc hb)⟩

theorem Q4b: compl (A ∪ B) = (compl A) ∩ (compl B) :=
ext $ λ x,
  ⟨λ hu,
    ⟨λ ha, hu (or.inl ha),
     λ hb, hu (or.inr hb)⟩,
   λ ⟨hac, hbc⟩ hu, or.elim hu
    (λ ha, hac ha)
    (λ hb, hbc hb)⟩


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

import ring_theory.polynomial.cyclotomic.basic
import tactic.by_contra
import topology.algebra.polynomial
import number_theory.padics.padic_norm

/-!
# Evaluating cyclotomic polynomials
This file states some results about evaluating cyclotomic polynomials in various different ways.
## Main definitions
* `polynomial.eval(₂)_one_cyclotomic_prime(_pow)`: `eval 1 (cyclotomic p^k R) = p`.
* `polynomial.eval_one_cyclotomic_not_prime_pow`: Otherwise, `eval 1 (cyclotomic n R) = 1`.
* `polynomial.cyclotomic_pos` : `∀ x, 0 < eval x (cyclotomic n R)` if `2 < n`.
-/

namespace polynomial

open finset nat
open_locale big_operators

@[simp] lemma eval_one_cyclotomic_prime {R : Type*} [comm_ring R] {p : ℕ} [hn : fact p.prime] :
  eval 1 (cyclotomic p R) = p :=
by simp only [cyclotomic_eq_geom_sum hn.out, geom_sum_def, eval_X, one_pow, sum_const, eval_pow,
              eval_finset_sum, card_range, smul_one_eq_coe]

@[simp] lemma eval₂_one_cyclotomic_prime {R S : Type*} [comm_ring R] [semiring S] (f : R →+* S)
  {p : ℕ} [fact p.prime] : eval₂ f 1 (cyclotomic p R) = p :=
by simp

@[simp] lemma eval_one_cyclotomic_prime_pow {R : Type*} [comm_ring R] {p : ℕ} (k : ℕ)
  [hn : fact p.prime] : eval 1 (cyclotomic (p ^ (k + 1)) R) = p :=
by simp only [cyclotomic_prime_pow_eq_geom_sum hn.out, geom_sum_def, eval_X, one_pow, sum_const,
              eval_pow, eval_finset_sum, card_range, smul_one_eq_coe]

@[simp] lemma eval₂_one_cyclotomic_prime_pow {R S : Type*} [comm_ring R] [semiring S] (f : R →+* S)
  {p : ℕ} (k : ℕ) [fact p.prime] : eval₂ f 1 (cyclotomic (p ^ (k + 1)) R) = p :=
by simp

private lemma cyclotomic_neg_one_pos {n : ℕ} (hn : 2 < n) {R} [linear_ordered_comm_ring R] :
  0 < eval (-1 : R) (cyclotomic n R) :=
begin
  haveI := ne_zero.of_gt hn,
  rw [←map_cyclotomic_int, ←int.cast_one, ←int.cast_neg, eval_int_cast_map,
      int.coe_cast_ring_hom, int.cast_pos],
  suffices : 0 < eval ↑(-1 : ℤ) (cyclotomic n ℝ),
  { rw [←map_cyclotomic_int n ℝ, eval_int_cast_map, int.coe_cast_ring_hom] at this,
    exact_mod_cast this },
  simp only [int.cast_one, int.cast_neg],
  have h0 := cyclotomic_coeff_zero ℝ hn.le,
  rw coeff_zero_eq_eval_zero at h0,
  by_contra' hx,
  have := intermediate_value_univ (-1) 0 (cyclotomic n ℝ).continuous,
  obtain ⟨y, hy : is_root _ y⟩ := this (show (0 : ℝ) ∈ set.Icc _ _, by simpa [h0] using hx),
  rw is_root_cyclotomic_iff at hy,
  rw hy.eq_order_of at hn,
  exact hn.not_le linear_ordered_ring.order_of_le_two,
end

lemma cyclotomic_pos {n : ℕ} (hn : 2 < n) {R} [linear_ordered_comm_ring R] (x : R) :
  0 < eval x (cyclotomic n R) :=
begin
  induction n using nat.strong_induction_on with n ih,
  have hn'  : 0 < n := pos_of_gt hn,
  have hn'' : 1 < n := one_lt_two.trans hn,
  dsimp at ih,
  have := prod_cyclotomic_eq_geom_sum hn' R,
  apply_fun eval x at this,
  rw [divisors_eq_proper_divisors_insert_self_of_pos hn', insert_sdiff_of_not_mem,
      prod_insert, eval_mul, eval_geom_sum] at this,
  rotate,
  { simp only [lt_self_iff_false, mem_sdiff, not_false_iff, mem_proper_divisors, and_false,
      false_and]},
  { simpa only [mem_singleton] using hn''.ne' },
  rcases lt_trichotomy 0 (geom_sum x n) with h | h | h,
  { apply pos_of_mul_pos_right,
    { rwa this },
    rw eval_prod,
    refine prod_nonneg (λ i hi, _),
    simp only [mem_sdiff, mem_proper_divisors, mem_singleton] at hi,
    rw geom_sum_pos_iff hn'' at h,
    cases h with hk hx,
    { refine (ih _ hi.1.2 (nat.two_lt_of_ne _ hi.2 _)).le; rintro rfl,
      { exact hn'.ne' (zero_dvd_iff.mp hi.1.1) },
      { exact even_iff_not_odd.mp (even_iff_two_dvd.mpr hi.1.1) hk } },
    { rcases eq_or_ne i 2 with rfl | hk,
      { simpa only [eval_X, eval_one, cyclotomic_two, eval_add] using hx.le },
      refine (ih _ hi.1.2 (nat.two_lt_of_ne _ hi.2 hk)).le,
      rintro rfl,
      exact (hn'.ne' $ zero_dvd_iff.mp hi.1.1) } },
  { rw [eq_comm, geom_sum_eq_zero_iff_neg_one hn''] at h,
    exact h.1.symm ▸ cyclotomic_neg_one_pos hn },
  { apply pos_of_mul_neg_left,
    { rwa this },
    rw [geom_sum_neg_iff hn''] at h,
    have h2 : {2} ⊆ n.proper_divisors \ {1},
    { rw [singleton_subset_iff, mem_sdiff, mem_proper_divisors, not_mem_singleton],
      exact ⟨⟨h.1, hn⟩, (nat.one_lt_bit0 one_ne_zero).ne'⟩ },
    rw [eval_prod, ←prod_sdiff h2, prod_singleton]; try { apply_instance },
    apply mul_nonpos_of_nonneg_of_nonpos,
    { refine prod_nonneg (λ i hi, le_of_lt _),
      simp only [mem_sdiff, mem_proper_divisors, mem_singleton] at hi,
      refine ih _ hi.1.1.2 (nat.two_lt_of_ne _ hi.1.2 hi.2),
      rintro rfl,
      rw zero_dvd_iff at hi,
      exact hn'.ne' hi.1.1.1 },
    { simpa only [eval_X, eval_one, cyclotomic_two, eval_add] using h.right.le } }
end

lemma eval_one_cyclotomic_not_prime_pow {R : Type*} [comm_ring R] {n : ℕ}
  (h : ∀ {p : ℕ}, p.prime → ∀ k : ℕ, p ^ k ≠ n) : eval 1 (cyclotomic n R) = 1 :=
begin
  rcases n.eq_zero_or_pos with rfl | hn',
  { simp },
  have hn   : 2 < n := two_lt_of_ne hn'.ne' (h nat.prime_two 0).symm (h nat.prime_two 1).symm,
  have hn'' : 1 < n := by linarith,
  suffices : eval 1 (cyclotomic n ℤ) = 1 ∨ eval 1 (cyclotomic n ℤ) = -1,
  { cases this with h h,
    { have := eval_int_cast_map (int.cast_ring_hom R) (cyclotomic n ℤ) 1,
      simpa only [map_cyclotomic, int.cast_one, h, ring_hom.eq_int_cast] using this },
    { exfalso,
      linarith [cyclotomic_pos hn (1 : ℤ)] }, },
  rw [←int.nat_abs_eq_nat_abs_iff, int.nat_abs_one, nat.eq_one_iff_not_exists_prime_dvd],
  intros p hp hpe,
  haveI := fact.mk hp,

  have hpn : p ∣ n,
  { apply hpe.trans,
    nth_rewrite 1 ←int.nat_abs_of_nat n,
    rw [int.nat_abs_dvd_iff_dvd, ←int.nat_cast_eq_coe_nat,
        ←one_geom_sum, ←eval_geom_sum, ←prod_cyclotomic_eq_geom_sum hn'],
    apply eval_dvd,
    apply finset.dvd_prod_of_mem,
    simpa using and.intro hn'.ne' hn''.ne' },

  have := prod_cyclotomic_eq_geom_sum hn' ℤ,
  apply_fun eval 1 at this,
  rw [eval_geom_sum, one_geom_sum, eval_prod, eq_comm,
      ←finset.prod_sdiff $ range_pow_padic_val_nat_subset_divisors' p, finset.prod_image] at this,
  simp_rw [eval_one_cyclotomic_prime_pow, finset.prod_const, finset.card_range, mul_comm] at this,
  rw [←finset.prod_sdiff $ show {n} ⊆ _, from _] at this,
  any_goals {apply_instance},
  swap,
  { simp only [not_exists, true_and, exists_prop, dvd_rfl, finset.mem_image, finset.mem_range,
    finset.mem_singleton, finset.singleton_subset_iff, finset.mem_sdiff, nat.mem_divisors, not_and],
    exact ⟨⟨hn'.ne', hn''.ne'⟩, λ t _, h hp _⟩ },
  rw [←int.nat_abs_of_nat p, int.nat_abs_dvd_iff_dvd] at hpe,
  obtain ⟨t, ht⟩ := hpe,
  rw [finset.prod_singleton, ht, mul_left_comm, mul_comm, ←mul_assoc, mul_assoc] at this,
  simp only [int.nat_cast_eq_coe_nat] at *,
  have : (p ^ (padic_val_nat p n) * p : ℤ) ∣ n := ⟨_, this⟩,
  simp only [←pow_succ', ←int.nat_abs_dvd_iff_dvd, int.nat_abs_of_nat, int.nat_abs_pow] at this,
  exact pow_succ_padic_val_nat_not_dvd hn' this,
  { rintro x - y - hxy,
    apply nat.succ_injective,
    exact nat.pow_right_injective hp.two_le hxy }
end

end polynomial


/- 3.3 Implicit Arguments
  * (x : α) : an explicit argument of type α
  * {x : α} : an implicit argument, eagerly inserted
  * ⦃x : α⦄ or {{x : α}} : an implicit argument, weakly inserted
  * [x : α] : implicit argument that should be inferred by type class resolution
  * (x : α := t) : an optional argument, with default value t
  * (x : α . t) : an implicit argument, to be synthesized by tactic t
-/
namespace args
universe u

def ex1 (x y z : ℕ) : ℕ := x + y + z
#check ex1 1 2 3 -- ex1 1 2 3 : ℕ

def id1 (α : Type u) (x : α) : α := x

#check id1 nat 3 -- id1 ℕ 3 : ℕ
#check id1 _ 3   -- id1 ℕ 3 : ℕ

-- id with implicit arguments
def id2 {α : Type u} (x : α) : α := x

#check id2 3     -- id2 3 : ℕ
#check @id2 ℕ 3 -- id2 3 : ℕ
#check (id2 : ℕ → ℕ) -- OK

def id3 {{α : Type u}} (x : α) : α := x

#check id3 3     -- id2 3 : ℕ
#check @id3 ℕ 3 -- id2 3 : ℕ
-- #check (id3 : ℕ → ℕ) -- invalid type ascription, term has type
--   Π ⦃α : Type ?⦄, α → α : Type (?+1)
-- but is expected to have type
--   ℕ → ℕ : Type
#check (id3 : Π α : Type, α → α) -- OK

class cls := (val : ℕ)
instance cls.five : cls := ⟨5⟩

#check cls.five -- cls_five : cls
#reduce cls.five -- {val := 5}

def ex2 [c : cls] : ℕ := c.val
#reduce ex2 -- 5

def ex2a [c : cls] : ℕ := ex2
#reduce ex2a -- 5

def ex3 (x : ℕ := 5) := x
#check ex3 2 -- ex3 2 : opt_param ℕ 5
#check ex3 -- ex3 : opt_param ℕ 5
#reduce ex3 -- 5
example : ex3 = 5 := rfl

meta def ex_tac : tactic unit := tactic.refine ``(5)

def ex4 (x : ℕ . ex_tac) := x
#reduce ex4 -- 5

end args
/- 3.5 Constructors, Projections, and Matching -/
namespace constructors

universes u v

variables {α : Type u} {β : Type v}
def p : ℕ × ℤ := ⟨1, 2⟩
#check p.fst -- ℕ
#check p.snd -- ℤ

def p' : ℕ × ℤ × bool := ⟨1, 2, tt⟩
#check p'.fst -- ℕ
#check p'.snd.fst -- ℤ
#check p'.snd.snd -- bool

def swap_pair (p : α × β) : β × α :=
⟨p.snd, p.fst⟩

theorem swap_conj {a b : Prop} (h : a ∧ b) : b ∧ a :=
⟨h.right, h.left⟩

#reduce [1, 2, 3].append [2, 3, 4] -- [1, 2, 3, 2, 3, 4] : list ℕ
#reduce [1, 2, 3].map (λ x, x^2) -- [1, 4, 9] : list ℕ

example (p q : Prop) : p ∧ q → q ∧ p :=
λ h, ⟨h.right, h.left⟩

def swap_pair' (p : α × β) : β × α :=
let (x, y) := p in (y, x)

theorem swap_conj' {a b : Prop} (h : a ∧ b) : b ∧ a :=
let ⟨ha, hb⟩ := h in ⟨hb, ha⟩

def swap_pair'' : α × β → β × α :=
λ ⟨x, y⟩, (y, x)

theorem swap_conj'' {a b : Prop} : a ∧ b → b ∧ a :=
assume ⟨ha, hb⟩, ⟨hb, ha⟩

end constructors


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_numbertheory_326
  (n : ℕ)
  (h₀ : (↑n - 1) * ↑n * (↑n + 1) = (720:ℤ)) :
  (n + 1) = 10 :=
begin
  cases n,
  norm_num at h₀,
  norm_num [mul_add] at h₀,
  norm_num [nat.succ_eq_add_one] at *,
  nlinarith,
end


-- #7

import data.int.basic

#check sub_self
#check mul_comm
#check eq.symm
#check eq.trans

example (x : ℤ) : x * 0 = 0 :=
have h : x - x = 0 := sub_self x,
-- have h1 : 0 = x - x := eq.symm h,
have h2 : x * 0 = x * 0 := eq.refl (x * 0),
have h3 : x * 0 = x * (x - x), by rw h,
have h4 : x * (x - x) = x * x - x * x := mul_sub x x x,
have h5 : x * x - x * x = 0 := sub_self (x * x),
have h6 : x * 0 = x * x - x * x := eq.trans h3 h4,
show x * 0 = 0,
from eq.trans h6 h5

-- write the above proof using calc
example (x : ℤ) : x * 0 = 0 :=
calc
  x * 0 =  x * (x - x)  : by rw (sub_self x)
  ...   = x * x - x * x : mul_sub x x x
  ...   = 0             : sub_self (x * x)


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.zero
import Mathlib.PostPort

universes v u l

namespace Mathlib

/-!
# Shift

A `shift` on a category is nothing more than an automorphism of the category. An example to
keep in mind might be the category of complexes ⋯ → C_{n-1} → C_n → C_{n+1} → ⋯ with the shift
operator re-indexing the terms, so the degree `n` term of `shift C` would be the degree `n+1`
term of `C`.

-/

namespace category_theory

/-- A category has a shift, or translation, if it is equipped with an automorphism. -/
class has_shift (C : Type u) [category C]
where
  shift : C ≌ C

/-- The shift autoequivalence, moving objects and morphisms 'up'. -/
def shift (C : Type u) [category C] [has_shift C] : C ≌ C :=
  has_shift.shift

-- Any better notational suggestions?

@[simp] theorem shift_zero_eq_zero (C : Type u) [category C] [has_shift C] [limits.has_zero_morphisms C] (X : C) (Y : C) (n : ℤ) : functor.map (equivalence.functor (shift C ^ n)) 0 = 0 :=
  limits.equivalence_preserves_zero_morphisms C (shift C ^ n) X Y


import Mathlib.analysis.normed_space.basic
import Mathlib.topology.local_homeomorph

namespace Mathlib

open filter
open set

namespace asymptotics

-- TODO: Type* syntax
variable {α : Type} {β : Type} {E : Type} {F : Type} {G : Type}
  {E' : Type} {F' : Type} {G' : Type} {R : Type} {R' : Type}

variable [has_norm E] [has_norm F] [has_norm G] [normed_group E'] [normed_group F']
  [normed_group G'] [normed_ring R] [normed_ring R']
  {c c' : ℝ} {f : α → E} {g : α → F} {k : α → G} {f' : α → E'} {g' : α → F'} {k' : α → G'}
  {l l' : filter α}

section defs

def is_O_with (c : ℝ) (f : α → E) (g : α → F) (l : filter α) : Prop :=
  filter.eventually (fun (x : α) => norm (f x) ≤ c * norm (g x)) l

theorem is_O_with_iff {c : real} {f : α → E} {g : α → F} {l : filter α} :
    is_O_with c f g l ↔ filter.eventually (fun (x : α) => norm (f x) ≤ c * norm (g x)) l :=
  iff.rfl

theorem is_O_with.of_bound {c : real} {f : α → E} {g : α → F} {l : filter α} :
    filter.eventually (λ (x : α) => norm (f x) ≤ c * norm (g x)) l → is_O_with c f g l :=
  λ (h : filter.eventually (fun (x : α) => norm (f x) ≤ c * norm (g x)) l) => h

def is_O (f : α → E) (g : α → F) (l : filter α) : Prop :=
  ∃ c : ℝ, is_O_with c f g l

theorem is_O_iff_is_O_with {f : α → E} {g : α → F} {l : filter α} :
    is_O f g l ↔ ∃ c : ℝ, is_O_with c f g l :=
  iff.rfl

theorem is_O_iff {f : α → E} {g : α → F} {l : filter α} :
    is_O f g l ↔ ∃ c : ℝ, filter.eventually (λ x => norm (f x) ≤ c * norm (g x)) l :=
  iff.rfl

theorem is_O.of_bound (c : ℝ) {f : α → E} {g : α → F} {l : filter α}
    (h : filter.eventually (λ x => norm (f x) ≤ c * norm (g x)) l) : is_O f g l :=
  ⟨c, h⟩

def is_o (f : α → E) (g : α → F) (l : filter α) : Prop :=
  ∀ (c : ℝ), 0 < c → is_O_with c f g l

theorem is_o_iff_forall_is_O_with {f : α → E} {g : α → F} {l : filter α} :
    is_o f g l ↔ ∀ (c : ℝ), 0 < c → is_O_with c f g l :=
  iff.rfl

theorem is_o_iff {f : α → E} {g : α → F} {l : filter α} :
    is_o f g l ↔ ∀ (c : ℝ), 0 < c → filter.eventually (λ x => norm (f x) ≤ c * norm (g x)) l
  := iff.rfl

theorem is_o.def {f : α → E} {g : α → F} {l : filter α} (h : is_o f g l) {c : ℝ} (hc : 0 < c) :
    filter.eventually (λ x => norm (f x) ≤ c * norm (g x)) l :=
  h _ hc

theorem is_o.def' {f : α → E} {g : α → F} {l : filter α} (h : is_o f g l) {c : ℝ} (hc : 0 < c) :
    is_O_with c f g l :=
  h _ hc

end defs

theorem is_O_with.is_O (h : is_O_with c f g l) : is_O f g l := ⟨c, h⟩

theorem is_o.is_O_with (hgf : is_o f g l) : is_O_with 1 f g l := hgf _ zero_lt_one

theorem is_o.is_O (hgf : is_o f g l) : is_O f g l := hgf.is_O_with.is_O

end asymptotics
end Mathlib


def new_list_sizeof : has_sizeof (list ℕ) := ⟨list.length⟩
local attribute [instance, priority 100000] new_list_sizeof

-- Creating definitions for the mergesort
def fhalf {α: Type} (xs: list α): list α := list.take (list.length xs/2) xs
def sndhalf {α: Type} (xs: list α): list α := list.drop (list.length xs/2) xs

#reduce fhalf [1,2,3,4,5,6]     --Expecting [1, 2, 3]
#reduce sndhalf [1,2,3,4,5,6]   --Expecting [4, 5, 6]

-- Definition of the merge (merging two lists with the smallest first)
def merge : list ℕ → list ℕ → list ℕ
   | xs [] := xs
   | [] ys := ys
   | (x :: xs) (y :: ys) := if x < y then x :: merge xs (y :: ys) else y :: merge (x :: xs) ys

-- Standard definitions for proving with merge later
def reverse {α : Type} : list α → list α
| []        := []
| (x :: xs) := reverse xs ++ [x]

-- Testing the merge
#reduce merge [8, 2, 1] [2,1,4]  --Expecting: [2, 1, 4, 8, 2, 1]
#reduce merge [2,3,4] [2,3,7]    --Expecting: [2, 2, 3, 3, 4, 7]

#reduce reverse(reverse(merge [2,3,7] [2,3,4]))

lemma test : 1 < 1 + 1 := by exact dec_trivial

def mergesort : list ℕ → list ℕ
| [] := []
| [a] := [a]
| (x::xs) :=
  have list.sizeof (fhalf xs) < x + (1 + list.sizeof xs), from begin induction x, simp, simp[fhalf], cases xs, simp, simp[list.sizeof], simp[test],simp[list.sizeof], exact dec_trivial end,
  have list.sizeof (sndhalf xs) < x + (1 + list.sizeof xs), from sorry,
  merge (mergesort (fhalf xs)) (mergesort (sndhalf xs))

-- #reduce mergesort [10,2,1,2,3]

-- Proving the merge definition instead of the mergesort as it includes the merge defenition.

-- Proving the reverse definitions which will also partially be applied to the merge definition
lemma reverse_append {α : Type} :
  ∀xs ys : list α, reverse (xs ++ ys) = reverse ys ++ reverse xs
| [] ys := by simp[reverse]
| (x :: xs) ys := begin simp[reverse, reverse_append] end

lemma reverse_append_nil : ∀{α:Type} (xs:list α) (x:α), reverse (x::xs) = reverse xs ++ ( x :: list.nil)
| xs [] := by simp[reverse]
| xs (x :: xss) := begin intro xsss, simp[reverse] end

lemma reverse_reverse {α : Type} : ∀xs : list α, reverse (reverse xs) = xs
| [] := by simp[reverse]
| (x :: xs) := begin simp[reverse], simp[reverse_append], rw[reverse_reverse], rw[reverse], rw[reverse], refl  end

-- Proving merge properties
lemma merge_nil: ∀(n : list ℕ), merge ([] : list ℕ) n  = n
| [] := by refl
| (x :: xs) := by simp[merge]

lemma nil_merge: ∀(n : list ℕ), merge n ([] : list ℕ)  = n
| [] := by refl
| (x :: xs) := by simp[merge]

lemma merge_reverse_reverse : ∀(n m: list ℕ), reverse(reverse(merge n m)) = merge n m
| [] m := by simp[reverse_reverse]
| (x :: xs) m := by simp[reverse_reverse]

-- Seems obvious, but can't seem to prove the ite..
lemma merge_cons: ∀(xs xss : list ℕ), ∀(x: ℕ),  merge xs (x::xss) = merge (x::xss) xs
| [] xss := by simp[merge]
| (x :: xs) [] := begin intro x2, simp[merge], simp[merge_cons], sorry end
| (x :: xs) (xx :: xss) := begin  intro x2, simp[merge], sorry end

lemma comm_merge : ∀(n m: list ℕ), merge m n = merge n m
| [] xs :=  by simp[nil_merge, merge_nil]
| (x :: xs) xss := by simp[merge_cons]


import quantum_lemmas
open Matrix
open quantum
open_locale big_operators

------------------------------------------------------------------------------
-- Generalized measurement

section generalized_measurement

variables {n : ℕ}
variables (M : fin n → Square n) -- a set of measurement operators
variables (s : Vector n) -- state

/-- `Pr(m)` - the probability that we find measurement result `m`.
In other words, measuring the operator `M m` in the state `s`. -/
noncomputable
def gen_measure (m : fin n) : ℝ := ((s† ⬝ ((M m)† ⬝ (M m)) ⬝ s) 0 0).re

/-- The state after general measurement. -/
noncomputable
def state_after_gen_measure (m : fin n) := (1 / real.sqrt (gen_measure M s m)) • M m ⬝ s

end generalized_measurement

section generalized_measurement_lemmas

variables {n : ℕ}
variables {M : fin n → Square n} -- a set of measurement operators
variables {s : Vector n} -- state
variables {m : fin n} -- the index of the measurement operator in the set `M`

lemma gen_measure_eq_inner_product : s† ⬝ ((M m)† ⬝ (M m)) ⬝ s = ((M m) ⬝ s)† ⬝ ((M m) ⬝ s)
:= begin
    rw adjoint_mul,
    iterate 3 { rw matrix.mul_assoc },
end

-- `Pr(m) = ∥M m ⬝ s∥^2`
lemma gen_measure_eq_norm_sq_mul : gen_measure M s m = ∥(M m ⬝ s)∥^2
:= begin
    unfold gen_measure,
    rw gen_measure_eq_inner_product,
    apply Matrix_inner_self_eq_norm_sq,
end

-- `√(Pr(m)) = ∥M m ⬝ s∥`
lemma sqrt_gen_measure_eq_norm_mul : real.sqrt (gen_measure M s m) = ∥(M m ⬝ s)∥
:= begin
    rw gen_measure_eq_norm_sq_mul,
    rw pow_two,
    apply real.sqrt_mul_self,
    apply matrix.norm_nonneg,
end

-- `Pr(m) = Tr(M m ⬝ ρ ⬝ (M m)†)`, where `ρ` is `proj s`.
example (m : fin n) : gen_measure M s m = Tr( M m ⬝ proj s ⬝ (M m)†).re
:= begin
    unfold gen_measure proj,
    congr' 1,
    rw gen_measure_eq_inner_product,
    rw <- trace_outer_eq_inner,
    rw adjoint_mul,
    repeat { rw matrix.mul_assoc },
end

-- `Pr(m) = Tr((M m)† ⬝ M m ⬝ ρ)`, where `ρ` is `proj s`.
example {m : fin n} : gen_measure M s m = Tr( (M m)† ⬝ M m ⬝ proj s).re
:= begin
    unfold gen_measure proj,
    congr' 1,
    rw gen_measure_eq_inner_product,
    rw <- trace_outer_eq_inner,
    rw adjoint_mul,

    have l: M m ⬝ s ⬝ (s† ⬝ M m†) = M m ⬝ (s ⬝ s†) ⬝ (M m)†,
    { repeat { rw matrix.mul_assoc }, },
    rw l, clear l,
    rw trace_mul_rotate_l (M m),
end

lemma state_after_gen_measure_unit {s : Vector n} {m : fin n}
        : gen_measure M s m ≠ 0 → (state_after_gen_measure M s m).unit
:= begin
    intros h,
    rw norm_eq_one_iff_unit,
    unfold state_after_gen_measure,
    rw sqrt_gen_measure_eq_norm_mul, simp,
    rw gen_measure_eq_norm_sq_mul at h,
    have : ∥M m ⬝ s∥ ≠ 0,
    {
        intro c, apply h, clear h,
        rw c, simp,
    },
    exact norm_smul_norm_inv_eq_one this,
end

end generalized_measurement_lemmas

------------------------------------------------------------------------------
-- Projective measurement

section projective_measurement

variables {n : ℕ}
variables (u : fin n → Vector n) -- a set of basis vectors to project
variables (s : Vector n) -- state

/-- `Pr(i)` - the probability that we find measurement result `i` in state `s`.
In other words, measuring the projection of basis `u i` in state `s`. -/
noncomputable
def proj_measure (i : fin n) : ℝ := ((s† ⬝ (proj (u i)) ⬝ s) 0 0).re

/-- The state after projective measurement -/
noncomputable
def state_after_proj_measure (i : fin n)
    := (1 / real.sqrt (proj_measure u s i)) • proj (u i) ⬝ s

end projective_measurement

section projective_measurement_lemmas

variables {n : ℕ}
variables {u : fin n → Vector n} -- a set of basis vectors to project
variables {s : Vector n} -- state

/-- The measurement operator computed from the basis vector `u i` -/
@[reducible] noncomputable
def proj_to_measure (u : fin n → Vector n) (i : fin n) := proj (u i)

/-- All projection vectors in the set `u` are unit. -/
@[reducible]
def proj_unit (u : fin n → Vector n) := ∀ i, (u i).unit

lemma proj_measure_eq_gen_measure_proj
        : proj_unit u
        → proj_measure u = gen_measure (proj_to_measure u)
:= begin
    intros h, ext s m,
    unfold proj_measure gen_measure proj_to_measure,
    rw proj_mul_adjoint_eq_self (h m),
end

variables {i : fin n} -- the index of the projection vector in the set `u`

lemma proj_measure_eq_inner_product : s† ⬝ (proj (u i)) ⬝ s = ((u i)† ⬝ s)† ⬝ ((u i)† ⬝ s)
:= begin
    unfold proj,
    rw adjoint_mul, simp,
    iterate 3 { rw matrix.mul_assoc },
end

-- `Pr(i) = ∥(P i) ⬝ s∥^2`, where `P i` is `proj (u i)`.
-- Inherited property from gen_measure.
lemma proj_measure_eq_norm_sq_mul : proj_unit u → proj_measure u s i = ∥(proj (u i) ⬝ s)∥^2
:= begin
    intro up,
    rw proj_measure_eq_gen_measure_proj up,
    apply gen_measure_eq_norm_sq_mul,
end

-- The Born rule
lemma proj_measure_eq_norm_sq_inner : proj_unit u → proj_measure u s i = ∥((u i)† ⬝ s)∥^2
:= begin
    intro up,
    rw proj_measure_eq_norm_sq_mul up,

    iterate 2 { rw <- Matrix_inner_self_eq_norm_sq },
    unfold proj,
    congr' 1,
    unfold inner inner_product,
    iterate 3 { rw adjoint_mul }, simp,
    have : s† ⬝ (u i ⬝ u i†) ⬝ (u i ⬝ u i† ⬝ s) = s† ⬝ u i ⬝ (u i† ⬝ u i) ⬝ (u i† ⬝ s),
    {
        repeat { rw matrix.mul_assoc },
    },
    rw this, clear this,
    rw unfold_unit (up i), simp,
end

-- Inherited property from gen_measure.
lemma state_after_proj_measure_unit
        : proj_unit u → proj_measure u s i ≠ 0 → (state_after_proj_measure u s i).unit
:= begin
    intros up,
    unfold state_after_proj_measure,
    rw proj_measure_eq_gen_measure_proj up,
    intros h,
    apply state_after_gen_measure_unit h,
end

lemma proj_self_square_of_proj_unit : proj_unit u → (u i).proj ⬝ (u i).proj = (u i).proj
:= begin
    intros up,
    unfold proj,
    have : u i ⬝ u i† ⬝ (u i ⬝ u i†) = u i ⬝ (u i† ⬝ u i) ⬝ u i†,
    { repeat { rw matrix.mul_assoc }, },
    rw this, clear this,
    rw unfold_unit (up i), simp,
end

-- Applying the same (projective) measurement operator has no effect.
lemma proj_measure_state_after_proj_measure
    : proj_unit u
    → (u i).proj ⬝ state_after_proj_measure u s i = state_after_proj_measure u s i
:= begin
    intros up,
    unfold state_after_proj_measure,
    rw Matrix.mul_real_smul,
    rw <- matrix.mul_assoc,
    rw proj_self_square_of_proj_unit up,
end

-- The probability of measuring again gives us the same result is 1.
lemma proj_measure_state_after_proj_measure_unit : proj_unit u → proj_measure u s i ≠ 0
        → proj_measure u (state_after_proj_measure u s i) i = 1
:= begin
    intros up h,
    rw proj_measure_eq_norm_sq_mul up,
    rw proj_measure_state_after_proj_measure up,
    have : (state_after_proj_measure u s i).unit,
    { apply state_after_proj_measure_unit; assumption },
    rw <- Matrix_inner_self_eq_norm_sq,
    unfold inner inner_product,
    rw unfold_unit this, simp,
end

end projective_measurement_lemmas

------------------------------------------------------------------------------
-- Standard basis measurement as a projective measurement

section projective_measurement_in_std_basis

variables {n : ℕ} {s : Vector n}

@[simp]
lemma proj_unit_std_basis : @proj_unit n std_basis
:= begin
    unfold proj_unit, intros i, simp,
end

lemma measure_eq_proj_measure
        : ⟦s⟧ = proj_measure std_basis s
:= begin
    ext m,
    unfold quantum.measure,
    rw proj_measure_eq_norm_sq_inner,
    {
        rw <- Matrix_inner_self_eq_norm_sq,
        unfold inner inner_product,
        rw matrix_mul_square_one, simp,
    },
    {
        simp,
    }
end

lemma state_after_measure_eq_state_after_proj_measure
        : state_after_measure s = state_after_proj_measure std_basis s
:= begin
    apply funext, intros m,
    unfold state_after_proj_measure state_after_measure,
    congr' 2,
    rw measure_eq_proj_measure,
end

end projective_measurement_in_std_basis

------------------------------------------------------------------------------
-- Simulating projective measurement by measurement in the standard basis

section proj_measure_to_measure

variables {n : ℕ} {s : Vector n}
variables {u : fin n → Vector n} -- a set of basis to project
variables {m : fin n} -- the index of the projection vector in the set `u`

/-- A unitary operator that converts an arbitrary projective measurement to
the standard basis measuremnt. -/
noncomputable
def proj_measure_op (u : fin n → Vector n) : Square n := λ i, (u i)† 0

lemma proj_measure_to_measure : proj_unit u
        → proj_measure u s = ⟦proj_measure_op u ⬝ s⟧
:= begin
    intros h,
    ext m,
    rw measure_eq_proj_measure,
    rw proj_measure_eq_norm_sq_inner h,
    rw proj_measure_eq_norm_sq_inner proj_unit_std_basis,
    congr' 2,
    rw <- matrix.mul_assoc,
    congr' 1,

    apply matrix.ext, intros i j,
    have : i = 0, by simp, cases this, clear this,
    simp,
    rw matrix.mul_apply,
    unfold proj_measure_op,
    rw finset.sum_eq_single m; try {solve1 {simp}},
    {
        intros i ip ie,
        rw std_basis_adjoint_apply,
        rw std_basis_eq_zero ie, simp,
    },
end

/-- The unitary operator that restores the state after projective measurement,
after simulating it with `proj_measure_op`. -/
noncomputable
def proj_measure_restore_op (u : fin n → Vector n) (m : fin n) := u m ⬝ (std_basis m)†

lemma state_after_proj_measure_to_measure
        : proj_unit u
        → state_after_proj_measure u s m
            = proj_measure_restore_op u m ⬝ state_after_measure (proj_measure_op u ⬝ s) m
:= begin
    intros up,
    unfold state_after_proj_measure state_after_measure,
    rw proj_measure_to_measure up,
    rw Matrix.mul_real_smul,
    congr' 1,
    unfold proj_measure_op proj_measure_restore_op,
    rw <- matrix.mul_assoc,
    unfold proj,
    have : u m ⬝ std_basis m† ⬝ (std_basis m ⬝ std_basis m†)
         = u m ⬝ std_basis m†,
    {
        rw matrix.mul_assoc,
        rw <- matrix.mul_assoc ((std_basis m)†),
        simp,
    },
    rw this, clear this,
    rw matrix.mul_assoc,
    rw matrix.mul_assoc,
    congr' 1,
    rw <- matrix.mul_assoc,
    congr' 1,
    apply matrix.ext, intros i j,
    have : i = 0, by simp, cases this, clear this, simp,
    rw matrix.mul_apply,
    rw finset.sum_eq_single m; try { solve1 {simp} },
    {
        intros k k_in k_neq,
        simp,
        rw std_basis_eq_zero k_neq,
        left, refl,
    },
end

end proj_measure_to_measure


theorem Ex012(a b : Prop): a → (a ∨ b) :=
  assume H1:a,
  show a ∨ b, from or.inl H1


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne
-/
import measure_theory.constructions.borel_space
import order.filter.ennreal

/-!
# Essential supremum and infimum
We define the essential supremum and infimum of a function `f : α → β` with respect to a measure
`μ` on `α`. The essential supremum is the infimum of the constants `c : β` such that `f x ≤ c`
almost everywhere.

TODO: The essential supremum of functions `α → ℝ≥0∞` is used in particular to define the norm in
the `L∞` space (see measure_theory/lp_space.lean).

There is a different quantity which is sometimes also called essential supremum: the least
upper-bound among measurable functions of a family of measurable functions (in an almost-everywhere
sense). We do not define that quantity here, which is simply the supremum of a map with values in
`α →ₘ[μ] β` (see measure_theory/ae_eq_fun.lean).

## Main definitions

* `ess_sup f μ := μ.ae.limsup f`
* `ess_inf f μ := μ.ae.liminf f`
-/

open measure_theory filter topological_space
open_locale ennreal measure_theory

variables {α β : Type*} {m : measurable_space α} {μ ν : measure α}

section conditionally_complete_lattice
variable [conditionally_complete_lattice β]

/-- Essential supremum of `f` with respect to measure `μ`: the smallest `c : β` such that
`f x ≤ c` a.e. -/
def ess_sup {m : measurable_space α} (f : α → β) (μ : measure α) := μ.ae.limsup f

/-- Essential infimum of `f` with respect to measure `μ`: the greatest `c : β` such that
`c ≤ f x` a.e. -/
def ess_inf {m : measurable_space α} (f : α → β) (μ : measure α) := μ.ae.liminf f

lemma ess_sup_congr_ae {f g : α → β} (hfg : f =ᵐ[μ] g) : ess_sup f μ = ess_sup g μ :=
limsup_congr hfg

lemma ess_inf_congr_ae {f g : α → β} (hfg : f =ᵐ[μ] g) :  ess_inf f μ = ess_inf g μ :=
@ess_sup_congr_ae α (order_dual β) _ _ _ _ _ hfg

end conditionally_complete_lattice

section conditionally_complete_linear_order
variable [conditionally_complete_linear_order β]

lemma ess_sup_eq_Inf {m : measurable_space α} (μ : measure α) (f : α → β) :
  ess_sup f μ = Inf {a | μ {x | a < f x} = 0} :=
begin
  dsimp [ess_sup, limsup, Limsup],
  congr,
  ext a,
  simp [eventually_map, ae_iff],
end

end conditionally_complete_linear_order

section complete_lattice
variable [complete_lattice β]

@[simp] lemma ess_sup_measure_zero {m : measurable_space α} {f : α → β} :
  ess_sup f (0 : measure α) = ⊥ :=
le_bot_iff.mp (Inf_le (by simp [set.mem_set_of_eq, eventually_le, ae_iff]))

@[simp] lemma ess_inf_measure_zero {m : measurable_space α} {f : α → β} :
  ess_inf f (0 : measure α) = ⊤ :=
@ess_sup_measure_zero α (order_dual β) _ _ _

lemma ess_sup_mono_ae {f g : α → β} (hfg : f ≤ᵐ[μ] g) : ess_sup f μ ≤ ess_sup g μ :=
limsup_le_limsup hfg

lemma ess_inf_mono_ae {f g : α → β} (hfg : f ≤ᵐ[μ] g) : ess_inf f μ ≤ ess_inf g μ :=
liminf_le_liminf hfg

lemma ess_sup_const (c : β) (hμ : μ ≠ 0) : ess_sup (λ x : α, c) μ = c :=
begin
  haveI hμ_ne_bot : μ.ae.ne_bot, { rwa [ne_bot_iff, ne.def, ae_eq_bot] },
  exact limsup_const c,
end

lemma ess_sup_le_of_ae_le {f : α → β} (c : β) (hf : f ≤ᵐ[μ] (λ _, c)) : ess_sup f μ ≤ c :=
begin
  refine (ess_sup_mono_ae hf).trans _,
  by_cases hμ : μ = 0,
  { simp [hμ], },
  { rwa ess_sup_const, },
end

lemma ess_inf_const (c : β) (hμ : μ ≠ 0) : ess_inf (λ x : α, c) μ = c :=
@ess_sup_const α (order_dual β) _ _ _ _ hμ

lemma le_ess_inf_of_ae_le {f : α → β} (c : β) (hf : (λ _, c) ≤ᵐ[μ] f) : c ≤ ess_inf f μ :=
@ess_sup_le_of_ae_le α (order_dual β) _ _ _ _ c hf

lemma ess_sup_const_bot : ess_sup (λ x : α, (⊥ : β)) μ = (⊥ : β) :=
limsup_const_bot

lemma ess_inf_const_top : ess_inf (λ x : α, (⊤ : β)) μ = (⊤ : β) :=
liminf_const_top

lemma order_iso.ess_sup_apply {m : measurable_space α} {γ} [complete_lattice γ]
  (f : α → β) (μ : measure α) (g : β ≃o γ) :
  g (ess_sup f μ) = ess_sup (λ x, g (f x)) μ :=
begin
  refine order_iso.limsup_apply g _ _ _ _,
  all_goals { is_bounded_default, },
end

lemma order_iso.ess_inf_apply {m : measurable_space α} {γ} [complete_lattice γ]
  (f : α → β) (μ : measure α) (g : β ≃o γ) :
  g (ess_inf f μ) = ess_inf (λ x, g (f x)) μ :=
@order_iso.ess_sup_apply α (order_dual β) _ _  (order_dual γ) _ _ _ g.dual

lemma ess_sup_mono_measure {f : α → β} (hμν : ν ≪ μ) : ess_sup f ν ≤ ess_sup f μ :=
begin
  refine limsup_le_limsup_of_le (measure.ae_le_iff_absolutely_continuous.mpr hμν) _ _,
  all_goals { is_bounded_default, },
end

lemma ess_sup_mono_measure' {α : Type*} {β : Type*} {m : measurable_space α}
  {μ ν : measure_theory.measure α} [complete_lattice β] {f : α → β} (hμν : ν ≤ μ) :
  ess_sup f ν ≤ ess_sup f μ := ess_sup_mono_measure (measure.absolutely_continuous_of_le hμν)

lemma ess_inf_antitone_measure {f : α → β} (hμν : μ ≪ ν) : ess_inf f ν ≤ ess_inf f μ :=
begin
  refine liminf_le_liminf_of_le (measure.ae_le_iff_absolutely_continuous.mpr hμν) _ _,
  all_goals { is_bounded_default, },
end

lemma ess_sup_smul_measure {f : α → β} {c : ℝ≥0∞} (hc : c ≠ 0) :
  ess_sup f (c • μ) = ess_sup f μ :=
begin
  simp_rw ess_sup,
  suffices h_smul : (c • μ).ae = μ.ae, by rw h_smul,
  ext1,
  simp_rw mem_ae_iff,
  simp [hc],
end

section topological_space

variables {γ : Type*} {mγ : measurable_space γ} {f : α → γ} {g : γ → β}

include mγ

lemma ess_sup_comp_le_ess_sup_map_measure (hf : measurable f) :
  ess_sup (g ∘ f) μ ≤ ess_sup g (measure.map f μ) :=
begin
  refine Limsup_le_Limsup_of_le (λ t, _) (by is_bounded_default) (by is_bounded_default),
  simp_rw filter.mem_map,
  have : (g ∘ f) ⁻¹' t = f ⁻¹' (g ⁻¹' t), by { ext1 x, simp_rw set.mem_preimage, },
  rw this,
  exact λ h, mem_ae_of_mem_ae_map hf h,
end

lemma _root_.measurable_embedding.ess_sup_map_measure (hf : measurable_embedding f) :
  ess_sup g (measure.map f μ) = ess_sup (g ∘ f) μ :=
begin
  refine le_antisymm _ (ess_sup_comp_le_ess_sup_map_measure hf.measurable),
  refine Limsup_le_Limsup (by is_bounded_default) (by is_bounded_default) (λ c h_le, _),
  rw eventually_map at h_le ⊢,
  exact hf.ae_map_iff.mpr h_le,
end

variables [measurable_space β] [topological_space β] [second_countable_topology β]
  [order_closed_topology β] [opens_measurable_space β]

lemma ess_sup_map_measure_of_measurable (hg : measurable g) (hf : measurable f) :
  ess_sup g (measure.map f μ) = ess_sup (g ∘ f) μ :=
begin
  refine le_antisymm _ (ess_sup_comp_le_ess_sup_map_measure hf),
  refine Limsup_le_Limsup (by is_bounded_default) (by is_bounded_default) (λ c h_le, _),
  rw eventually_map at h_le ⊢,
  rw ae_map_iff hf (measurable_set_le hg measurable_const),
  exact h_le,
end

lemma ess_sup_map_measure (hg : ae_measurable g (measure.map f μ)) (hf : measurable f) :
  ess_sup g (measure.map f μ) = ess_sup (g ∘ f) μ :=
begin
  rw [ess_sup_congr_ae hg.ae_eq_mk, ess_sup_map_measure_of_measurable hg.measurable_mk hf],
  refine ess_sup_congr_ae _,
  have h_eq := ae_of_ae_map hf hg.ae_eq_mk,
  rw ← eventually_eq at h_eq,
  exact h_eq.symm,
end

omit mγ

end topological_space

end complete_lattice

section complete_linear_order
variable [complete_linear_order β]

lemma ae_lt_of_ess_sup_lt {f : α → β} {x : β} (hf : ess_sup f μ < x) : ∀ᵐ y ∂μ, f y < x :=
filter.eventually_lt_of_limsup_lt hf

lemma ae_lt_of_lt_ess_inf {f : α → β} {x : β} (hf : x < ess_inf f μ) : ∀ᵐ y ∂μ, x < f y :=
@ae_lt_of_ess_sup_lt α (order_dual β) _ _ _ _ _ hf

lemma ess_sup_indicator_eq_ess_sup_restrict [has_zero β] {s : set α}
  {f : α → β} (hf : 0 ≤ᵐ[μ.restrict s] f) (hs : measurable_set s) (hs_not_null : μ s ≠ 0) :
  ess_sup (s.indicator f) μ = ess_sup f (μ.restrict s) :=
begin
  refine le_antisymm _ (Limsup_le_Limsup_of_le (map_restrict_ae_le_map_indicator_ae hs)
    (by is_bounded_default) (by is_bounded_default)),
  refine Limsup_le_Limsup (by is_bounded_default) (by is_bounded_default) (λ c h_restrict_le, _),
  rw eventually_map at h_restrict_le ⊢,
  rw ae_restrict_iff' hs at h_restrict_le,
  have hc : 0 ≤ c,
  { suffices : ∃ x, 0 ≤ f x ∧ f x ≤ c, by { obtain ⟨x, hx⟩ := this, exact hx.1.trans hx.2, },
    refine frequently.exists _,
    { exact μ.ae, },
    rw [eventually_le, ae_restrict_iff' hs] at hf,
    have hs' : ∃ᵐ x ∂μ, x ∈ s,
    { contrapose! hs_not_null,
      rw [not_frequently, ae_iff] at hs_not_null,
      suffices : {a : α | ¬a ∉ s} = s, by rwa ← this,
      simp, },
    refine hs'.mp (hf.mp (h_restrict_le.mono (λ x hxs_imp_c hxf_nonneg hxs, _))),
    rw pi.zero_apply at hxf_nonneg,
    exact ⟨hxf_nonneg hxs, hxs_imp_c hxs⟩, },
  refine h_restrict_le.mono (λ x hxc, _),
  by_cases hxs : x ∈ s,
  { simpa [hxs] using hxc hxs, },
  { simpa [hxs] using hc, },
end

end complete_linear_order

namespace ennreal

variables {f : α → ℝ≥0∞}

lemma ae_le_ess_sup (f : α → ℝ≥0∞) : ∀ᵐ y ∂μ, f y ≤ ess_sup f μ :=
eventually_le_limsup f

@[simp] lemma ess_sup_eq_zero_iff : ess_sup f μ = 0 ↔ f =ᵐ[μ] 0 :=
limsup_eq_zero_iff

lemma ess_sup_const_mul {a : ℝ≥0∞} : ess_sup (λ (x : α), a * (f x)) μ = a * ess_sup f μ :=
limsup_const_mul

lemma ess_sup_add_le (f g : α → ℝ≥0∞) : ess_sup (f + g) μ ≤ ess_sup f μ + ess_sup g μ :=
limsup_add_le f g

lemma ess_sup_liminf_le {ι} [encodable ι] [linear_order ι] (f : ι → α → ℝ≥0∞) :
  ess_sup (λ x, at_top.liminf (λ n, f n x)) μ ≤ at_top.liminf (λ n, ess_sup (λ x, f n x) μ) :=
by { simp_rw ess_sup, exact ennreal.limsup_liminf_le_liminf_limsup (λ a b, f b a), }

end ennreal


import data.real.basic

open tactic
set_option trace.simplify.rewrite true

constants a b c d : ℝ
example : (a * b) * c = b * (a * c) :=
begin
  -- simp only [mul_comm _ _, mul_assoc _ _ a], -- simp give infinite loop
  simp only [mul_comm _ b, mul_assoc _ _ _],
  -- rewrite mul_comm a b,
  -- rewrite mul_assoc b a c,
end

example (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  -- rw hyp' at hyp,
  -- rw hyp,
  -- rw mul_comm d _,
  -- rw ← two_mul _,
  -- rw ← mul_assoc 2 a d,
  -- simp [hyp'] at hyp,

  -- simp only [*, mul_comm, two_mul, mul_assoc] at *, -- fails
  -- 1 goal
  -- hyp' : b = a * d,
  -- hyp : c = a * d + a * d
  -- ⊢ a * d + a * d = d * (a + a)
  simp only [*, mul_comm, two_mul, mul_assoc, ← add_mul] at *, -- goals solved
-- [mul_comm]: d * a ==> a * d
-- [hyp']: b ==> a * d
-- [add_mul]: a * d + a * d ==> (a + a) * d
-- [mul_comm]: (a + a) * d ==> d * (a + a)
-- [[← add_mul]]: c ==> d * (a + a)
-- [two_mul]: 2 * a ==> a + a
-- [mul_comm]: (a + a) * d ==> d * (a + a)
end

example (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 :=
begin
  rw hyp' at hyp,
  rw hyp,
  simp only [sub_self, mul_comm],
end

example (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  calc c = d*a + b : by { rw hyp } -- ⊢ c = d * a + b
  ... = d*a + a*d  : by { rw hyp' } -- ⊢ c = 2 * a * b
  ... = a*d + a*d  : by { rw mul_comm d a } -- ⊢ d * a + a * d = a * d + a * d
  ... = 2*(a*d)    : by { simp [two_mul], } --
  ... = 2*a*d      : by { rw mul_assoc,},
end

example (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  calc c = d*a + b   : by rw hyp
     ... = d*a + a*d : by rw hyp'
     ... = 2*a*d     : by ring
end

example : a * (b * c) = b * (a * c) :=
begin
  ring,
end

lemma helper1 {x} : x < 1 → x = 0 := sorry

lemma l1 (x y) : x + y < 1 → x = 0 ∧ y = 0 :=
begin
  assume h₁,
  have h₆ : x + y = 0 := helper1 h₁,
  by_cases (x = 0),
  split, show_term { assumption, },
  rw h at h₁,
  show_term { simp at h₁,},

  -- show_term { solve_by_elim [helper1], },
  exact helper1 h₁,
  have h₂ : ¬x = 0 → x > 0 := sorry,
  have h₃ := h₂ h, clear h h₂,

  refine ⟨_, _⟩, -- ↔ split
  have h₄ : ∀ x, x < 0 → false := sorry,
  -- solve_by_elim [helper1] {discharger := cc},
  have h₇ : ∀ {n m} (k : ℕ), n > m → n + k > m + k := sorry,
  have h₈ := h₇ y h₃,

  -- show_term { finish [h₄, h₈], }, -- goal solved
  rw h₆ at h₈,
  show_term { solve_by_elim [h₄, h₈], }, -- OK
  -- exact false.elim (h₄ y h₈),

  -- apply h₄,
  /-
  x + y = 0, y = 0 → x = 0
    (action: change goal to ⊢ y = 0)
  x + y = 0
  x > 0 => x = 1 ∨ x > 1
  ⊢ x = 0

  h₆ : ∀ (n m k), n > m → n + k > m + k
  x > 0 [h₆]=> x + y > y [x+y=0] => y < 0 => false

  n + m = k => n

  let x = 1 => false
  let x > 1 => x + y = 0
  -/
  sorry,
end

example : (a + b) * (a - b) = a^2 - b^2 := by ring