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

! This file was ported from Lean 3 source module data.analysis.topology
! leanprover-community/mathlib commit 55d771df074d0dd020139ee1cd4b95521422df9f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Analysis.Filter
import Mathbin.Topology.Bases
import Mathbin.Topology.LocallyFinite

/-!
# Computational realization of topological spaces (experimental)

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

This file provides infrastructure to compute with topological spaces.

## Main declarations

* `ctop`: Realization of a topology basis.
* `ctop.realizer`: Realization of a topological space. `ctop` that generates the given topology.
* `locally_finite.realizer`: Realization of the local finiteness of an indexed family of sets.
* `compact.realizer`: Realization of the compactness of a set.
-/

open Set

open Filter hiding Realizer

open Topology

#print Ctop /-
/-- A `ctop α σ` is a realization of a topology (basis) on `α`,
  represented by a type `σ` together with operations for the top element and
  the intersection operation. -/
structure Ctop (α σ : Type _) where
  f : σ → Set α
  top : α → σ
  top_mem : ∀ x : α, x ∈ f (top x)
  inter : ∀ (a b) (x : α), x ∈ f a ∩ f b → σ
  inter_mem : ∀ a b x h, x ∈ f (inter a b x h)
  inter_sub : ∀ a b x h, f (inter a b x h) ⊆ f a ∩ f b
#align ctop Ctop
-/

variable {α : Type _} {β : Type _} {σ : Type _} {τ : Type _}

instance : Inhabited (Ctop α (Set α)) :=
  ⟨{  f := id
      top := singleton
      top_mem := mem_singleton
      inter := fun s t _ _ => s ∩ t
      inter_mem := fun s t a => id
      inter_sub := fun s t a ha => Subset.rfl }⟩

namespace Ctop

section

variable (F : Ctop α σ)

instance : CoeFun (Ctop α σ) fun _ => σ → Set α :=
  ⟨Ctop.f⟩

/- warning: ctop.coe_mk -> Ctop.coe_mk is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {σ : Type.{u2}} (f : σ -> (Set.{u1} α)) (T : α -> σ) (h₁ : forall (x : α), Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (f (T x))) (I : forall (a : σ) (b : σ) (x : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (f a) (f b))) -> σ) (h₂ : forall (a : σ) (b : σ) (x : α) (h : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (f a) (f b))), Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (f (I a b x h))) (h₃ : forall (a : σ) (b : σ) (x : α) (h : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (f a) (f b))), HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (f (I a b x h)) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (f a) (f b))) (a : σ), Eq.{succ u1} (Set.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) (Ctop.mk.{u1, u2} α σ f T h₁ I h₂ h₃) a) (f a)
but is expected to have type
  forall {α : Type.{u2}} {σ : Type.{u1}} (f : σ -> (Set.{u2} α)) (T : α -> σ) (h₁ : forall (x : α), Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (f (T x))) (I : forall (a : σ) (b : σ) (x : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Inter.inter.{u2} (Set.{u2} α) (Set.instInterSet.{u2} α) (f a) (f b))) -> σ) (h₂ : forall (a : σ) (b : σ) (x : α) (h : Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Inter.inter.{u2} (Set.{u2} α) (Set.instInterSet.{u2} α) (f a) (f b))), Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (f (I a b x h))) (h₃ : forall (a : σ) (b : σ) (x : α) (h : Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Inter.inter.{u2} (Set.{u2} α) (Set.instInterSet.{u2} α) (f a) (f b))), HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (f (I a b x h)) (Inter.inter.{u2} (Set.{u2} α) (Set.instInterSet.{u2} α) (f a) (f b))) (a : σ), Eq.{succ u2} (Set.{u2} α) (Ctop.f.{u2, u1} α σ (Ctop.mk.{u2, u1} α σ f T h₁ I h₂ h₃) a) (f a)
Case conversion may be inaccurate. Consider using '#align ctop.coe_mk Ctop.coe_mkₓ'. -/
@[simp]
theorem coe_mk (f T h₁ I h₂ h₃ a) : (@Ctop.mk α σ f T h₁ I h₂ h₃) a = f a :=
  rfl
#align ctop.coe_mk Ctop.coe_mk

#print Ctop.ofEquiv /-
/-- Map a ctop to an equivalent representation type. -/
def ofEquiv (E : σ ≃ τ) : Ctop α σ → Ctop α τ
  | ⟨f, T, h₁, I, h₂, h₃⟩ =>
    { f := fun a => f (E.symm a)
      top := fun x => E (T x)
      top_mem := fun x => by simpa using h₁ x
      inter := fun a b x h => E (I (E.symm a) (E.symm b) x h)
      inter_mem := fun a b x h => by simpa using h₂ (E.symm a) (E.symm b) x h
      inter_sub := fun a b x h => by simpa using h₃ (E.symm a) (E.symm b) x h }
#align ctop.of_equiv Ctop.ofEquiv
-/

/- warning: ctop.of_equiv_val -> Ctop.ofEquiv_val is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {σ : Type.{u2}} {τ : Type.{u3}} (E : Equiv.{succ u2, succ u3} σ τ) (F : Ctop.{u1, u2} α σ) (a : τ), Eq.{succ u1} (Set.{u1} α) (coeFn.{max (succ u1) (succ u3), max (succ u3) (succ u1)} (Ctop.{u1, u3} α τ) (fun (_x : Ctop.{u1, u3} α τ) => τ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u3} α τ) (Ctop.ofEquiv.{u1, u2, u3} α σ τ E F) a) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F (coeFn.{max 1 (max (succ u3) (succ u2)) (succ u2) (succ u3), max (succ u3) (succ u2)} (Equiv.{succ u3, succ u2} τ σ) (fun (_x : Equiv.{succ u3, succ u2} τ σ) => τ -> σ) (Equiv.hasCoeToFun.{succ u3, succ u2} τ σ) (Equiv.symm.{succ u2, succ u3} σ τ E) a))
but is expected to have type
  forall {α : Type.{u1}} {σ : Type.{u3}} {τ : Type.{u2}} (E : Equiv.{succ u3, succ u2} σ τ) (F : Ctop.{u1, u3} α σ) (a : τ), Eq.{succ u1} (Set.{u1} α) (Ctop.f.{u1, u2} α τ (Ctop.ofEquiv.{u1, u3, u2} α σ τ E F) a) (Ctop.f.{u1, u3} α σ F (FunLike.coe.{max (succ u3) (succ u2), succ u2, succ u3} (Equiv.{succ u2, succ u3} τ σ) τ (fun (_x : τ) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : τ) => σ) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u3} τ σ) (Equiv.symm.{succ u3, succ u2} σ τ E) a))
Case conversion may be inaccurate. Consider using '#align ctop.of_equiv_val Ctop.ofEquiv_valₓ'. -/
@[simp]
theorem ofEquiv_val (E : σ ≃ τ) (F : Ctop α σ) (a : τ) : F.of_equiv E a = F (E.symm a) := by
  cases F <;> rfl
#align ctop.of_equiv_val Ctop.ofEquiv_val

end

#print Ctop.toTopsp /-
/-- Every `ctop` is a topological space. -/
def toTopsp (F : Ctop α σ) : TopologicalSpace α :=
  TopologicalSpace.generateFrom (Set.range F.f)
#align ctop.to_topsp Ctop.toTopsp
-/

/- warning: ctop.to_topsp_is_topological_basis -> Ctop.toTopsp_isTopologicalBasis is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {σ : Type.{u2}} (F : Ctop.{u1, u2} α σ), TopologicalSpace.IsTopologicalBasis.{u1} α (Ctop.toTopsp.{u1, u2} α σ F) (Set.range.{u1, succ u2} (Set.{u1} α) σ (Ctop.f.{u1, u2} α σ F))
but is expected to have type
  forall {α : Type.{u2}} {σ : Type.{u1}} (F : Ctop.{u2, u1} α σ), TopologicalSpace.IsTopologicalBasis.{u2} α (Ctop.toTopsp.{u2, u1} α σ F) (Set.range.{u2, succ u1} (Set.{u2} α) σ (Ctop.f.{u2, u1} α σ F))
Case conversion may be inaccurate. Consider using '#align ctop.to_topsp_is_topological_basis Ctop.toTopsp_isTopologicalBasisₓ'. -/
theorem toTopsp_isTopologicalBasis (F : Ctop α σ) :
    @TopologicalSpace.IsTopologicalBasis _ F.toTopsp (Set.range F.f) :=
  letI := F.to_topsp
  ⟨fun u ⟨a, e₁⟩ v ⟨b, e₂⟩ =>
    e₁ ▸ e₂ ▸ fun x h => ⟨_, ⟨_, rfl⟩, F.inter_mem a b x h, F.inter_sub a b x h⟩,
    eq_univ_iff_forall.2 fun x => ⟨_, ⟨_, rfl⟩, F.top_mem x⟩, rfl⟩
#align ctop.to_topsp_is_topological_basis Ctop.toTopsp_isTopologicalBasis

/- warning: ctop.mem_nhds_to_topsp -> Ctop.mem_nhds_toTopsp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {σ : Type.{u2}} (F : Ctop.{u1, u2} α σ) {s : Set.{u1} α} {a : α}, Iff (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α (Ctop.toTopsp.{u1, u2} α σ F) a)) (Exists.{succ u2} σ (fun (b : σ) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b) s)))
but is expected to have type
  forall {α : Type.{u2}} {σ : Type.{u1}} (F : Ctop.{u2, u1} α σ) {s : Set.{u2} α} {a : α}, Iff (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α (Ctop.toTopsp.{u2, u1} α σ F) a)) (Exists.{succ u1} σ (fun (b : σ) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α σ F b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α σ F b) s)))
Case conversion may be inaccurate. Consider using '#align ctop.mem_nhds_to_topsp Ctop.mem_nhds_toTopspₓ'. -/
@[simp]
theorem mem_nhds_toTopsp (F : Ctop α σ) {s : Set α} {a : α} :
    s ∈ @nhds _ F.toTopsp a ↔ ∃ b, a ∈ F b ∧ F b ⊆ s :=
  (@TopologicalSpace.IsTopologicalBasis.mem_nhds_iff _ F.toTopsp _ _ _
        F.toTopsp_isTopologicalBasis).trans <|
    ⟨fun ⟨_, ⟨x, rfl⟩, h⟩ => ⟨x, h⟩, fun ⟨x, h⟩ => ⟨_, ⟨x, rfl⟩, h⟩⟩
#align ctop.mem_nhds_to_topsp Ctop.mem_nhds_toTopsp

end Ctop

#print Ctop.Realizer /-
/-- A `ctop` realizer for the topological space `T` is a `ctop`
  which generates `T`. -/
structure Ctop.Realizer (α) [T : TopologicalSpace α] where
  σ : Type _
  f : Ctop α σ
  Eq : F.toTopsp = T
#align ctop.realizer Ctop.Realizer
-/

open Ctop

#print Ctop.toRealizer /-
/-- A `ctop` realizes the topological space it generates. -/
protected def Ctop.toRealizer (F : Ctop α σ) : @Ctop.Realizer _ F.toTopsp :=
  @Ctop.Realizer.mk _ F.toTopsp σ F rfl
#align ctop.to_realizer Ctop.toRealizer
-/

instance (F : Ctop α σ) : Inhabited (@Ctop.Realizer _ F.toTopsp) :=
  ⟨F.toRealizer⟩

namespace Ctop.Realizer

/- warning: ctop.realizer.is_basis -> Ctop.Realizer.is_basis is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [T : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α T), TopologicalSpace.IsTopologicalBasis.{u1} α T (Set.range.{u1, succ u2} (Set.{u1} α) (Ctop.Realizer.σ.{u1, u2} α T F) (Ctop.f.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F) (Ctop.Realizer.f.{u1, u2} α T F)))
but is expected to have type
  forall {α : Type.{u2}} [T : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α T), TopologicalSpace.IsTopologicalBasis.{u2} α T (Set.range.{u2, succ u1} (Set.{u2} α) (Ctop.Realizer.σ.{u2, u1} α T F) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α T F) (Ctop.Realizer.F.{u2, u1} α T F)))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.is_basis Ctop.Realizer.is_basisₓ'. -/
protected theorem is_basis [T : TopologicalSpace α] (F : Realizer α) :
    TopologicalSpace.IsTopologicalBasis (Set.range F.f.f) := by
  have := to_topsp_is_topological_basis F.F <;> rwa [F.eq] at this
#align ctop.realizer.is_basis Ctop.Realizer.is_basis

/- warning: ctop.realizer.mem_nhds -> Ctop.Realizer.mem_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [T : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α T) {s : Set.{u1} α} {a : α}, Iff (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α T a)) (Exists.{succ u2} (Ctop.Realizer.σ.{u1, u2} α T F) (fun (b : Ctop.Realizer.σ.{u1, u2} α T F) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) => (Ctop.Realizer.σ.{u1, u2} α T F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) (Ctop.Realizer.f.{u1, u2} α T F) b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) => (Ctop.Realizer.σ.{u1, u2} α T F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α T F)) (Ctop.Realizer.f.{u1, u2} α T F) b) s)))
but is expected to have type
  forall {α : Type.{u2}} [T : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α T) {s : Set.{u2} α} {a : α}, Iff (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α T a)) (Exists.{succ u1} (Ctop.Realizer.σ.{u2, u1} α T F) (fun (b : Ctop.Realizer.σ.{u2, u1} α T F) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α T F) (Ctop.Realizer.F.{u2, u1} α T F) b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α T F) (Ctop.Realizer.F.{u2, u1} α T F) b) s)))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.mem_nhds Ctop.Realizer.mem_nhdsₓ'. -/
protected theorem mem_nhds [T : TopologicalSpace α] (F : Realizer α) {s : Set α} {a : α} :
    s ∈ 𝓝 a ↔ ∃ b, a ∈ F.f b ∧ F.f b ⊆ s := by have := mem_nhds_to_topsp F.F <;> rwa [F.eq] at this
#align ctop.realizer.mem_nhds Ctop.Realizer.mem_nhds

/- warning: ctop.realizer.is_open_iff -> Ctop.Realizer.isOpen_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) {s : Set.{u1} α}, Iff (IsOpen.{u1} α _inst_1 s) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (Exists.{succ u2} (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) (fun (b : Ctop.Realizer.σ.{u1, u2} α _inst_1 F) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b) s))))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) {s : Set.{u2} α}, Iff (IsOpen.{u2} α _inst_1 s) (forall (a : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a s) -> (Exists.{succ u1} (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (fun (b : Ctop.Realizer.σ.{u2, u1} α _inst_1 F) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b) s))))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.is_open_iff Ctop.Realizer.isOpen_iffₓ'. -/
theorem isOpen_iff [TopologicalSpace α] (F : Realizer α) {s : Set α} :
    IsOpen s ↔ ∀ a ∈ s, ∃ b, a ∈ F.f b ∧ F.f b ⊆ s :=
  isOpen_iff_mem_nhds.trans <| ball_congr fun a h => F.mem_nhds
#align ctop.realizer.is_open_iff Ctop.Realizer.isOpen_iff

/- warning: ctop.realizer.is_closed_iff -> Ctop.Realizer.isClosed_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) {s : Set.{u1} α}, Iff (IsClosed.{u1} α _inst_1 s) (forall (a : α), (forall (b : Ctop.Realizer.σ.{u1, u2} α _inst_1 F), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b)) -> (Exists.{succ u1} α (fun (z : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) z (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b) s)))) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) {s : Set.{u2} α}, Iff (IsClosed.{u2} α _inst_1 s) (forall (a : α), (forall (b : Ctop.Realizer.σ.{u2, u1} α _inst_1 F), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b)) -> (Exists.{succ u2} α (fun (z : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) z (Inter.inter.{u2} (Set.{u2} α) (Set.instInterSet.{u2} α) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b) s)))) -> (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a s))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.is_closed_iff Ctop.Realizer.isClosed_iffₓ'. -/
theorem isClosed_iff [TopologicalSpace α] (F : Realizer α) {s : Set α} :
    IsClosed s ↔ ∀ a, (∀ b, a ∈ F.f b → ∃ z, z ∈ F.f b ∩ s) → a ∈ s :=
  isOpen_compl_iff.symm.trans <|
    F.isOpen_iff.trans <|
      forall_congr' fun a =>
        show (a ∉ s → ∃ b : F.σ, a ∈ F.f b ∧ ∀ z ∈ F.f b, z ∉ s) ↔ _ by
          haveI := Classical.propDecidable <;> rw [not_imp_comm] <;>
            simp [not_exists, not_and, not_forall, and_comm']
#align ctop.realizer.is_closed_iff Ctop.Realizer.isClosed_iff

/- warning: ctop.realizer.mem_interior_iff -> Ctop.Realizer.mem_interior_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) {s : Set.{u1} α} {a : α}, Iff (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (interior.{u1} α _inst_1 s)) (Exists.{succ u2} (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) (fun (b : Ctop.Realizer.σ.{u1, u2} α _inst_1 F) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) b) s)))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) {s : Set.{u2} α} {a : α}, Iff (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (interior.{u2} α _inst_1 s)) (Exists.{succ u1} (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (fun (b : Ctop.Realizer.σ.{u2, u1} α _inst_1 F) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) b) s)))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.mem_interior_iff Ctop.Realizer.mem_interior_iffₓ'. -/
theorem mem_interior_iff [TopologicalSpace α] (F : Realizer α) {s : Set α} {a : α} :
    a ∈ interior s ↔ ∃ b, a ∈ F.f b ∧ F.f b ⊆ s :=
  mem_interior_iff_mem_nhds.trans F.mem_nhds
#align ctop.realizer.mem_interior_iff Ctop.Realizer.mem_interior_iff

/- warning: ctop.realizer.is_open -> Ctop.Realizer.isOpen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) (s : Ctop.Realizer.σ.{u1, u2} α _inst_1 F), IsOpen.{u1} α _inst_1 (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) s)
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) (s : Ctop.Realizer.σ.{u2, u1} α _inst_1 F), IsOpen.{u2} α _inst_1 (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) s)
Case conversion may be inaccurate. Consider using '#align ctop.realizer.is_open Ctop.Realizer.isOpenₓ'. -/
protected theorem isOpen [TopologicalSpace α] (F : Realizer α) (s : F.σ) : IsOpen (F.f s) :=
  isOpen_iff_nhds.2 fun a m => by simpa using F.mem_nhds.2 ⟨s, m, subset.refl _⟩
#align ctop.realizer.is_open Ctop.Realizer.isOpen

/- warning: ctop.realizer.ext' -> Ctop.Realizer.ext' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [T : TopologicalSpace.{u1} α] {σ : Type.{u2}} {F : Ctop.{u1, u2} α σ}, (forall (a : α) (s : Set.{u1} α), Iff (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α T a)) (Exists.{succ u2} σ (fun (b : σ) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b) s)))) -> (Eq.{succ u1} (TopologicalSpace.{u1} α) (Ctop.toTopsp.{u1, u2} α σ F) T)
but is expected to have type
  forall {α : Type.{u2}} [T : TopologicalSpace.{u2} α] {σ : Type.{u1}} {F : Ctop.{u2, u1} α σ}, (forall (a : α) (s : Set.{u2} α), Iff (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α T a)) (Exists.{succ u1} σ (fun (b : σ) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α σ F b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α σ F b) s)))) -> (Eq.{succ u2} (TopologicalSpace.{u2} α) (Ctop.toTopsp.{u2, u1} α σ F) T)
Case conversion may be inaccurate. Consider using '#align ctop.realizer.ext' Ctop.Realizer.ext'ₓ'. -/
theorem ext' [T : TopologicalSpace α] {σ : Type _} {F : Ctop α σ}
    (H : ∀ a s, s ∈ 𝓝 a ↔ ∃ b, a ∈ F b ∧ F b ⊆ s) : F.toTopsp = T :=
  by
  refine' eq_of_nhds_eq_nhds fun x => _
  ext s
  rw [mem_nhds_to_topsp, H]
#align ctop.realizer.ext' Ctop.Realizer.ext'

/- warning: ctop.realizer.ext -> Ctop.Realizer.ext is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [T : TopologicalSpace.{u1} α] {σ : Type.{u2}} {F : Ctop.{u1, u2} α σ}, (forall (a : σ), IsOpen.{u1} α T (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F a)) -> (forall (a : α) (s : Set.{u1} α), (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α T a)) -> (Exists.{succ u2} σ (fun (b : σ) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b)) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α σ) (fun (_x : Ctop.{u1, u2} α σ) => σ -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α σ) F b) s)))) -> (Eq.{succ u1} (TopologicalSpace.{u1} α) (Ctop.toTopsp.{u1, u2} α σ F) T)
but is expected to have type
  forall {α : Type.{u2}} [T : TopologicalSpace.{u2} α] {σ : Type.{u1}} {F : Ctop.{u2, u1} α σ}, (forall (a : σ), IsOpen.{u2} α T (Ctop.f.{u2, u1} α σ F a)) -> (forall (a : α) (s : Set.{u2} α), (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α T a)) -> (Exists.{succ u1} σ (fun (b : σ) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α σ F b)) (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) (Ctop.f.{u2, u1} α σ F b) s)))) -> (Eq.{succ u2} (TopologicalSpace.{u2} α) (Ctop.toTopsp.{u2, u1} α σ F) T)
Case conversion may be inaccurate. Consider using '#align ctop.realizer.ext Ctop.Realizer.extₓ'. -/
theorem ext [T : TopologicalSpace α] {σ : Type _} {F : Ctop α σ} (H₁ : ∀ a, IsOpen (F a))
    (H₂ : ∀ a s, s ∈ 𝓝 a → ∃ b, a ∈ F b ∧ F b ⊆ s) : F.toTopsp = T :=
  ext' fun a s => ⟨H₂ a s, fun ⟨b, h₁, h₂⟩ => mem_nhds_iff.2 ⟨_, h₂, H₁ _, h₁⟩⟩
#align ctop.realizer.ext Ctop.Realizer.ext

variable [TopologicalSpace α]

#print Ctop.Realizer.id /-
/-- The topological space realizer made of the open sets. -/
protected def id : Realizer α :=
  ⟨{ x : Set α // IsOpen x },
    { f := Subtype.val
      top := fun _ => ⟨univ, isOpen_univ⟩
      top_mem := mem_univ
      inter := fun ⟨x, h₁⟩ ⟨y, h₂⟩ a h₃ => ⟨_, h₁.inter h₂⟩
      inter_mem := fun ⟨x, h₁⟩ ⟨y, h₂⟩ a => id
      inter_sub := fun ⟨x, h₁⟩ ⟨y, h₂⟩ a h₃ => Subset.refl _ },
    ext Subtype.property fun x s h =>
      let ⟨t, h, o, m⟩ := mem_nhds_iff.1 h
      ⟨⟨t, o⟩, m, h⟩⟩
#align ctop.realizer.id Ctop.Realizer.id
-/

#print Ctop.Realizer.ofEquiv /-
/-- Replace the representation type of a `ctop` realizer. -/
def ofEquiv (F : Realizer α) (E : F.σ ≃ τ) : Realizer α :=
  ⟨τ, F.f.of_equiv E,
    ext' fun a s =>
      F.mem_nhds.trans <|
        ⟨fun ⟨s, h⟩ => ⟨E s, by simpa using h⟩, fun ⟨t, h⟩ => ⟨E.symm t, by simpa using h⟩⟩⟩
#align ctop.realizer.of_equiv Ctop.Realizer.ofEquiv
-/

/- warning: ctop.realizer.of_equiv_σ -> Ctop.Realizer.ofEquiv_σ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {τ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u3} α _inst_1) (E : Equiv.{succ u3, succ u2} (Ctop.Realizer.σ.{u1, u3} α _inst_1 F) τ), Eq.{succ (succ u2)} Type.{u2} (Ctop.Realizer.σ.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E)) τ
but is expected to have type
  forall {α : Type.{u3}} {τ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] (F : Ctop.Realizer.{u3, u2} α _inst_1) (E : Equiv.{succ u2, succ u1} (Ctop.Realizer.σ.{u3, u2} α _inst_1 F) τ), Eq.{succ (succ u1)} Type.{u1} (Ctop.Realizer.σ.{u3, u1} α _inst_1 (Ctop.Realizer.ofEquiv.{u3, u1, u2} α τ _inst_1 F E)) τ
Case conversion may be inaccurate. Consider using '#align ctop.realizer.of_equiv_σ Ctop.Realizer.ofEquiv_σₓ'. -/
@[simp]
theorem ofEquiv_σ (F : Realizer α) (E : F.σ ≃ τ) : (F.of_equiv E).σ = τ :=
  rfl
#align ctop.realizer.of_equiv_σ Ctop.Realizer.ofEquiv_σ

/- warning: ctop.realizer.of_equiv_F -> Ctop.Realizer.ofEquiv_F is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {τ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u3} α _inst_1) (E : Equiv.{succ u3, succ u2} (Ctop.Realizer.σ.{u1, u3} α _inst_1 F) τ) (s : τ), Eq.{succ u1} (Set.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E))) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E))) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E)) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E))) (Ctop.Realizer.f.{u1, u2} α _inst_1 (Ctop.Realizer.ofEquiv.{u1, u2, u3} α τ _inst_1 F E)) s) (coeFn.{max (succ u1) (succ u3), max (succ u3) (succ u1)} (Ctop.{u1, u3} α (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) (fun (_x : Ctop.{u1, u3} α (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u3} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u3} α (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) (Ctop.Realizer.f.{u1, u3} α _inst_1 F) (coeFn.{max 1 (max (succ u2) (succ u3)) (succ u3) (succ u2), max (succ u2) (succ u3)} (Equiv.{succ u2, succ u3} τ (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) (fun (_x : Equiv.{succ u2, succ u3} τ (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) => τ -> (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) (Equiv.hasCoeToFun.{succ u2, succ u3} τ (Ctop.Realizer.σ.{u1, u3} α _inst_1 F)) (Equiv.symm.{succ u3, succ u2} (Ctop.Realizer.σ.{u1, u3} α _inst_1 F) τ E) s))
but is expected to have type
  forall {α : Type.{u3}} {τ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] (F : Ctop.Realizer.{u3, u2} α _inst_1) (E : Equiv.{succ u2, succ u1} (Ctop.Realizer.σ.{u3, u2} α _inst_1 F) τ) (s : τ), Eq.{succ u3} (Set.{u3} α) (Ctop.f.{u3, u1} α (Ctop.Realizer.σ.{u3, u1} α _inst_1 (Ctop.Realizer.ofEquiv.{u3, u1, u2} α τ _inst_1 F E)) (Ctop.Realizer.F.{u3, u1} α _inst_1 (Ctop.Realizer.ofEquiv.{u3, u1, u2} α τ _inst_1 F E)) s) (Ctop.f.{u3, u2} α (Ctop.Realizer.σ.{u3, u2} α _inst_1 F) (Ctop.Realizer.F.{u3, u2} α _inst_1 F) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Equiv.{succ u1, succ u2} τ (Ctop.Realizer.σ.{u3, u2} α _inst_1 F)) τ (fun (_x : τ) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : τ) => Ctop.Realizer.σ.{u3, u2} α _inst_1 F) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} τ (Ctop.Realizer.σ.{u3, u2} α _inst_1 F)) (Equiv.symm.{succ u2, succ u1} (Ctop.Realizer.σ.{u3, u2} α _inst_1 F) τ E) s))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.of_equiv_F Ctop.Realizer.ofEquiv_Fₓ'. -/
@[simp]
theorem ofEquiv_F (F : Realizer α) (E : F.σ ≃ τ) (s : τ) : (F.of_equiv E).f s = F.f (E.symm s) := by
  delta of_equiv <;> simp
#align ctop.realizer.of_equiv_F Ctop.Realizer.ofEquiv_F

#print Ctop.Realizer.nhds /-
/-- A realizer of the neighborhood of a point. -/
protected def nhds (F : Realizer α) (a : α) : (𝓝 a).Realizer :=
  ⟨{ s : F.σ // a ∈ F.f s },
    { f := fun s => F.f s.1
      pt := ⟨_, F.f.top_mem a⟩
      inf := fun ⟨x, h₁⟩ ⟨y, h₂⟩ => ⟨_, F.f.inter_mem x y a ⟨h₁, h₂⟩⟩
      inf_le_left := fun ⟨x, h₁⟩ ⟨y, h₂⟩ z h => (F.f.inter_sub x y a ⟨h₁, h₂⟩ h).1
      inf_le_right := fun ⟨x, h₁⟩ ⟨y, h₂⟩ z h => (F.f.inter_sub x y a ⟨h₁, h₂⟩ h).2 },
    filter_eq <|
      Set.ext fun x =>
        ⟨fun ⟨⟨s, as⟩, h⟩ => mem_nhds_iff.2 ⟨_, h, F.IsOpen _, as⟩, fun h =>
          let ⟨s, h, as⟩ := F.mem_nhds.1 h
          ⟨⟨s, h⟩, as⟩⟩⟩
#align ctop.realizer.nhds Ctop.Realizer.nhds
-/

/- warning: ctop.realizer.nhds_σ -> Ctop.Realizer.nhds_σ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) (a : α), Eq.{succ (succ u2)} Type.{u2} (Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) (Subtype.{succ u2} (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) (fun (s : Ctop.Realizer.σ.{u1, u2} α _inst_1 F) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) s)))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) (a : α), Eq.{succ (succ u1)} Type.{u1} (Filter.Realizer.σ.{u2, u1} α (nhds.{u2} α _inst_1 a) (Ctop.Realizer.nhds.{u2, u1} α _inst_1 F a)) (Subtype.{succ u1} (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (fun (s : Ctop.Realizer.σ.{u2, u1} α _inst_1 F) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) s)))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.nhds_σ Ctop.Realizer.nhds_σₓ'. -/
@[simp]
theorem nhds_σ (F : Realizer α) (a : α) : (F.nhds a).σ = { s : F.σ // a ∈ F.f s } :=
  rfl
#align ctop.realizer.nhds_σ Ctop.Realizer.nhds_σ

/- warning: ctop.realizer.nhds_F -> Ctop.Realizer.nhds_F is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u2} α _inst_1) (a : α) (s : Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)), Eq.{succ u1} (Set.{u1} α) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (CFilter.{u1, u2} (Set.{u1} α) (Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))))) (fun (_x : CFilter.{u1, u2} (Set.{u1} α) (Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))))) => (Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) -> (Set.{u1} α)) (CFilter.hasCoeToFun.{u1, u2} (Set.{u1} α) (Filter.Realizer.σ.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))))) (Filter.Realizer.f.{u1, u2} α (nhds.{u1} α _inst_1 a) (Ctop.Realizer.nhds.{u1, u2} α _inst_1 F a)) s) (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) (Subtype.val.{succ u2} (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) (fun (s : Ctop.Realizer.σ.{u1, u2} α _inst_1 F) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (fun (_x : Ctop.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) => (Ctop.Realizer.σ.{u1, u2} α _inst_1 F) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u2} α (Ctop.Realizer.σ.{u1, u2} α _inst_1 F)) (Ctop.Realizer.f.{u1, u2} α _inst_1 F) s)) s))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} α] (F : Ctop.Realizer.{u2, u1} α _inst_1) (a : α) (s : Filter.Realizer.σ.{u2, u1} α (nhds.{u2} α _inst_1 a) (Ctop.Realizer.nhds.{u2, u1} α _inst_1 F a)), Eq.{succ u2} (Set.{u2} α) (CFilter.f.{u2, u1} (Set.{u2} α) (Filter.Realizer.σ.{u2, u1} α (nhds.{u2} α _inst_1 a) (Ctop.Realizer.nhds.{u2, u1} α _inst_1 F a)) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α)))))) (Filter.Realizer.F.{u2, u1} α (nhds.{u2} α _inst_1 a) (Ctop.Realizer.nhds.{u2, u1} α _inst_1 F a)) s) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) (Subtype.val.{succ u1} (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (fun (s : Ctop.Realizer.σ.{u2, u1} α _inst_1 F) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 F) (Ctop.Realizer.F.{u2, u1} α _inst_1 F) s)) s))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.nhds_F Ctop.Realizer.nhds_Fₓ'. -/
@[simp]
theorem nhds_F (F : Realizer α) (a : α) (s) : (F.nhds a).f s = F.f s.1 :=
  rfl
#align ctop.realizer.nhds_F Ctop.Realizer.nhds_F

/- warning: ctop.realizer.tendsto_nhds_iff -> Ctop.Realizer.tendsto_nhds_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] {m : β -> α} {f : Filter.{u2} β} (F : Filter.Realizer.{u2, u3} β f) (R : Ctop.Realizer.{u1, u4} α _inst_1) {a : α}, Iff (Filter.Tendsto.{u2, u1} β α m f (nhds.{u1} α _inst_1 a)) (forall (t : Ctop.Realizer.σ.{u1, u4} α _inst_1 R), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (coeFn.{max (succ u1) (succ u4), max (succ u4) (succ u1)} (Ctop.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) (fun (_x : Ctop.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) => (Ctop.Realizer.σ.{u1, u4} α _inst_1 R) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) (Ctop.Realizer.f.{u1, u4} α _inst_1 R) t)) -> (Exists.{succ u3} (Filter.Realizer.σ.{u2, u3} β f F) (fun (s : Filter.Realizer.σ.{u2, u3} β f F) => forall (x : β), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (coeFn.{max (succ u2) (succ u3), max (succ u3) (succ u2)} (CFilter.{u2, u3} (Set.{u2} β) (Filter.Realizer.σ.{u2, u3} β f F) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} β) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} β) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} β) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} β) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} β) (Set.completeBooleanAlgebra.{u2} β))))))) (fun (_x : CFilter.{u2, u3} (Set.{u2} β) (Filter.Realizer.σ.{u2, u3} β f F) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} β) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} β) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} β) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} β) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} β) (Set.completeBooleanAlgebra.{u2} β))))))) => (Filter.Realizer.σ.{u2, u3} β f F) -> (Set.{u2} β)) (CFilter.hasCoeToFun.{u2, u3} (Set.{u2} β) (Filter.Realizer.σ.{u2, u3} β f F) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} β) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} β) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} β) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} β) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} β) (Set.completeBooleanAlgebra.{u2} β))))))) (Filter.Realizer.f.{u2, u3} β f F) s)) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (m x) (coeFn.{max (succ u1) (succ u4), max (succ u4) (succ u1)} (Ctop.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) (fun (_x : Ctop.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) => (Ctop.Realizer.σ.{u1, u4} α _inst_1 R) -> (Set.{u1} α)) (Ctop.hasCoeToFun.{u1, u4} α (Ctop.Realizer.σ.{u1, u4} α _inst_1 R)) (Ctop.Realizer.f.{u1, u4} α _inst_1 R) t)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u4}} [_inst_1 : TopologicalSpace.{u2} α] {m : β -> α} {f : Filter.{u4} β} (F : Filter.Realizer.{u4, u3} β f) (R : Ctop.Realizer.{u2, u1} α _inst_1) {a : α}, Iff (Filter.Tendsto.{u4, u2} β α m f (nhds.{u2} α _inst_1 a)) (forall (t : Ctop.Realizer.σ.{u2, u1} α _inst_1 R), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 R) (Ctop.Realizer.F.{u2, u1} α _inst_1 R) t)) -> (Exists.{succ u3} (Filter.Realizer.σ.{u4, u3} β f F) (fun (s : Filter.Realizer.σ.{u4, u3} β f F) => forall (x : β), (Membership.mem.{u4, u4} β (Set.{u4} β) (Set.instMembershipSet.{u4} β) x (CFilter.f.{u4, u3} (Set.{u4} β) (Filter.Realizer.σ.{u4, u3} β f F) (CompleteSemilatticeInf.toPartialOrder.{u4} (Set.{u4} β) (CompleteLattice.toCompleteSemilatticeInf.{u4} (Set.{u4} β) (Order.Coframe.toCompleteLattice.{u4} (Set.{u4} β) (CompleteDistribLattice.toCoframe.{u4} (Set.{u4} β) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u4} (Set.{u4} β) (Set.instCompleteBooleanAlgebraSet.{u4} β)))))) (Filter.Realizer.F.{u4, u3} β f F) s)) -> (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) (m x) (Ctop.f.{u2, u1} α (Ctop.Realizer.σ.{u2, u1} α _inst_1 R) (Ctop.Realizer.F.{u2, u1} α _inst_1 R) t)))))
Case conversion may be inaccurate. Consider using '#align ctop.realizer.tendsto_nhds_iff Ctop.Realizer.tendsto_nhds_iffₓ'. -/
theorem tendsto_nhds_iff {m : β → α} {f : Filter β} (F : f.Realizer) (R : Realizer α) {a : α} :
    Tendsto m f (𝓝 a) ↔ ∀ t, a ∈ R.f t → ∃ s, ∀ x ∈ F.f s, m x ∈ R.f t :=
  (F.tendsto_iffₓ _ (R.nhds a)).trans Subtype.forall
#align ctop.realizer.tendsto_nhds_iff Ctop.Realizer.tendsto_nhds_iff

end Ctop.Realizer

#print LocallyFinite.Realizer /-
/-- A `locally_finite.realizer F f` is a realization that `f` is locally finite, namely it is a
choice of open sets from the basis of `F` such that they intersect only finitely many of the values
of `f`.  -/
structure LocallyFinite.Realizer [TopologicalSpace α] (F : Realizer α) (f : β → Set α) where
  bas : ∀ a, { s // a ∈ F.f s }
  sets : ∀ x : α, Fintype { i | (f i ∩ F.f (bas x)).Nonempty }
#align locally_finite.realizer LocallyFinite.Realizer
-/

/- warning: locally_finite.realizer.to_locally_finite -> LocallyFinite.Realizer.to_locallyFinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] {F : Ctop.Realizer.{u1, u3} α _inst_1} {f : β -> (Set.{u1} α)}, (LocallyFinite.Realizer.{u1, u2, u3} α β _inst_1 F f) -> (LocallyFinite.{u2, u1} β α _inst_1 f)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] {F : Ctop.Realizer.{u3, u2} α _inst_1} {f : β -> (Set.{u3} α)}, (LocallyFinite.Realizer.{u3, u1, u2} α β _inst_1 F f) -> (LocallyFinite.{u1, u3} β α _inst_1 f)
Case conversion may be inaccurate. Consider using '#align locally_finite.realizer.to_locally_finite LocallyFinite.Realizer.to_locallyFiniteₓ'. -/
theorem LocallyFinite.Realizer.to_locallyFinite [TopologicalSpace α] {F : Realizer α}
    {f : β → Set α} (R : LocallyFinite.Realizer F f) : LocallyFinite f := fun a =>
  ⟨_, F.mem_nhds.2 ⟨(R.bas a).1, (R.bas a).2, Subset.refl _⟩, ⟨R.sets a⟩⟩
#align locally_finite.realizer.to_locally_finite LocallyFinite.Realizer.to_locallyFinite

/- warning: locally_finite_iff_exists_realizer -> locallyFinite_iff_exists_realizer is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] (F : Ctop.Realizer.{u1, u3} α _inst_1) {f : β -> (Set.{u1} α)}, Iff (LocallyFinite.{u2, u1} β α _inst_1 f) (Nonempty.{max (succ u1) (succ u2) (succ u3)} (LocallyFinite.Realizer.{u1, u2, u3} α β _inst_1 F f))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] (F : Ctop.Realizer.{u3, u2} α _inst_1) {f : β -> (Set.{u3} α)}, Iff (LocallyFinite.{u1, u3} β α _inst_1 f) (Nonempty.{max (max (succ u2) (succ u1)) (succ u3)} (LocallyFinite.Realizer.{u3, u1, u2} α β _inst_1 F f))
Case conversion may be inaccurate. Consider using '#align locally_finite_iff_exists_realizer locallyFinite_iff_exists_realizerₓ'. -/
theorem locallyFinite_iff_exists_realizer [TopologicalSpace α] (F : Realizer α) {f : β → Set α} :
    LocallyFinite f ↔ Nonempty (LocallyFinite.Realizer F f) :=
  ⟨fun h =>
    let ⟨g, h₁⟩ := Classical.axiom_of_choice h
    let ⟨g₂, h₂⟩ :=
      Classical.axiom_of_choice fun x =>
        show ∃ b : F.σ, x ∈ F.f b ∧ F.f b ⊆ g x from
          let ⟨h, h'⟩ := h₁ x
          F.mem_nhds.1 h
    ⟨⟨fun x => ⟨g₂ x, (h₂ x).1⟩, fun x =>
        Finite.fintype <|
          let ⟨h, h'⟩ := h₁ x
          h'.Subset fun i hi => hi.mono (inter_subset_inter_right _ (h₂ x).2)⟩⟩,
    fun ⟨R⟩ => R.to_locallyFinite⟩
#align locally_finite_iff_exists_realizer locallyFinite_iff_exists_realizer

instance [TopologicalSpace α] [Finite β] (F : Realizer α) (f : β → Set α) :
    Nonempty (LocallyFinite.Realizer F f) :=
  (locallyFinite_iff_exists_realizer _).1 <| locallyFinite_of_finite _

#print Compact.Realizer /-
/-- A `compact.realizer s` is a realization that `s` is compact, namely it is a
choice of finite open covers for each set family covering `s`.  -/
def Compact.Realizer [TopologicalSpace α] (s : Set α) :=
  ∀ {f : Filter α} (F : f.Realizer) (x : F.σ), f ≠ ⊥ → F.f x ⊆ s → { a // a ∈ s ∧ 𝓝 a ⊓ f ≠ ⊥ }
#align compact.realizer Compact.Realizer
-/

instance [TopologicalSpace α] : Inhabited (Compact.Realizer (∅ : Set α)) :=
  ⟨fun f F x h hF => by
    cases h _
    rw [← F.eq, eq_bot_iff]
    exact fun s _ => ⟨x, hF.trans s.empty_subset⟩⟩


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.metric_space.closeds
import Mathlib.set_theory.cardinal
import Mathlib.topology.metric_space.gromov_hausdorff_realized
import Mathlib.topology.metric_space.completion
import Mathlib.PostPort

universes u v w l

namespace Mathlib

/-!
# Gromov-Hausdorff distance

This file defines the Gromov-Hausdorff distance on the space of nonempty compact metric spaces
up to isometry.

We introduce the space of all nonempty compact metric spaces, up to isometry,
called `GH_space`, and endow it with a metric space structure. The distance,
known as the Gromov-Hausdorff distance, is defined as follows: given two
nonempty compact spaces `X` and `Y`, their distance is the minimum Hausdorff distance
between all possible isometric embeddings of `X` and `Y` in all metric spaces.
To define properly the Gromov-Hausdorff space, we consider the non-empty
compact subsets of `ℓ^∞(ℝ)` up to isometry, which is a well-defined type,
and define the distance as the infimum of the Hausdorff distance over all
embeddings in `ℓ^∞(ℝ)`. We prove that this coincides with the previous description,
as all separable metric spaces embed isometrically into `ℓ^∞(ℝ)`, through an
embedding called the Kuratowski embedding.
To prove that we have a distance, we should show that if spaces can be coupled
to be arbitrarily close, then they are isometric. More generally, the Gromov-Hausdorff
distance is realized, i.e., there is a coupling for which the Hausdorff distance
is exactly the Gromov-Hausdorff distance. This follows from a compactness
argument, essentially following from Arzela-Ascoli.

## Main results

We prove the most important properties of the Gromov-Hausdorff space: it is a polish space,
i.e., it is complete and second countable. We also prove the Gromov compactness criterion.

-/

namespace Gromov_Hausdorff

/- In this section, we define the Gromov-Hausdorff space, denoted `GH_space` as the quotient
of nonempty compact subsets of `ℓ^∞(ℝ)` by identifying isometric sets.
Using the Kuratwoski embedding, we get a canonical map `to_GH_space` mapping any nonempty
compact type to `GH_space`. -/

/-- Equivalence relation identifying two nonempty compact sets which are isometric -/
/-- This is indeed an equivalence relation -/
/-- setoid instance identifying two isometric nonempty compact subspaces of ℓ^∞(ℝ) -/
protected instance isometry_rel.setoid : setoid (topological_space.nonempty_compacts ℓ_infty_ℝ) :=
  setoid.mk isometry_rel is_equivalence_isometry_rel

/-- The Gromov-Hausdorff space -/
def GH_space :=
  quotient isometry_rel.setoid

/-- Map any nonempty compact type to `GH_space` -/
def to_GH_space (α : Type u) [metric_space α] [compact_space α] [Nonempty α] : GH_space :=
  quotient.mk (nonempty_compacts.Kuratowski_embedding α)

protected instance GH_space.inhabited : Inhabited GH_space :=
  { default := Quot.mk setoid.r { val := singleton 0, property := sorry } }

/-- A metric space representative of any abstract point in `GH_space` -/
def GH_space.rep (p : GH_space) :=
  ↥(subtype.val (quot.out p))

theorem eq_to_GH_space_iff {α : Type u} [metric_space α] [compact_space α] [Nonempty α] {p : topological_space.nonempty_compacts ℓ_infty_ℝ} : quotient.mk p = to_GH_space α ↔ ∃ (Ψ : α → ℓ_infty_ℝ), isometry Ψ ∧ set.range Ψ = subtype.val p := sorry

theorem eq_to_GH_space {p : topological_space.nonempty_compacts ℓ_infty_ℝ} : quotient.mk p = to_GH_space ↥(subtype.val p) :=
  iff.mpr eq_to_GH_space_iff
    (Exists.intro (fun (x : ↥(subtype.val p)) => ↑x) { left := isometry_subtype_coe, right := subtype.range_coe })

protected instance rep_GH_space_metric_space {p : GH_space} : metric_space (GH_space.rep p) :=
  subtype.metric_space

protected instance rep_GH_space_compact_space {p : GH_space} : compact_space (GH_space.rep p) :=
  topological_space.nonempty_compacts.to_compact_space

protected instance rep_GH_space_nonempty {p : GH_space} : Nonempty (GH_space.rep p) :=
  topological_space.nonempty_compacts.to_nonempty

theorem GH_space.to_GH_space_rep (p : GH_space) : to_GH_space (GH_space.rep p) = p :=
  id
    (eq.mpr (id (Eq._oldrec (Eq.refl (to_GH_space ↥(subtype.val (quot.out p)) = p)) (Eq.symm eq_to_GH_space)))
      (quot.out_eq p))

/-- Two nonempty compact spaces have the same image in `GH_space` if and only if they are
isometric. -/
theorem to_GH_space_eq_to_GH_space_iff_isometric {α : Type u} [metric_space α] [compact_space α] [Nonempty α] {β : Type u} [metric_space β] [compact_space β] [Nonempty β] : to_GH_space α = to_GH_space β ↔ Nonempty (α ≃ᵢ β) := sorry

/-- Distance on `GH_space`: the distance between two nonempty compact spaces is the infimum
Hausdorff distance between isometric copies of the two spaces in a metric space. For the definition,
we only consider embeddings in `ℓ^∞(ℝ)`, but we will prove below that it works for all spaces. -/
protected instance GH_space.has_dist : has_dist GH_space :=
  has_dist.mk
    fun (x y : GH_space) =>
      Inf
        ((fun (p : topological_space.nonempty_compacts ℓ_infty_ℝ × topological_space.nonempty_compacts ℓ_infty_ℝ) =>
            metric.Hausdorff_dist (subtype.val (prod.fst p)) (subtype.val (prod.snd p))) ''
          set.prod (set_of fun (a : topological_space.nonempty_compacts ℓ_infty_ℝ) => quotient.mk a = x)
            (set_of fun (b : topological_space.nonempty_compacts ℓ_infty_ℝ) => quotient.mk b = y))

/-- The Gromov-Hausdorff distance between two nonempty compact metric spaces, equal by definition to
the distance of the equivalence classes of these spaces in the Gromov-Hausdorff space. -/
def GH_dist (α : Type u) (β : Type v) [metric_space α] [Nonempty α] [compact_space α] [metric_space β] [Nonempty β] [compact_space β] : ℝ :=
  dist (to_GH_space α) (to_GH_space β)

theorem dist_GH_dist (p : GH_space) (q : GH_space) : dist p q = GH_dist (GH_space.rep p) (GH_space.rep q) := sorry

/-- The Gromov-Hausdorff distance between two spaces is bounded by the Hausdorff distance
of isometric copies of the spaces, in any metric space. -/
theorem GH_dist_le_Hausdorff_dist {α : Type u} [metric_space α] [compact_space α] [Nonempty α] {β : Type v} [metric_space β] [compact_space β] [Nonempty β] {γ : Type w} [metric_space γ] {Φ : α → γ} {Ψ : β → γ} (ha : isometry Φ) (hb : isometry Ψ) : GH_dist α β ≤ metric.Hausdorff_dist (set.range Φ) (set.range Ψ) := sorry

/-- The optimal coupling constructed above realizes exactly the Gromov-Hausdorff distance,
essentially by design. -/
theorem Hausdorff_dist_optimal {α : Type u} [metric_space α] [compact_space α] [Nonempty α] {β : Type v} [metric_space β] [compact_space β] [Nonempty β] : metric.Hausdorff_dist (set.range (optimal_GH_injl α β)) (set.range (optimal_GH_injr α β)) = GH_dist α β := sorry

/-- The Gromov-Hausdorff distance can also be realized by a coupling in `ℓ^∞(ℝ)`, by embedding
the optimal coupling through its Kuratowski embedding. -/
theorem GH_dist_eq_Hausdorff_dist (α : Type u) [metric_space α] [compact_space α] [Nonempty α] (β : Type v) [metric_space β] [compact_space β] [Nonempty β] : ∃ (Φ : α → ℓ_infty_ℝ),
  ∃ (Ψ : β → ℓ_infty_ℝ), isometry Φ ∧ isometry Ψ ∧ GH_dist α β = metric.Hausdorff_dist (set.range Φ) (set.range Ψ) := sorry

-- without the next two lines, `{ exact hΦ.is_closed }` in the next

-- proof is very slow, as the `t2_space` instance is very hard to find

/-- The Gromov-Hausdorff distance defines a genuine distance on the Gromov-Hausdorff space. -/
protected instance GH_space_metric_space : metric_space GH_space := sorry

end Gromov_Hausdorff

/-- In particular, nonempty compacts of a metric space map to `GH_space`. We register this
in the topological_space namespace to take advantage of the notation `p.to_GH_space`. -/
def topological_space.nonempty_compacts.to_GH_space {α : Type u} [metric_space α] (p : topological_spa


constant f : Nat → Nat
constant g : Nat → Nat

namespace Foo

@[scoped simp] axiom ax1 (x : Nat) : f (g x) = x
@[scoped simp] axiom ax2 (x : Nat) : g (g x) = g x

end Foo

theorem ex1 : f (g (g (g x))) = x := by
  simp -- does not use ax1 and ax2
  simp [Foo.ax1, Foo.ax2]

theorem ex2 : f (g (g (g x))) = x :=
  have h₁ : f (g (g (g x))) = f (g x) := by simp; /- try again with `Foo` scoped lemmas -/ open Foo in simp
  have h₂ : f (g x) = x               := by simp; open Foo in simp
  Eq.trans h₁ h₂
  -- open Foo in simp -- works

theorem ex3 : f (g (g (g x))) = x := by
  simp
  simp [Foo.ax1, Foo.ax2]

open Foo in
theorem ex4 : f (g (g (g x))) = x := by
  simp

theorem ex5 : f (g (g (g x))) = x ∧ f (g x) = x := by
  apply And.intro
  { simp; open Foo in simp }
  { simp; open Foo in simp }


/-
Copyright (c) 2020 Kevin Kappelmann. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Kappelmann
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.continued_fractions.computation.translations
import Mathlib.algebra.continued_fractions.terminated_stable
import Mathlib.algebra.continued_fractions.continuants_recurrence
import Mathlib.order.filter.at_top_bot
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Correctness of Terminating Continued Fraction Computations (`gcf.of`)

## Summary

Let us write `gcf` for `generalized_continued_fraction`. We show the correctness of the
algorithm computing continued fractions (`gcf.of`) in case of termination in the following sense:

At every step `n : ℕ`, we can obtain the value `v` by adding a specific residual term to the last
denominator of the fraction described by `(gcf.of v).convergents' n`. The residual term will be zero
exactly when the continued fraction terminated; otherwise, the residual term will be given by the
fractional part stored in `gcf.int_fract_pair.stream v n`.

For an example, refer to `gcf.comp_exact_value_correctness_of_stream_eq_some` and for more
information about the computation process, refer to `algebra.continued_fraction.computation.basic`.

## Main definitions

- `gcf.comp_exact_value` can be used to compute the exact value approximated by the continued
  fraction `gcf.of v` by adding a residual term as described in the summary.

## Main Theorems

- `gcf.comp_exact_value_correctness_of_stream_eq_some` shows that `gcf.comp_exact_value` indeed
  returns the value `v` when given the convergent and fractional part as described in the summary.
- `gcf.of_correctness_of_terminated_at` shows the equality `v = (gcf.of v).convergents n`
  if `gcf.of v` terminated at position `n`.
-/

namespace generalized_continued_fraction

/--
Given two continuants `pconts` and `conts` and a value `fr`, this function returns
- `conts.a / conts.b` if `fr = 0`
- `exact_conts.a / exact_conts.b` where `exact_conts = next_continuants 1 fr⁻¹ pconts conts` otherwise.

This function can be used to compute the exact value approxmated by a continued fraction `gcf.of v`
as described in lemma `comp_exact_value_correctness_of_stream_eq_some`.
-/
-- if the fractional part is zero, we exactly approximated the value by the last continuants

protected def comp_exact_value {K : Type u_1} [linear_ordered_field K] (pconts : pair K)
    (conts : pair K) (fr : K) : K :=
  ite (fr = 0) (pair.a conts / pair.b conts)
    (let exact_conts : pair K := next_continuants 1 (fr⁻¹) pconts conts;
    pair.a exact_conts / pair.b exact_conts)

-- otherwise, we have to include the fractional part in a final continuants step.

/-- Just a computational lemma we need for the next main proof. -/
protected theorem comp_exact_value_correctness_of_stream_eq_some_aux_comp {K : Type u_1}
    [linear_ordered_field K] [floor_ring K] {a : K} (b : K) (c : K)
    (fract_a_ne_zero : fract a ≠ 0) : (↑(floor a) * b + c) / fract a + b = (b * a + c) / fract a :=
  sorry

/--
Shows the correctness of `comp_exact_value` in case the continued fraction `gcf.of v` did not
terminate at position `n`. That is, we obtain the value `v` if we pass the two successive
(auxiliary) continuants at positions `n` and `n + 1` as well as the fractional part at
`int_fract_pair.stream n` to `comp_exact_value`.

The correctness might be seen more readily if one uses `convergents'` to evaluate the continued
fraction. Here is an example to illustrate the idea:

Let `(v : ℚ) := 3.4`. We have
- `gcf.int_fract_pair.stream v 0 = some ⟨3, 0.4⟩`, and
- `gcf.int_fract_pair.stream v 1 = some ⟨2, 0.5⟩`.
Now `(gcf.of v).convergents' 1 = 3 + 1/2`, and our fractional term at position `2` is `0.5`. We hence
have `v = 3 + 1/(2 + 0.5) = 3 + 1/2.5 = 3.4`. This computation corresponds exactly to the one using
the recurrence equation in `comp_exact_value`.
-/
theorem comp_exact_value_correctness_of_stream_eq_some {K : Type u_1} [linear_ordered_field K]
    {v : K} {n : ℕ} [floor_ring K] {ifp_n : int_fract_pair K} :
    int_fract_pair.stream v n = some ifp_n →
        v =
          generalized_continued_fraction.comp_exact_value
            (continuants_aux (generalized_continued_fraction.of v) n)
            (continuants_aux (generalized_continued_fraction.of v) (n + 1))
            (int_fract_pair.fr ifp_n) :=
  sorry

/-- The convergent of `gcf.of v` at step `n - 1` is exactly `v` if the `int_fract_pair.stream` of
the corresponding continued fraction terminated at step `n`. -/
theorem of_correctness_of_nth_stream_eq_none {K : Type u_1} [linear_ordered_field K] {v : K} {n : ℕ}
    [floor_ring K] (nth_stream_eq_none : int_fract_pair.stream v n = none) :
    v = convergents (generalized_continued_fraction.of v) (n - 1) :=
  sorry

/-- If `gcf.of v` terminated at step `n`, then the `n`th convergent is exactly `v`. -/
theorem of_correctness_of_terminated_at {K : Type u_1} [linear_ordered_field K] {v : K} {n : ℕ}
    [floor_ring K] (terminated_at_n : terminated_at (generalized_continued_fraction.of v) n) :
    v = convergents (generalized_continued_fraction.of v) n :=
  (fun (this : int_fract_pair.stream v (n + 1) = none) => of_correctness_of_nth_stream_eq_none this)
    (iff.elim_left of_terminated_at_n_iff_succ_nth_int_fract_pair_stream_eq_none terminated_at_n)

/-- If `gcf.of v` terminates, then there is `n : ℕ` such that the `n`th convergent is exactly `v`. -/
theorem of_correctness_of_terminates {K : Type u_1} [linear_ordered_field K] {v : K} [floor_ring K]
    (terminates : terminates (generalized_continued_fraction.of v)) :
    ∃ (n : ℕ), v = convergents (generalized_continued_fraction.of v) n :=
  exists.elim terminates
    fun (n : ℕ) (terminated_at_n : seq.terminated_at (s (generalized_continued_fraction.of v)) n) =>
      exists.intro n (of_correctness_of_terminated_at terminated_at_n)

/-- If `gcf.of v` terminates, then its convergents will eventually always be `v`. -/
theorem of_correctness_at_top_of_terminates {K : Type u_1} [linear_ordered_field K] {v : K}
    [floor_ring K] (terminates : terminates (generalized_continued_fraction.of v)) :
    filter.eventually (fun (n : ℕ) => v = convergents (generalized_continued_fraction.of v) n)
        filter.at_top :=
  sorry

end Mathlib


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Heather Macbeth, Sébastien Gouëzel
-/
import analysis.calculus.times_cont_diff
import analysis.normed_space.banach
import topology.local_homeomorph
import topology.metric_space.contracting

/-!
# Inverse function theorem

In this file we prove the inverse function theorem. It says that if a map `f : E → F`
has an invertible strict derivative `f'` at `a`, then it is locally invertible,
and the inverse function has derivative `f' ⁻¹`.

We define `has_strict_deriv_at.to_local_homeomorph` that repacks a function `f`
with a `hf : has_strict_fderiv_at f f' a`, `f' : E ≃L[𝕜] F`, into a `local_homeomorph`.
The `to_fun` of this `local_homeomorph` is `defeq` to `f`, so one can apply theorems
about `local_homeomorph` to `hf.to_local_homeomorph f`, and get statements about `f`.

Then we define `has_strict_fderiv_at.local_inverse` to be the `inv_fun` of this `local_homeomorph`,
and prove two versions of the inverse function theorem:

* `has_strict_fderiv_at.to_local_inverse`: if `f` has an invertible derivative `f'` at `a` in the
  strict sense (`hf`), then `hf.local_inverse f f' a` has derivative `f'.symm` at `f a` in the
  strict sense;

* `has_strict_fderiv_at.to_local_left_inverse`: if `f` has an invertible derivative `f'` at `a` in
  the strict sense and `g` is locally left inverse to `f` near `a`, then `g` has derivative
  `f'.symm` at `f a` in the strict sense.

In the one-dimensional case we reformulate these theorems in terms of `has_strict_deriv_at` and
`f'⁻¹`.

We also reformulate the theorems in terms of `times_cont_diff`, to give that `C^k` (respectively,
smooth) inputs give `C^k` (smooth) inverses.  These versions require that continuous
differentiability implies strict differentiability; this is false over a general field, true over
`ℝ` or `ℂ` and implemented here assuming `is_R_or_C 𝕂`.

Some related theorems, providing the derivative and higher regularity assuming that we already know
the inverse function, are formulated in `fderiv.lean`, `deriv.lean`, and `times_cont_diff.lean`.

## Notations

In the section about `approximates_linear_on` we introduce some `local notation` to make formulas
shorter:

* by `N` we denote `∥f'⁻¹∥`;
* by `g` we denote the auxiliary contracting map `x ↦ x + f'.symm (y - f x)` used to prove that
  `{x | f x = y}` is nonempty.

## Tags

derivative, strictly differentiable, continuously differentiable, smooth, inverse function
-/

open function set filter metric
open_locale topological_space classical nnreal

noncomputable theory

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E]
variables {F : Type*} [normed_group F] [normed_space 𝕜 F]
variables {G : Type*} [normed_group G] [normed_space 𝕜 G]
variables {G' : Type*} [normed_group G'] [normed_space 𝕜 G']
variables {ε : ℝ}

open asymptotics filter metric set
open continuous_linear_map (id)

/-!
### Non-linear maps close to affine maps

In this section we study a map `f` such that `∥f x - f y - f' (x - y)∥ ≤ c * ∥x - y∥` on an open set
`s`, where `f' : E →L[𝕜] F` is a continuous linear map and `c` is suitably small. Maps of this type
behave like `f a + f' (x - a)` near each `a ∈ s`.

When `f'` is onto, we show that `f` is locally onto.

When `f'` is a continuous linear equiv, we show that `f` is a homeomorphism
between `s` and `f '' s`. More precisely, we define `approximates_linear_on.to_local_homeomorph` to
be a `local_homeomorph` with `to_fun = f`, `source = s`, and `target = f '' s`.

Maps of this type naturally appear in the proof of the inverse function theorem (see next section),
and `approximates_linear_on.to_local_homeomorph` will imply that the locally inverse function
exists.

We define this auxiliary notion to split the proof of the inverse function theorem into small
lemmas. This approach makes it possible

- to prove a lower estimate on the size of the domain of the inverse function;

- to reuse parts of the proofs in the case if a function is not strictly differentiable. E.g., for a
  function `f : E × F → G` with estimates on `f x y₁ - f x y₂` but not on `f x₁ y - f x₂ y`.
-/

/-- We say that `f` approximates a continuous linear map `f'` on `s` with constant `c`,
if `∥f x - f y - f' (x - y)∥ ≤ c * ∥x - y∥` whenever `x, y ∈ s`.

This predicate is defined to facilitate the splitting of the inverse function theorem into small
lemmas. Some of these lemmas can be useful, e.g., to prove that the inverse function is defined
on a specific set. -/
def approximates_linear_on (f : E → F) (f' : E →L[𝕜] F) (s : set E) (c : ℝ≥0) : Prop :=
∀ (x ∈ s) (y ∈ s), ∥f x - f y - f' (x - y)∥ ≤ c * ∥x - y∥

namespace approximates_linear_on

variables [cs : complete_space E] {f : E → F}

/-! First we prove some properties of a function that `approximates_linear_on` a (not necessarily
invertible) continuous linear map. -/

section

variables {f' : E →L[𝕜] F} {s t : set E} {c c' : ℝ≥0}

theorem mono_num (hc : c ≤ c') (hf : approximates_linear_on f f' s c) :
  approximates_linear_on f f' s c' :=
λ x hx y hy, le_trans (hf x hx y hy) (mul_le_mul_of_nonneg_right hc $ norm_nonneg _)

theorem mono_set (hst : s ⊆ t) (hf : approximates_linear_on f f' t c) :
  approximates_linear_on f f' s c :=
λ x hx y hy, hf x (hst hx) y (hst hy)

lemma lipschitz_sub (hf : approximates_linear_on f f' s c) :
  lipschitz_with c (λ x : s, f x - f' x) :=
begin
  refine lipschitz_with.of_dist_le_mul (λ x y, _),
  rw [dist_eq_norm, subtype.dist_eq, dist_eq_norm],
  convert hf x x.2 y y.2 using 2,
  rw [f'.map_sub], abel
end

protected lemma lipschitz (hf : approximates_linear_on f f' s c) :
  lipschitz_with (nnnorm f' + c) (s.restrict f) :=
by simpa only [restrict_apply, add_sub_cancel'_right]
  using (f'.lipschitz.restrict s).add hf.lipschitz_sub

protected lemma continuous (hf : approximates_linear_on f f' s c) :
  continuous (s.restrict f) :=
hf.lipschitz.continuous

protected lemma continuous_on (hf : approximates_linear_on f f' s c) :
  continuous_on f s :=
continuous_on_iff_continuous_restrict.2 hf.continuous

end

section locally_onto
/-!
We prove that a function which is linearly approximated by a continuous linear map with a nonlinear
right inverse is locally onto. This will apply to the case where the approximating map is a linear
equivalence, for the local inverse theorem, but also whenever the approximating map is onto,
by Banach's open mapping theorem. -/

include cs

variables {s : set E} {c : ℝ≥0} {f' : E →L[𝕜] F}

/-- If a function is linearly approximated by a continuous linear map with a (possibly nonlinear)
right inverse, then it is locally onto: a ball of an explicit radius is included in the image
of the map. -/
theorem surj_on_closed_ball_of_nonlinear_right_inverse
  (hf : approximates_linear_on f f' s c)  (f'symm : f'.nonlinear_right_inverse)
  {ε : ℝ} {b : E} (ε0 : 0 ≤ ε) (hε : closed_ball b ε ⊆ s) :
  surj_on f (closed_ball b ε) (closed_ball (f b) (((f'symm.nnnorm : ℝ)⁻¹ - c) * ε)) :=
begin
  assume y hy,
  cases le_or_lt (f'symm.nnnorm : ℝ) ⁻¹ c with hc hc,
  { refine ⟨b, by simp [ε0], _⟩,
    have : dist y (f b) ≤ 0 :=
      (mem_closed_ball.1 hy).trans (mul_nonpos_of_nonpos_of_nonneg (by linarith) ε0),
    simp only [dist_le_zero] at this,
    rw this },
  have If' : (0 : ℝ) < f'symm.nnnorm,
    by { rw [← inv_pos], exact (nnreal.coe_nonneg _).trans_lt hc },
  have Icf' : (c : ℝ) * f'symm.nnnorm < 1, by rwa [inv_eq_one_div, lt_div_iff If'] at hc,
  have Jf' : (f'symm.nnnorm : ℝ) ≠ 0 := ne_of_gt If',
  have Jcf' : (1 : ℝ) - c * f'symm.nnnorm ≠ 0, by { apply ne_of_gt, linarith },
  /- We have to show that `y` can be written as `f x` for some `x ∈ closed_ball b ε`.
  The idea of the proof is to apply the Banach contraction principle to the map
  `g : x ↦ x + f'symm (y - f x)`, as a fixed point of this map satisfies `f x = y`.
  When `f'symm` is a genuine linear inverse, `g` is a contracting map. In our case, since `f'symm`
  is nonlinear, this map is not contracting (it is not even continuous), but still the proof of
  the contraction theorem holds: `uₙ = gⁿ b` is a Cauchy sequence, converging exponentially fast
  to the desired point `x`. Instead of appealing to general results, we check this by hand.

  The main point is that `f (u n)` becomes exponentially close to `y`, and therefore
  `dist (u (n+1)) (u n)` becomes exponentally small, making it possible to get an inductive
  bound on `dist (u n) b`, from which one checks that `u n` stays in the ball on which one has a
  control. Therefore, the bound can be checked at the next step, and so on inductively.
  -/
  set g := λ x, x + f'symm (y - f x) with hg,
  set u := λ (n : ℕ), g ^[n] b with hu,
  have usucc : ∀ n, u (n + 1) = g (u n), by simp [hu, ← iterate_succ_apply' g _ b],
  -- First bound: if `f z` is close to `y`, then `g z` is close to `z` (i.e., almost a fixed point).
  have A : ∀ z, dist (g z) z ≤ f'symm.nnnorm * dist (f z) y,
  { assume z,
    rw [dist_eq_norm, hg, add_sub_cancel', dist_eq_norm'],
    exact f'symm.bound _ },
  -- Second bound: if `z` and `g z` are in the set with good control, then `f (g z)` becomes closer
  -- to `y` than `f z` was (this uses the linear approximation property, and is the reason for the
  -- choice of the formula for `g`).
  have B : ∀ z ∈ closed_ball b ε, g z ∈ closed_ball b ε →
    dist (f (g z)) y ≤ c * f'symm.nnnorm * dist (f z) y,
  { assume z hz hgz,
    set v := f'symm (y - f z) with hv,
    calc dist (f (g z)) y = ∥f (z + v) - y∥ : by rw [dist_eq_norm]
    ... = ∥f (z + v) - f  z - f' v + f' v - (y - f z)∥ : by { congr' 1, abel }
    ... = ∥f (z + v) - f z - f' ((z + v) - z)∥ :
      by simp only [continuous_linear_map.nonlinear_right_inverse.right_inv,
                    add_sub_cancel', sub_add_cancel]
    ... ≤ c * ∥(z + v) - z∥ : hf _ (hε hgz) _ (hε hz)
    ... ≤ c * (f'symm.nnnorm * dist (f z) y) : begin
      apply mul_le_mul_of_nonneg_left _ (nnreal.coe_nonneg c),
      simpa [hv, dist_eq_norm'] using f'symm.bound (y - f z),
    end
    ... = c * f'symm.nnnorm * dist (f z) y : by ring },
  -- Third bound: a complicated bound on `dist w b` (that will show up in the induction) is enough
  -- to check that `w` is in the ball on which one has controls. Will be used to check that `u n`
  -- belongs to this ball for all `n`.
  have C : ∀ (n : ℕ) (w : E),
    dist w b ≤ f'symm.nnnorm * (1 - (c * f'symm.nnnorm)^n) / (1 - c * f'symm.nnnorm) * dist (f b) y
    → w ∈ closed_ball b ε,
  { assume n w hw,
    apply hw.trans,
    rw [div_mul_eq_mul_div, div_le_iff], swap, { linarith },
    calc (f'symm.nnnorm : ℝ) * (1 - (c * f'symm.nnnorm) ^ n) * dist (f b) y
      = f'symm.nnnorm * dist (f b) y * (1 - (c * f'symm.nnnorm) ^ n) : by ring
      ... ≤ f'symm.nnnorm * dist (f b) y * 1 :
      begin
        apply mul_le_mul_of_nonneg_left _ (mul_nonneg (nnreal.coe_nonneg _) dist_nonneg),
        rw [sub_le_self_iff],
        exact pow_nonneg (mul_nonneg (nnreal.coe_nonneg _) (nnreal.coe_nonneg _)) _,
      end
    ... ≤ f'symm.nnnorm * (((f'symm.nnnorm : ℝ)⁻¹ - c) * ε) :
      by { rw [mul_one],
           exact mul_le_mul_of_nonneg_left (mem_closed_ball'.1 hy) (nnreal.coe_nonneg _) }
    ... = ε * (1 - c * f'symm.nnnorm) : by { field_simp, ring } },
  /- Main inductive control: `f (u n)` becomes exponentially close to `y`, and therefore
  `dist (u (n+1)) (u n)` becomes exponentally small, making it possible to get an inductive
  bound on `dist (u n) b`, from which one checks that `u n` remains in the ball on which we
  have estimates. -/
  have D : ∀ (n : ℕ), dist (f (u n)) y ≤ (c * f'symm.nnnorm)^n * dist (f b) y
    ∧ dist (u n) b ≤ f'symm.nnnorm * (1 - (c * f'symm.nnnorm)^n) / (1 - c * f'symm.nnnorm)
      * dist (f b) y,
  { assume n,
    induction n with n IH, { simp [hu, le_refl] },
    rw usucc,
    have Ign : dist (g (u n)) b ≤
      f'symm.nnnorm * (1 - (c * f'symm.nnnorm)^n.succ) / (1 - c * f'symm.nnnorm) * dist (f b) y :=
    calc
      dist (g (u n)) b ≤ dist (g (u n)) (u n) + dist (u n) b : dist_triangle _ _ _
      ... ≤ f'symm.nnnorm * dist (f (u n)) y + dist (u n) b : add_le_add (A _) (le_refl _)
      ... ≤ f'symm.nnnorm * ((c * f'symm.nnnorm)^n * dist (f b) y) +
        f'symm.nnnorm * (1 - (c * f'symm.nnnorm)^n) / (1 - c * f'symm.nnnorm) * dist (f b) y :
          add_le_add (mul_le_mul_of_nonneg_left IH.1 (nnreal.coe_nonneg _)) IH.2
      ... = f'symm.nnnorm * (1 - (c * f'symm.nnnorm)^n.succ) / (1 - c * f'symm.nnnorm)
        * dist (f b) y : by { field_simp [Jcf'], ring_exp },
    refine ⟨_, Ign⟩,
    calc dist (f (g (u n))) y ≤ c * f'symm.nnnorm * dist (f (u n)) y :
      B _ (C n _ IH.2) (C n.succ _ Ign)
    ... ≤ (c * f'symm.nnnorm) * ((c * f'symm.nnnorm)^n * dist (f b) y) :
      mul_le_mul_of_nonneg_left IH.1 (mul_nonneg (nnreal.coe_nonneg _) (nnreal.coe_nonneg _))
    ... = (c * f'symm.nnnorm) ^ n.succ * dist (f b) y : by ring_exp },
  -- Deduce from the inductive bound that `uₙ` is a Cauchy sequence, therefore converging.
  have : cauchy_seq u,
  { have : ∀ (n : ℕ), dist (u n) (u (n+1)) ≤ f'symm.nnnorm * dist (f b) y * (c * f'symm.nnnorm)^n,
    { assume n,
      calc dist (u n) (u (n+1)) = dist (g (u n)) (u n) :  by rw [usucc, dist_comm]
      ... ≤ f'symm.nnnorm * dist (f (u n)) y : A _
      ... ≤ f'symm.nnnorm * ((c * f'symm.nnnorm)^n * dist (f b) y) :
        mul_le_mul_of_nonneg_left (D n).1 (nnreal.coe_nonneg _)
      ... = f'symm.nnnorm * dist (f b) y * (c * f'symm.nnnorm)^n : by ring },
    exact cauchy_seq_of_le_geometric _ _ Icf' this },
  obtain ⟨x, hx⟩ : ∃ x, tendsto u at_top (𝓝 x) := cauchy_seq_tendsto_of_complete this,
  -- As all the `uₙ` belong to the ball `closed_ball b ε`, so does their limit `x`.
  have xmem : x ∈ closed_ball b ε :=
    is_closed_ball.mem_of_tendsto hx (eventually_of_forall (λ n, C n _ (D n).2)),
  refine ⟨x, xmem, _⟩,
  -- It remains to check that `f x = y`. This follows from continuity of `f` on `closed_ball b ε`
  -- and from the fact that `f uₙ` is converging to `y` by construction.
  have hx' : tendsto u at_top (𝓝[closed_ball b ε] x),
  { simp only [nhds_within, tendsto_inf, hx, true_and, ge_iff_le, tendsto_principal],
    exact eventually_of_forall (λ n, C n _ (D n).2) },
  have T1 : tendsto (λ n, f (u n)) at_top (𝓝 (f x)) :=
    (hf.continuous_on.mono hε x xmem).tendsto.comp hx',
  have T2 : tendsto (λ n, f (u n)) at_top (𝓝 y),
  { rw tendsto_iff_dist_tendsto_zero,
    refine squeeze_zero (λ n, dist_nonneg) (λ n, (D n).1) _,
    simpa using (tendsto_pow_at_top_nhds_0_of_lt_1
      (mul_nonneg (nnreal.coe_nonneg _) (nnreal.coe_nonneg _)) Icf').mul tendsto_const_nhds },
  exact tendsto_nhds_unique T1 T2,
end

lemma open_image (hf : approximates_linear_on f f' s c)  (f'symm : f'.nonlinear_right_inverse)
  (hs : is_open s) (hc : subsingleton F ∨ c < f'symm.nnnorm⁻¹) : is_open (f '' s) :=
begin
  cases hc with hE hc, { resetI, apply is_open_discrete },
  simp only [is_open_iff_mem_nhds, nhds_basis_closed_ball.mem_iff, ball_image_iff] at hs ⊢,
  intros x hx,
  rcases hs x hx with ⟨ε, ε0, hε⟩,
  refine ⟨(f'symm.nnnorm⁻¹ - c) * ε, mul_pos (sub_pos.2 hc) ε0, _⟩,
  exact (hf.surj_on_closed_ball_of_nonlinear_right_inverse f'symm (le_of_lt ε0) hε).mono
    hε (subset.refl _)
end

lemma image_mem_nhds (hf : approximates_linear_on f f' s c) (f'symm : f'.nonlinear_right_inverse)
  {x : E} (hs : s ∈ 𝓝 x) (hc : subsingleton F ∨ c < f'symm.nnnorm⁻¹) :
  f '' s ∈ 𝓝 (f x) :=
begin
  obtain ⟨t, hts, ht, xt⟩ : ∃ t ⊆ s, is_open t ∧ x ∈ t := mem_nhds_sets_iff.1 hs,
  have := mem_nhds_sets ((hf.mono_set hts).open_image f'symm ht hc) (mem_image_of_mem _ xt),
  exact mem_sets_of_superset this (image_subset _ hts),
end

lemma map_nhds_eq (hf : approximates_linear_on f f' s c) (f'symm : f'.nonlinear_right_inverse)
  {x : E} (hs : s ∈ 𝓝 x) (hc : subsingleton F ∨ c < f'symm.nnnorm⁻¹) :
  map f (𝓝 x) = 𝓝 (f x) :=
begin
  refine le_antisymm ((hf.continuous_on x (mem_of_nhds hs)).continuous_at hs) (le_map (λ t ht, _)),
  have : f '' (s ∩ t) ∈ 𝓝 (f x) := (hf.mono_set (inter_subset_left s t)).image_mem_nhds
    f'symm (inter_mem_sets hs ht) hc,
  exact mem_sets_of_superset this (image_subset _ (inter_subset_right _ _)),
end

end locally_onto

/-!
From now on we assume that `f` approximates an invertible continuous linear map `f : E ≃L[𝕜] F`.

We also assume that either `E = {0}`, or `c < ∥f'⁻¹∥⁻¹`. We use `N` as an abbreviation for `∥f'⁻¹∥`.
-/

variables {f' : E ≃L[𝕜] F} {s : set E} {c : ℝ≥0}

local notation `N` := nnnorm (f'.symm : F →L[𝕜] E)

protected lemma antilipschitz (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) :
  antilipschitz_with (N⁻¹ - c)⁻¹ (s.restrict f) :=
begin
  cases hc with hE hc,
  { haveI : subsingleton s := ⟨λ x y, subtype.eq $ @subsingleton.elim _ hE _ _⟩,
    exact antilipschitz_with.of_subsingleton },
  convert (f'.antilipschitz.restrict s).add_lipschitz_with hf.lipschitz_sub hc,
  simp [restrict]
end

protected lemma injective (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) :
  injective (s.restrict f) :=
(hf.antilipschitz hc).injective

protected lemma inj_on (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) :
  inj_on f s :=
inj_on_iff_injective.2 $ hf.injective hc

/-- A map approximating a linear equivalence on a set defines a local equivalence on this set.
Should not be used outside of this file, because it is superseded by `to_local_homeomorph` below.

This is a first step towards the inverse function. -/
def to_local_equiv (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) : local_equiv E F :=
(hf.inj_on hc).to_local_equiv _ _

/-- The inverse function is continuous on `f '' s`. Use properties of `local_homeomorph` instead. -/
lemma inverse_continuous_on (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) :
  continuous_on (hf.to_local_equiv hc).symm (f '' s) :=
begin
  apply continuous_on_iff_continuous_restrict.2,
  refine ((hf.antilipschitz hc).to_right_inv_on' _ (hf.to_local_equiv hc).right_inv').continuous,
  exact (λ x hx, (hf.to_local_equiv hc).map_target hx)
end

include cs

section
variables (f s)

/-- Given a function `f` that approximates a linear equivalence on an open set `s`,
returns a local homeomorph with `to_fun = f` and `source = s`. -/
def to_local_homeomorph (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) (hs : is_open s) : local_homeomorph E F :=
{ to_local_equiv := hf.to_local_equiv hc,
  open_source := hs,
  open_target := hf.open_image f'.to_nonlinear_right_inverse hs
    (by rwa f'.to_linear_equiv.to_equiv.subsingleton_iff at hc),
  continuous_to_fun := hf.continuous_on,
  continuous_inv_fun := hf.inverse_continuous_on hc }

end

@[simp] lemma to_local_homeomorph_coe (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) (hs : is_open s) :
  (hf.to_local_homeomorph f s hc hs : E → F) = f := rfl

@[simp] lemma to_local_homeomorph_source (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) (hs : is_open s) :
  (hf.to_local_homeomorph f s hc hs).source = s := rfl

@[simp] lemma to_local_homeomorph_target (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) (hs : is_open s) :
  (hf.to_local_homeomorph f s hc hs).target = f '' s := rfl

lemma closed_ball_subset_target (hf : approximates_linear_on f (f' : E →L[𝕜] F) s c)
  (hc : subsingleton E ∨ c < N⁻¹) (hs : is_open s) {b : E} (ε0 : 0 ≤ ε) (hε : closed_ball b ε ⊆ s) :
  closed_ball (f b) ((N⁻¹ - c) * ε) ⊆ (hf.to_local_homeomorph f s hc hs).target :=
(hf.surj_on_closed_ball_of_nonlinear_right_inverse f'.to_nonlinear_right_inverse
  ε0 hε).mono hε (subset.refl _)

end approximates_linear_on

/-!
### Inverse function theorem

Now we prove the inverse function theorem. Let `f : E → F` be a map defined on a complete vector
space `E`. Assume that `f` has an invertible derivative `f' : E ≃L[𝕜] F` at `a : E` in the strict
sense. Then `f` approximates `f'` in the sense of `approximates_linear_on` on an open neighborhood
of `a`, and we can apply `approximates_linear_on.to_local_homeomorph` to construct the inverse
function. -/

namespace has_strict_fderiv_at

/-- If `f` has derivative `f'` at `a` in the strict sense and `c > 0`, then `f` approximates `f'`
with constant `c` on some neighborhood of `a`. -/
lemma approximates_deriv_on_nhds {f : E → F} {f' : E →L[𝕜] F} {a : E}
  (hf : has_strict_fderiv_at f f' a) {c : ℝ≥0} (hc : subsingleton E ∨ 0 < c) :
  ∃ s ∈ 𝓝 a, approximates_linear_on f f' s c :=
begin
  cases hc with hE hc,
  { refine ⟨univ, mem_nhds_sets is_open_univ trivial, λ x hx y hy, _⟩,
    simp [@subsingleton.elim E hE x y] },
  have := hf.def hc,
  rw [nhds_prod_eq, filter.eventually, mem_prod_same_iff] at this,
  rcases this with ⟨s, has, hs⟩,
  exact ⟨s, has, λ x hx y hy, hs (mk_mem_prod hx hy)⟩
end

lemma map_nhds_eq_of_surj [complete_space E] [complete_space F]
  {f : E → F} {f' : E →L[𝕜] F} {a : E}
  (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) (h : f'.range = ⊤) :
  map f (𝓝 a) = 𝓝 (f a) :=
begin
  let f'symm := f'.nonlinear_right_inverse_of_surjective h,
  set c : ℝ≥0 := f'symm.nnnorm⁻¹ / 2 with hc,
  have f'symm_pos : 0 < f'symm.nnnorm := f'.nonlinear_right_inverse_of_surjective_nnnorm_pos h,
  have cpos : 0 < c, by simp [hc, nnreal.half_pos, nnreal.inv_pos, f'symm_pos],
  obtain ⟨s, s_nhds, hs⟩ : ∃ s ∈ 𝓝 a, approximates_linear_on f f' s c :=
    hf.approximates_deriv_on_nhds (or.inr cpos),
  apply hs.map_nhds_eq f'symm s_nhds (or.inr (nnreal.half_lt_self _)),
  simp [ne_of_gt f'symm_pos],
end

variables [cs : complete_space E] {f : E → F} {f' : E ≃L[𝕜] F} {a : E}

lemma approximates_deriv_on_open_nhds (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  ∃ (s : set E) (hs : a ∈ s ∧ is_open s),
    approximates_linear_on f (f' : E →L[𝕜] F) s ((nnnorm (f'.symm : F →L[𝕜] E))⁻¹ / 2) :=
begin
  refine ((nhds_basis_opens a).exists_iff _).1 _,
  exact (λ s t, approximates_linear_on.mono_set),
  exact (hf.approximates_deriv_on_nhds $ f'.subsingleton_or_nnnorm_symm_pos.imp id $
    λ hf', nnreal.half_pos $ nnreal.inv_pos.2 $ hf')
end

include cs

variable (f)

/-- Given a function with an invertible strict derivative at `a`, returns a `local_homeomorph`
with `to_fun = f` and `a ∈ source`. This is a part of the inverse function theorem.
The other part `has_strict_fderiv_at.to_local_inverse` states that the inverse function
of this `local_homeomorph` has derivative `f'.symm`. -/
def to_local_homeomorph (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) : local_homeomorph E F :=
approximates_linear_on.to_local_homeomorph f
  (classical.some hf.approximates_deriv_on_open_nhds)
  (classical.some_spec hf.approximates_deriv_on_open_nhds).snd
  (f'.subsingleton_or_nnnorm_symm_pos.imp id $ λ hf', nnreal.half_lt_self $ ne_of_gt $
    nnreal.inv_pos.2 $ hf')
  (classical.some_spec hf.approximates_deriv_on_open_nhds).fst.2

variable {f}

@[simp] lemma to_local_homeomorph_coe (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  (hf.to_local_homeomorph f : E → F) = f := rfl

lemma mem_to_local_homeomorph_source (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  a ∈ (hf.to_local_homeomorph f).source :=
(classical.some_spec hf.approximates_deriv_on_open_nhds).fst.1

lemma image_mem_to_local_homeomorph_target (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  f a ∈ (hf.to_local_homeomorph f).target :=
(hf.to_local_homeomorph f).map_source hf.mem_to_local_homeomorph_source

lemma map_nhds_eq_of_equiv (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  map f (𝓝 a) = 𝓝 (f a) :=
(hf.to_local_homeomorph f).map_nhds_eq hf.mem_to_local_homeomorph_source

variables (f f' a)

/-- Given a function `f` with an invertible derivative, returns a function that is locally inverse
to `f`. -/
def local_inverse (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) : F → E :=
(hf.to_local_homeomorph f).symm

variables {f f' a}

lemma local_inverse_def (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  hf.local_inverse f _ _ = (hf.to_local_homeomorph f).symm :=
rfl

lemma eventually_left_inverse (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  ∀ᶠ x in 𝓝 a, hf.local_inverse f f' a (f x) = x :=
(hf.to_local_homeomorph f).eventually_left_inverse hf.mem_to_local_homeomorph_source

@[simp]

lemma eventually_right_inverse (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  ∀ᶠ y in 𝓝 (f a), f (hf.local_inverse f f' a y) = y :=
(hf.to_local_homeomorph f).eventually_right_inverse' hf.mem_to_local_homeomorph_source

lemma local_inverse_continuous_at (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  continuous_at (hf.local_inverse f f' a) (f a) :=
(hf.to_local_homeomorph f).continuous_at_symm hf.image_mem_to_local_homeomorph_target

lemma local_inverse_tendsto (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  tendsto (hf.local_inverse f f' a) (𝓝 $ f a) (𝓝 a) :=
(hf.to_local_homeomorph f).tendsto_symm hf.mem_to_local_homeomorph_source

lemma local_inverse_unique (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) {g : F → E}
  (hg : ∀ᶠ x in 𝓝 a, g (f x) = x) :
  ∀ᶠ y in 𝓝 (f a), g y = local_inverse f f' a hf y :=
eventually_eq_of_left_inv_of_right_inv hg hf.eventually_right_inverse $
  (hf.to_local_homeomorph f).tendsto_symm hf.mem_to_local_homeomorph_source

/-- If `f` has an invertible derivative `f'` at `a` in the sense of strict differentiability `(hf)`,
then the inverse function `hf.local_inverse f` has derivative `f'.symm` at `f a`. -/
theorem to_local_inverse (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) :
  has_strict_fderiv_at (hf.local_inverse f f' a) (f'.symm : F →L[𝕜] E) (f a) :=
(hf.to_local_homeomorph f).has_strict_fderiv_at_symm hf.image_mem_to_local_homeomorph_target $
  by simpa [← local_inverse_def] using hf

/-- If `f : E → F` has an invertible derivative `f'` at `a` in the sense of strict differentiability
and `g (f x) = x` in a neighborhood of `a`, then `g` has derivative `f'.symm` at `f a`.

For a version assuming `f (g y) = y` and continuity of `g` at `f a` but not `[complete_space E]`
see `of_local_left_inverse`.  -/
theorem to_local_left_inverse (hf : has_strict_fderiv_at f (f' : E →L[𝕜] F) a) {g : F → E}
  (hg : ∀ᶠ x in 𝓝 a, g (f x) = x) :
  has_strict_fderiv_at g (f'.symm : F →L[𝕜] E) (f a) :=
hf.to_local_inverse.congr_of_eventually_eq $ (hf.local_inverse_unique hg).mono $ λ _, eq.symm

end has_strict_fderiv_at

/-- If a function has an invertible strict derivative at all points, then it is an open map. -/
lemma open_map_of_strict_fderiv_equiv [complete_space E] {f : E → F} {f' : E → E ≃L[𝕜] F}
  (hf : ∀ x, has_strict_fderiv_at f (f' x : E →L[𝕜] F) x) :
  is_open_map f :=
is_open_map_iff_nhds_le.2 $ λ x, (hf x).map_nhds_eq_of_equiv.ge

/-!
### Inverse function theorem, 1D case

In this case we prove a version of the inverse function theorem for maps `f : 𝕜 → 𝕜`.
We use `continuous_linear_equiv.units_equiv_aut` to translate `has_strict_deriv_at f f' a` and
`f' ≠ 0` into `has_strict_fderiv_at f (_ : 𝕜 ≃L[𝕜] 𝕜) a`.
-/

namespace has_strict_deriv_at

variables [cs : complete_space 𝕜] {f : 𝕜 → 𝕜} {f' a : 𝕜} (hf : has_strict_deriv_at f f' a)
  (hf' : f' ≠ 0)

include cs

variables (f f' a)

/-- A function that is inverse to `f` near `a`. -/
@[reducible] def local_inverse : 𝕜 → 𝕜 :=
(hf.has_strict_fderiv_at_equiv hf').local_inverse _ _ _

variables {f f' a}

lemma map_nhds_eq : map f (𝓝 a) = 𝓝 (f a) :=
(hf.has_strict_fderiv_at_equiv hf').map_nhds_eq_of_equiv

theorem to_local_inverse : has_strict_deriv_at (hf.local_inverse f f' a hf') f'⁻¹ (f a) :=
(hf.has_strict_fderiv_at_equiv hf').to_local_inverse

theorem to_local_left_inverse {g : 𝕜 → 𝕜} (hg : ∀ᶠ x in 𝓝 a, g (f x) = x) :
  has_strict_deriv_at g f'⁻¹ (f a) :=
(hf.has_strict_fderiv_at_equiv hf').to_local_left_inverse hg

end has_strict_deriv_at

/-- If a function has a non-zero strict derivative at all points, then it is an open map. -/
lemma open_map_of_strict_deriv [complete_space 𝕜] {f f' : 𝕜 → 𝕜}
  (hf : ∀ x, has_strict_deriv_at f (f' x) x) (h0 : ∀ x, f' x ≠ 0) :
  is_open_map f :=
is_open_map_iff_nhds_le.2 $ λ x, ((hf x).map_nhds_eq (h0 x)).ge

/-!
### Inverse function theorem, smooth case

-/

namespace times_cont_diff_at
variables {𝕂 : Type*} [is_R_or_C 𝕂]
variables {E' : Type*} [normed_group E'] [normed_space 𝕂 E']
variables {F' : Type*} [normed_group F'] [normed_space 𝕂 F']
variables [complete_space E'] (f : E' → F') {f' : E' ≃L[𝕂] F'} {a : E'}

/-- Given a `times_cont_diff` function over `𝕂` (which is `ℝ` or `ℂ`) with an invertible
derivative at `a`, returns a `local_homeomorph` with `to_fun = f` and `a ∈ source`. -/
def to_local_homeomorph
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  local_homeomorph E' F' :=
(hf.has_strict_fderiv_at' hf' hn).to_local_homeomorph f

variable {f}

@[simp] lemma to_local_homeomorph_coe
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  (hf.to_local_homeomorph f hf' hn : E' → F') = f := rfl

lemma mem_to_local_homeomorph_source
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  a ∈ (hf.to_local_homeomorph f hf' hn).source :=
(hf.has_strict_fderiv_at' hf' hn).mem_to_local_homeomorph_source

lemma image_mem_to_local_homeomorph_target
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  f a ∈ (hf.to_local_homeomorph f hf' hn).target :=
(hf.has_strict_fderiv_at' hf' hn).image_mem_to_local_homeomorph_target

/-- Given a `times_cont_diff` function over `𝕂` (which is `ℝ` or `ℂ`) with an invertible derivative
at `a`, returns a function that is locally inverse to `f`. -/
def local_inverse
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  F' → E' :=
(hf.has_strict_fderiv_at' hf' hn).local_inverse f f' a

lemma local_inverse_apply_image
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  hf.local_inverse hf' hn (f a) = a :=
(hf.has_strict_fderiv_at' hf' hn).local_inverse_apply_image

/-- Given a `times_cont_diff` function over `𝕂` (which is `ℝ` or `ℂ`) with an invertible derivative
at `a`, the inverse function (produced by `times_cont_diff.to_local_homeomorph`) is
also `times_cont_diff`. -/
lemma to_local_inverse
  {n : with_top ℕ} (hf : times_cont_diff_at 𝕂 n f a) (hf' : has_fderiv_at f (f' : E' →L[𝕂] F') a)
  (hn : 1 ≤ n) :
  times_cont_diff_at 𝕂 n (hf.local_inverse hf' hn) (f a) :=
begin
  have := hf.local_inverse_apply_image hf' hn,
  apply (hf.to_local_homeomorph f hf' hn).times_cont_diff_at_symm
    (image_mem_to_local_homeomorph_target hf hf' hn),
  { convert hf' },
  { convert hf }
end

end times_cont_diff_at


import hilbert.wr.ka_bot
import hilbert.wr.proofs.ka

namespace clfrags
    namespace hilbert
        namespace wr
            namespace ka_bot

                theorem  kab₁_ka {a b c d e : Prop} (h₁ : ka d e (ka a b bot)) : ka d e (ka a b c) :=
                    have h₂ : ka d e (ka d b bot), from ka.ka₇ h₁,
                    have h₃ : ka d (ka d e b) bot, from ka.ka₄ h₂,
                    have h₄ : ka d (ka d e b) c, from kab₁ h₃,
                    have h₅ : ka d e a, from ka.ka₆ h₁,
                    have h₆ : ka d e (ka d b c), from ka.ka₄' h₄,
                    show ka d e (ka a b c), from ka.ka₅ h₅ h₆

                theorem  b₁ {a : Prop} (h₁ : bot) : a :=
                    have h₂ : ka bot bot bot, from ka.ka₁ h₁ h₁,
                    have h₃ : ka bot bot a, from kab₁ h₂,
                    have h₄ : ka bot a bot, from ka.ka₃ h₃,
                    have h₅ : ka bot a a, from kab₁ h₄,
                    show a, from ka.ka₂ h₅

            end ka_bot
        end wr
    end hilbert
end clfrags


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.function.basic
import Mathlib.PostPort

universes u_1 u_2 u_3

namespace Mathlib

/-!
# Semiconjugate and commuting maps

We define the following predicates:

* `function.semiconj`: `f : α → β` semiconjugates `ga : α → α` to `gb : β → β` if `f ∘ ga = gb ∘ f`;
* `function.semiconj₂: `f : α → β` semiconjugates a binary operation `ga : α → α → α`
  to `gb : β → β → β` if `f (ga x y) = gb (f x) (f y)`;
* `f : α → α` commutes with `g : α → α` if `f ∘ g = g ∘ f`, or equivalently `semiconj f g g`.

-/

namespace function

/-- We say that `f : α → β` semiconjugates `ga : α → α` to `gb : β → β` if `f ∘ ga = gb ∘ f`. -/
def semiconj {α : Type u_1} {β : Type u_2} (f : α → β) (ga : α → α) (gb : β → β) :=
  ∀ (x : α), f (ga x) = gb (f x)

namespace semiconj

protected theorem comp_eq {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α} {gb : β → β}
    (h : semiconj f ga gb) : f ∘ ga = gb ∘ f :=
  funext h

protected theorem eq {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α} {gb : β → β}
    (h : semiconj f ga gb) (x : α) : f (ga x) = gb (f x) :=
  h x

theorem comp_right {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α} {ga' : α → α} {gb : β → β}
    {gb' : β → β} (h : semiconj f ga gb) (h' : semiconj f ga' gb') :
    semiconj f (ga ∘ ga') (gb ∘ gb') :=
  sorry

theorem comp_left {α : Type u_1} {β : Type u_2} {γ : Type u_3} {fab : α → β} {fbc : β → γ}
    {ga : α → α} {gb : β → β} {gc : γ → γ} (hab : semiconj fab ga gb) (hbc : semiconj fbc gb gc) :
    semiconj (fbc ∘ fab) ga gc :=
  sorry

theorem id_right {α : Type u_1} {β : Type u_2} {f : α → β} : semiconj f id id := fun (_x : α) => rfl

theorem id_left {α : Type u_1} {ga : α → α} : semiconj id ga ga := fun (_x : α) => rfl

theorem inverses_right {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α} {ga' : α → α}
    {gb : β → β} {gb' : β → β} (h : semiconj f ga gb) (ha : right_inverse ga' ga)
    (hb : left_inverse gb' gb) : semiconj f ga' gb' :=
  sorry

end semiconj

/-- Two maps `f g : α → α` commute if `f ∘ g = g ∘ f`. -/
def commute {α : Type u_1} (f : α → α) (g : α → α) := semiconj f g g

theorem semiconj.commute {α : Type u_1} {f : α → α} {g : α → α} (h : semiconj f g g) :
    commute f g :=
  h

namespace commute

theorem refl {α : Type u_1} (f : α → α) : commute f f := fun (_x : α) => Eq.refl (f (f _x))

theorem symm {α : Type u_1} {f : α → α} {g : α → α} (h : commute f g) : commute g f :=
  fun (x : α) => Eq.symm (h x)

theorem comp_right {α : Type u_1} {f : α → α} {g : α → α} {g' : α → α} (h : commute f g)
    (h' : commute f g') : commute f (g ∘ g') :=
  semiconj.comp_right h h'

theorem comp_left {α : Type u_1} {f : α → α} {f' : α → α} {g : α → α} (h : commute f g)
    (h' : commute f' g) : commute (f ∘ f') g :=
  symm (comp_right (symm h) (symm h'))

theorem id_right {α : Type u_1} {f : α → α} : commute f id := semiconj.id_right

theorem id_left {α : Type u_1} {f : α → α} : commute id f := semiconj.id_left

end commute

/-- A map `f` semiconjugates a binary operation `ga` to a binary operation `gb` if
for all `x`, `y` we have `f (ga x y) = gb (f x) (f y)`. E.g., a `monoid_hom`
semiconjugates `(*)` to `(*)`. -/
def semiconj₂ {α : Type u_1} {β : Type u_2} (f : α → β) (ga : α → α → α) (gb : β → β → β) :=
  ∀ (x y : α), f (ga x y) = gb (f x) (f y)

namespace semiconj₂

protected theorem eq {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α} {gb : β → β → β}
    (h : semiconj₂ f ga gb) (x : α) (y : α) : f (ga x y) = gb (f x) (f y) :=
  h x y

protected theorem comp_eq {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} (h : semiconj₂ f ga gb) : bicompr f ga = bicompl gb f f :=
  funext fun (x : α) => funext (h x)

theorem id_left {α : Type u_1} (op : α → α → α) : semiconj₂ id op op := fun (_x _x_1 : α) => rfl

theorem comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} {f' : β → γ} {gc : γ → γ → γ} (hf' : semiconj₂ f' gb gc)
    (hf : semiconj₂ f ga gb) : semiconj₂ (f' ∘ f) ga gc :=
  sorry

theorem is_associative_right {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} [is_associative α ga] (h : semiconj₂ f ga gb) (h_surj : surjective f) :
    is_associative β gb :=
  sorry

theorem is_associative_left {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} [is_associative β gb] (h : semiconj₂ f ga gb) (h_inj : injective f) :
    is_associative α ga :=
  sorry

theorem is_idempotent_right {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} [is_idempotent α ga] (h : semiconj₂ f ga gb) (h_surj : surjective f) :
    is_idempotent β gb :=
  sorry

theorem is_idempotent_left {α : Type u_1} {β : Type u_2} {f : α → β} {ga : α → α → α}
    {gb : β → β → β} [is_idempotent β gb] (h : semiconj₂ f ga gb) (h_inj : injective f) :
    is_idempotent α ga :=
  sorry

end Mathlib