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

import algebraic_geometry.ringed_space
import algebraic_geometry.stalks
import logic.equiv.transfer_instance

/-!
# The category of locally ringed spaces

We define (bundled) locally ringed spaces (as `SheafedSpace CommRing` along with the fact that the
stalks are local rings), and morphisms between these (morphisms in `SheafedSpace` with
`is_local_ring_hom` on the stalk maps).
-/

universes v u

open category_theory
open Top
open topological_space
open opposite
open category_theory.category category_theory.functor

namespace algebraic_geometry

/-- A `LocallyRingedSpace` is a topological space equipped with a sheaf of commutative rings
such that all the stalks are local rings.

A morphism of locally ringed spaces is a morphism of ringed spaces
such that the morphisms induced on stalks are local ring homomorphisms. -/
@[nolint has_nonempty_instance]
structure LocallyRingedSpace extends SheafedSpace CommRing :=
(local_ring : ∀ x, local_ring (presheaf.stalk x))

attribute [instance] LocallyRingedSpace.local_ring

namespace LocallyRingedSpace

variables (X : LocallyRingedSpace)

/--
An alias for `to_SheafedSpace`, where the result type is a `RingedSpace`.
This allows us to use dot-notation for the `RingedSpace` namespace.
 -/
def to_RingedSpace : RingedSpace := X.to_SheafedSpace

/-- The underlying topological space of a locally ringed space. -/
def to_Top : Top := X.1.carrier

instance : has_coe_to_sort LocallyRingedSpace (Type u) :=
⟨λ X : LocallyRingedSpace, (X.to_Top : Type u)⟩

instance (x : X) : _root_.local_ring (X.to_PresheafedSpace.stalk x) := X.local_ring x

-- PROJECT: how about a typeclass "has_structure_sheaf" to mediate the 𝒪 notation, rather
-- than defining it over and over for PresheafedSpace, LRS, Scheme, etc.

/-- The structure sheaf of a locally ringed space. -/
def 𝒪 : sheaf CommRing X.to_Top := X.to_SheafedSpace.sheaf

/-- A morphism of locally ringed spaces is a morphism of ringed spaces
 such that the morphims induced on stalks are local ring homomorphisms. -/
@[ext]
structure hom (X Y : LocallyRingedSpace.{u}) : Type u :=
(val : X.to_SheafedSpace ⟶ Y.to_SheafedSpace)
(prop : ∀ x, is_local_ring_hom (PresheafedSpace.stalk_map val x))

instance : quiver LocallyRingedSpace := ⟨hom⟩

/--
The stalk of a locally ringed space, just as a `CommRing`.
-/
-- TODO perhaps we should make a bundled `LocalRing` and return one here?
-- TODO define `sheaf.stalk` so we can write `X.𝒪.stalk` here?
noncomputable
def stalk (X : LocallyRingedSpace) (x : X) : CommRing := X.presheaf.stalk x

/--
A morphism of locally ringed spaces `f : X ⟶ Y` induces
a local ring homomorphism from `Y.stalk (f x)` to `X.stalk x` for any `x : X`.
-/
noncomputable
def stalk_map {X Y : LocallyRingedSpace} (f : X ⟶ Y) (x : X) :
  Y.stalk (f.1.1 x) ⟶ X.stalk x :=
PresheafedSpace.stalk_map f.1 x

instance {X Y : LocallyRingedSpace} (f : X ⟶ Y) (x : X) :
  is_local_ring_hom (stalk_map f x) := f.2 x

instance {X Y : LocallyRingedSpace} (f : X ⟶ Y) (x : X) :
   is_local_ring_hom (PresheafedSpace.stalk_map f.1 x) := f.2 x

/-- The identity morphism on a locally ringed space. -/
@[simps]
def id (X : LocallyRingedSpace) : hom X X :=
⟨𝟙 _, λ x, by { erw PresheafedSpace.stalk_map.id, apply is_local_ring_hom_id, }⟩

instance (X : LocallyRingedSpace) : inhabited (hom X X) := ⟨id X⟩

/-- Composition of morphisms of locally ringed spaces. -/
def comp {X Y Z : LocallyRingedSpace} (f : hom X Y) (g : hom Y Z) : hom X Z :=
⟨f.val ≫ g.val, λ x,
begin
  erw PresheafedSpace.stalk_map.comp,
  exact @is_local_ring_hom_comp _ _ _ _ _ _ _ _ (f.2 _) (g.2 _),
end⟩

/-- The category of locally ringed spaces. -/
instance : category LocallyRingedSpace :=
{ hom := hom,
  id := id,
  comp := λ X Y Z f g, comp f g,
  comp_id' := by { intros, ext1, simp [comp], },
  id_comp' := by { intros, ext1, simp [comp], },
  assoc' := by { intros, ext1, simp [comp], }, }.

/-- The forgetful functor from `LocallyRingedSpace` to `SheafedSpace CommRing`. -/
@[simps] def forget_to_SheafedSpace : LocallyRingedSpace ⥤ SheafedSpace CommRing :=
{ obj := λ X, X.to_SheafedSpace,
  map := λ X Y f, f.1, }

instance : faithful forget_to_SheafedSpace := {}

/-- The forgetful functor from `LocallyRingedSpace` to `Top`. -/
@[simps]
def forget_to_Top : LocallyRingedSpace ⥤ Top :=
forget_to_SheafedSpace ⋙ SheafedSpace.forget _

@[simp] lemma comp_val {X Y Z : LocallyRingedSpace} (f : X ⟶ Y) (g : Y ⟶ Z) :
  (f ≫ g).val = f.val ≫ g.val := rfl

@[simp] lemma comp_val_c {X Y Z : LocallyRingedSpace.{u}} (f : X ⟶ Y) (g : Y ⟶ Z) :
  (f ≫ g).val.c = g.val.c ≫ (presheaf.pushforward _ g.val.base).map f.val.c := rfl

lemma comp_val_c_app {X Y Z : LocallyRingedSpace} (f : X ⟶ Y) (g : Y ⟶ Z) (U : (opens Z)ᵒᵖ) :
  (f ≫ g).val.c.app U = g.val.c.app U ≫ f.val.c.app (op $ (opens.map g.val.base).obj U.unop) :=
rfl

/--
Given two locally ringed spaces `X` and `Y`, an isomorphism between `X` and `Y` as _sheafed_
spaces can be lifted to a morphism `X ⟶ Y` as locally ringed spaces.

See also `iso_of_SheafedSpace_iso`.
-/
@[simps]
def hom_of_SheafedSpace_hom_of_is_iso {X Y : LocallyRingedSpace}
  (f : X.to_SheafedSpace ⟶ Y.to_SheafedSpace) [is_iso f] : X ⟶ Y :=
hom.mk f $ λ x,
-- Here we need to see that the stalk maps are really local ring homomorphisms.
-- This can be solved by type class inference, because stalk maps of isomorphisms are isomorphisms
-- and isomorphisms are local ring homomorphisms.
show is_local_ring_hom (PresheafedSpace.stalk_map
  (SheafedSpace.forget_to_PresheafedSpace.map f) x),
by apply_instance

/--
Given two locally ringed spaces `X` and `Y`, an isomorphism between `X` and `Y` as _sheafed_
spaces can be lifted to an isomorphism `X ⟶ Y` as locally ringed spaces.

This is related to the property that the functor `forget_to_SheafedSpace` reflects isomorphisms.
In fact, it is slightly stronger as we do not require `f` to come from a morphism between
_locally_ ringed spaces.
-/
def iso_of_SheafedSpace_iso {X Y : LocallyRingedSpace}
  (f : X.to_SheafedSpace ≅ Y.to_SheafedSpace) : X ≅ Y :=
{ hom := hom_of_SheafedSpace_hom_of_is_iso f.hom,
  inv := hom_of_SheafedSpace_hom_of_is_iso f.inv,
  hom_inv_id' := hom.ext _ _ f.hom_inv_id,
  inv_hom_id' := hom.ext _ _ f.inv_hom_id }

instance : reflects_isomorphisms forget_to_SheafedSpace :=
{ reflects := λ X Y f i,
  { out := by exactI
    ⟨hom_of_SheafedSpace_hom_of_is_iso (category_theory.inv (forget_to_SheafedSpace.map f)),
      hom.ext _ _ (is_iso.hom_inv_id _), hom.ext _ _ (is_iso.inv_hom_id _)⟩ } }

instance is_SheafedSpace_iso {X Y : LocallyRingedSpace} (f : X ⟶ Y) [is_iso f] :
  is_iso f.1 :=
LocallyRingedSpace.forget_to_SheafedSpace.map_is_iso f

/--
The restriction of a locally ringed space along an open embedding.
-/
@[simps]
def restrict {U : Top} (X : LocallyRingedSpace) {f : U ⟶ X.to_Top}
  (h : open_embedding f) : LocallyRingedSpace :=
{ local_ring :=
  begin
    intro x,
    dsimp at *,
    -- We show that the stalk of the restriction is isomorphic to the original stalk,
    apply @ring_equiv.local_ring _ _ _ (X.local_ring (f x)),
    exact (X.to_PresheafedSpace.restrict_stalk_iso h x).symm.CommRing_iso_to_ring_equiv,
  end,
  to_SheafedSpace := X.to_SheafedSpace.restrict h }

/-- The canonical map from the restriction to the supspace. -/
def of_restrict {U : Top} (X : LocallyRingedSpace) {f : U ⟶ X.to_Top}
  (h : open_embedding f) : X.restrict h ⟶ X :=
⟨X.to_PresheafedSpace.of_restrict h, λ x, infer_instance⟩

/--
The restriction of a locally ringed space `X` to the top subspace is isomorphic to `X` itself.
-/
def restrict_top_iso (X : LocallyRingedSpace) :
  X.restrict (opens.open_embedding ⊤) ≅ X :=
@iso_of_SheafedSpace_iso (X.restrict (opens.open_embedding ⊤)) X
  X.to_SheafedSpace.restrict_top_iso

/--
The global sections, notated Gamma.
-/
def Γ : LocallyRingedSpaceᵒᵖ ⥤ CommRing :=
forget_to_SheafedSpace.op ⋙ SheafedSpace.Γ

lemma Γ_def : Γ = forget_to_SheafedSpace.op ⋙ SheafedSpace.Γ := rfl

@[simp] lemma Γ_obj (X : LocallyRingedSpaceᵒᵖ) : Γ.obj X = (unop X).presheaf.obj (op ⊤) := rfl

lemma Γ_obj_op (X : LocallyRingedSpace) : Γ.obj (op X) = X.presheaf.obj (op ⊤) := rfl

@[simp] lemma Γ_map {X Y : LocallyRingedSpaceᵒᵖ} (f : X ⟶ Y) :
  Γ.map f = f.unop.1.c.app (op ⊤) := rfl

lemma Γ_map_op {X Y : LocallyRingedSpace} (f : X ⟶ Y) :
  Γ.map f.op = f.1.c.app (op ⊤) := rfl

lemma preimage_basic_open {X Y : LocallyRingedSpace} (f : X ⟶ Y) {U : opens Y}
  (s : Y.presheaf.obj (op U)) :
  (opens.map f.1.base).obj (Y.to_RingedSpace.basic_open s) =
    @RingedSpace.basic_open X.to_RingedSpace ((opens.map f.1.base).obj U) (f.1.c.app _ s) :=
begin
  ext,
  split,
  { rintros ⟨⟨y, hyU⟩, (hy : is_unit _), (rfl : y = _)⟩,
    erw RingedSpace.mem_basic_open _ _ ⟨x, show x ∈ (opens.map f.1.base).obj U, from hyU⟩,
    rw ← PresheafedSpace.stalk_map_germ_apply,
    exact (PresheafedSpace.stalk_map f.1 _).is_unit_map hy },
  { rintros ⟨y, (hy : is_unit _), rfl⟩,
    erw RingedSpace.mem_basic_open _ _ ⟨f.1.base y.1, y.2⟩,
    rw ← PresheafedSpace.stalk_map_germ_apply at hy,
    exact (is_unit_map_iff (PresheafedSpace.stalk_map f.1 _) _).mp hy }
end

-- This actually holds for all ringed spaces with nontrivial stalks.
@[simp] lemma basic_open_zero (X : LocallyRingedSpace) (U : opens X.carrier) :
  X.to_RingedSpace.basic_open (0 : X.presheaf.obj $ op U) = ⊥ :=
begin
  simp only [RingedSpace.basic_open, is_unit_zero_iff, map_zero,
    zero_ne_one' (X.presheaf.stalk _), set.set_of_false, set.image_empty],
  refl
end

instance component_nontrivial (X : LocallyRingedSpace) (U : opens X.carrier)
  [hU : nonempty U] : nontrivial (X.presheaf.obj $ op U) :=
(X.to_PresheafedSpace.presheaf.germ hU.some).domain_nontrivial

end LocallyRingedSpace

end algebraic_geometry


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Kenny Lau

! This file was ported from Lean 3 source module number_theory.basic
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.GeomSum
import Mathbin.RingTheory.Ideal.Quotient

/-!
# Basic results in number theory

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

This file should contain basic results in number theory. So far, it only contains the essential
lemma in the construction of the ring of Witt vectors.

## Main statement

`dvd_sub_pow_of_dvd_sub` proves that for elements `a` and `b` in a commutative ring `R` and for
all natural numbers `p` and `k` if `p` divides `a-b` in `R`, then `p ^ (k + 1)` divides
`a ^ (p ^ k) - b ^ (p ^ k)`.
-/

section

open Ideal Ideal.Quotient

/- warning: dvd_sub_pow_of_dvd_sub -> dvd_sub_pow_of_dvd_sub is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommRing.{u1} R] {p : Nat} {a : R} {b : R}, (Dvd.Dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R _inst_1)))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R _inst_1)))))))) p) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R _inst_1))))))) a b)) -> (forall (k : Nat), Dvd.Dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R _inst_1)))))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (Ring.toMonoid.{u1} R (CommRing.toRing.{u1} R _inst_1)))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R _inst_1)))))))) p) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) k (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R (CommRing.toRing.{u1} R _inst_1))))))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (Ring.toMonoid.{u1} R (CommRing.toRing.{u1} R _inst_1)))) a (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p k)) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (Ring.toMonoid.{u1} R (CommRing.toRing.{u1} R _inst_1)))) b (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p k))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommRing.{u1} R] {p : Nat} {a : R} {b : R}, (Dvd.dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R _inst_1)))))) (Nat.cast.{u1} R (NonAssocRing.toNatCast.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R _inst_1))) p) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R (CommRing.toRing.{u1} R _inst_1))) a b)) -> (forall (k : Nat), Dvd.dvd.{u1} R (semigroupDvd.{u1} R (SemigroupWithZero.toSemigroup.{u1} R (NonUnitalSemiring.toSemigroupWithZero.{u1} R (NonUnitalRing.toNonUnitalSemiring.{u1} R (NonUnitalCommRing.toNonUnitalRing.{u1} R (CommRing.toNonUnitalCommRing.{u1} R _inst_1)))))) (Nat.cast.{u1} R (NonAssocRing.toNatCast.{u1} R (Ring.toNonAssocRing.{u1} R (CommRing.toRing.{u1} R _inst_1))) (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) k (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R (CommRing.toRing.{u1} R _inst_1))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R (CommRing.toRing.{u1} R _inst_1)))))) a (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p k)) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R (CommRing.toRing.{u1} R _inst_1)))))) b (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p k))))
Case conversion may be inaccurate. Consider using '#align dvd_sub_pow_of_dvd_sub dvd_sub_pow_of_dvd_subₓ'. -/
theorem dvd_sub_pow_of_dvd_sub {R : Type _} [CommRing R] {p : ℕ} {a b : R} (h : (p : R) ∣ a - b)
    (k : ℕ) : (p ^ (k + 1) : R) ∣ a ^ p ^ k - b ^ p ^ k :=
  by
  induction' k with k ih
  · rwa [pow_one, pow_zero, pow_one, pow_one]
  rw [pow_succ' p k, pow_mul, pow_mul, ← geom_sum₂_mul, pow_succ]
  refine' mul_dvd_mul _ ih
  let I : Ideal R := span {p}
  let f : R →+* R ⧸ I := mk I
  have hp : (p : R ⧸ I) = 0 := by rw [← map_natCast f, eq_zero_iff_mem, mem_span_singleton]
  rw [← mem_span_singleton, ← Ideal.Quotient.eq] at h
  rw [← mem_span_singleton, ← eq_zero_iff_mem, RingHom.map_geom_sum₂, RingHom.map_pow,
    RingHom.map_pow, h, geom_sum₂_self, hp, MulZeroClass.zero_mul]
#align dvd_sub_pow_of_dvd_sub dvd_sub_pow_of_dvd_sub

end


import locally_constant.SemiNormedGroup
import normed_group.normed_with_aut
import analysis.normed.group.SemiNormedGroup.completion

/-!

# Completions of normed groups

This file contains an API for completions for seminormed groups equipped with
an automorphism which scales norms by a constant factor `r`.

## Main definitions

- `normed_with_aut_Completion` : if `V` is equipped with an automorphism changing norms
  by a factor `r` then the completion also has such an automorphism.
- `LCC : SemiNormedGroup ⥤ Profiniteᵒᵖ ⥤ SemiNormedGroup` :
  `LCC V S` is the seminormed group completion of the locally constant functions from `S` to `V`.

## TODO

Pull off the stuff about completions and put it into `normed_group/SemiNormedGroup`?
Then `system_of_complexes.basic` would not have to import this file.

-/

noncomputable theory
open_locale nnreal

universe u

namespace SemiNormedGroup

open uniform_space _root_.opposite _root_.category_theory Completion

instance normed_with_aut_Completion (V : SemiNormedGroup.{u}) (r : ℝ≥0) [normed_with_aut r V] :
  normed_with_aut r (Completion.obj V) :=
{ T := Completion.map_iso normed_with_aut.T,
  norm_T :=
  begin
    rw ← function.funext_iff,
    refine abstract_completion.funext completion.cpkg _ _ _,
    { apply continuous_norm.comp _, exact completion.continuous_map },
    { exact (continuous_const.mul continuous_norm : _) },
    intro v,
    calc _ = _ : congr_arg norm (completion.map_coe _ _)
       ... = _ : _,
    { exact normed_group_hom.uniform_continuous _ },
    { erw [completion.norm_coe, normed_with_aut.norm_T, completion.norm_coe] }
  end }

@[simp] lemma Completion_T_inv_eq (V : SemiNormedGroup.{u}) (r : ℝ≥0) [normed_with_aut r V] :
  (normed_with_aut.T.hom : Completion.obj V ⟶ _) = Completion.map normed_with_aut.T.hom := rfl

lemma T_hom_incl {V : SemiNormedGroup} {r : ℝ≥0} [normed_with_aut r V] :
  (incl : V ⟶ _) ≫ normed_with_aut.T.hom = normed_with_aut.T.hom ≫ incl :=
begin
  ext x,
  simp only [incl_apply, category_theory.comp_apply, Completion_T_inv_eq],
  change completion.map normed_with_aut.T.hom _ = _,
  rw completion.map_coe,
  exact normed_group_hom.uniform_continuous _,
end

lemma T_hom_eq {V : SemiNormedGroup} {r : ℝ≥0} [normed_with_aut r V] :
  normed_with_aut.T.hom = Completion.lift ((normed_with_aut.T.hom : V ⟶ V) ≫ incl) :=
lift_unique _ _ T_hom_incl

/-- `LCC` (Locally Constant Completion) is the bifunctor
that sends a seminormed group `V` and a profinite space `S` to `V-hat(S)`.
Here `V-hat(S)` is the completion (for the sup norm) of the locally constant functions `S → V`. -/
def LCC : SemiNormedGroup ⥤ Profiniteᵒᵖ ⥤ SemiNormedGroup :=
curry.obj ((uncurry.obj LocallyConstant) ⋙ Completion)

lemma LCC_obj_map' (V : SemiNormedGroup) {X Y : Profiniteᵒᵖ} (f : Y ⟶ X) :
  (LCC.obj V).map f = Completion.map ((LocallyConstant.obj V).map f) :=
begin
  delta LCC,
  simp only [curry.obj_obj_map, LocallyConstant_obj_map, functor.comp_map, uncurry.obj_map,
    nat_trans.id_app, functor.map_comp, functor.map_id, category_theory.functor.map_id],
  erw [← functor.map_comp, category.id_comp]
end

lemma LCC_obj_map (V : SemiNormedGroup) {X Y : Profiniteᵒᵖ} (f : Y ⟶ X) (v : (LCC.obj V).obj Y) :
  (LCC.obj V).map f v = completion.map (locally_constant.comap f.unop) v :=
by { rw LCC_obj_map', refl }

lemma LCC_obj_map_norm_noninc (V : SemiNormedGroup) {X Y : Profiniteᵒᵖ} (f : Y ⟶ X) :
  ((LCC.obj V).map f).norm_noninc :=
begin
  rw LCC_obj_map',
  exact (Completion.map_norm_noninc $ LocallyConstant_obj_map_norm_noninc _ _ _ _)
end

variables (S : Type*) [topological_space S] [compact_space S]

@[simps]
instance normed_with_aut_LocallyConstant (V : SemiNormedGroup) (S : Profiniteᵒᵖ) (r : ℝ≥0)
  [normed_with_aut r V] [hr : fact (0 < r)] :
  normed_with_aut r ((LocallyConstant.obj V).obj S) :=
{ T := (LocallyConstant.map_iso normed_with_aut.T).app S,
  norm_T :=
  begin
    rw ← op_unop S,
    rintro (f : locally_constant (unop S : Profinite) V),
    show Sup _ = ↑r * Sup _,
    dsimp,
    simp only [normed_with_aut.norm_T],
    convert real.Sup_mul r _ hr.out,
    ext,
    simp only [exists_prop, set.mem_range, exists_exists_eq_and, set.mem_set_of_eq]
  end }

instance normed_with_aut_LCC (V : SemiNormedGroup) (S : Profiniteᵒᵖ) (r : ℝ≥0)
  [normed_with_aut r V] [hr : fact (0 < r)] :
  normed_with_aut r ((LCC.obj V).obj S) :=
show normed_with_aut r (Completion.obj $ (LocallyConstant.obj V).obj S), by apply_instance

end SemiNormedGroup

#lint- only unused_arguments def_lemma doc_blame


/-
Copyright (c) 2020 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import algebra.hom.group_action
import algebra.module.basic
import data.set_like.basic
import group_theory.group_action.basic
/-!

# Sets invariant to a `mul_action`

In this file we define `sub_mul_action R M`; a subset of a `mul_action R M` which is closed with
respect to scalar multiplication.

For most uses, typically `submodule R M` is more powerful.

## Main definitions

* `sub_mul_action.mul_action` - the `mul_action R M` transferred to the subtype.
* `sub_mul_action.mul_action'` - the `mul_action S M` transferred to the subtype when
  `is_scalar_tower S R M`.
* `sub_mul_action.is_scalar_tower` - the `is_scalar_tower S R M` transferred to the subtype.

## Tags

submodule, mul_action
-/

open function

universes u u' u'' v
variables {S : Type u'} {T : Type u''} {R : Type u} {M : Type v}

set_option old_structure_cmd true

/-- A sub_mul_action is a set which is closed under scalar multiplication.  -/
structure sub_mul_action (R : Type u) (M : Type v) [has_smul R M] : Type v :=
(carrier : set M)
(smul_mem' : ∀ (c : R) {x : M}, x ∈ carrier → c • x ∈ carrier)

namespace sub_mul_action

variables [has_smul R M]

instance : set_like (sub_mul_action R M) M :=
⟨sub_mul_action.carrier, λ p q h, by cases p; cases q; congr'⟩

@[simp] lemma mem_carrier {p : sub_mul_action R M} {x : M} : x ∈ p.carrier ↔ x ∈ (p : set M) :=
iff.rfl

@[ext] theorem ext {p q : sub_mul_action R M} (h : ∀ x, x ∈ p ↔ x ∈ q) : p = q := set_like.ext h

/-- Copy of a sub_mul_action with a new `carrier` equal to the old one. Useful to fix definitional
equalities.-/
protected def copy (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) : sub_mul_action R M :=
{ carrier := s,
  smul_mem' := hs.symm ▸ p.smul_mem' }

@[simp] lemma coe_copy (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) :
  (p.copy s hs : set M) = s := rfl

lemma copy_eq (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) : p.copy s hs = p :=
set_like.coe_injective hs

instance : has_bot (sub_mul_action R M) :=
⟨{ carrier := ∅, smul_mem' := λ c, set.not_mem_empty}⟩

instance : inhabited (sub_mul_action R M) := ⟨⊥⟩

end sub_mul_action

namespace sub_mul_action

section has_smul

variables [has_smul R M]
variables (p : sub_mul_action R M)
variables {r : R} {x : M}

lemma smul_mem (r : R) (h : x ∈ p) : r • x ∈ p := p.smul_mem' r h

instance : has_smul R p :=
{ smul := λ c x, ⟨c • x.1, smul_mem _ c x.2⟩ }

variables {p}
@[simp, norm_cast] lemma coe_smul (r : R) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_mk (x : M) (hx : x ∈ p) : ((⟨x, hx⟩ : p) : M) = x := rfl

variables (p)

/-- Embedding of a submodule `p` to the ambient space `M`. -/
protected def subtype : p →[R] M :=
by refine {to_fun := coe, ..}; simp [coe_smul]

@[simp] theorem subtype_apply (x : p) : p.subtype x = x := rfl

lemma subtype_eq_val : ((sub_mul_action.subtype p) : p → M) = subtype.val := rfl

end has_smul

section mul_action_monoid

variables [monoid R] [mul_action R M]

section
variables [has_smul S R] [has_smul S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M)

lemma smul_of_tower_mem (s : S) {x : M} (h : x ∈ p) : s • x ∈ p :=
by { rw [←one_smul R x, ←smul_assoc], exact p.smul_mem _ h }

instance has_smul' : has_smul S p :=
{ smul := λ c x, ⟨c • x.1, smul_of_tower_mem _ c x.2⟩ }

instance : is_scalar_tower S R p :=
{ smul_assoc := λ s r x, subtype.ext $ smul_assoc s r ↑x }

@[simp, norm_cast] lemma coe_smul_of_tower (s : S) (x : p) : ((s • x : p) : M) = s • ↑x := rfl

@[simp] lemma smul_mem_iff' {G} [group G] [has_smul G R] [mul_action G M]
  [is_scalar_tower G R M] (g : G) {x : M} :
  g • x ∈ p ↔ x ∈ p :=
⟨λ h, inv_smul_smul g x ▸ p.smul_of_tower_mem g⁻¹ h, p.smul_of_tower_mem g⟩

instance [has_smul Sᵐᵒᵖ R] [has_smul Sᵐᵒᵖ M] [is_scalar_tower Sᵐᵒᵖ R M]
  [is_central_scalar S M] : is_central_scalar S p :=
{ op_smul_eq_smul := λ r x, subtype.ext $ op_smul_eq_smul r x }

end

section

variables [monoid S] [has_smul S R] [mul_action S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M)

/-- If the scalar product forms a `mul_action`, then the subset inherits this action -/
instance mul_action' : mul_action S p :=
{ smul := (•),
  one_smul := λ x, subtype.ext $ one_smul _ x,
  mul_smul := λ c₁ c₂ x, subtype.ext $ mul_smul c₁ c₂ x }

instance : mul_action R p := p.mul_action'

end

/-- Orbits in a `sub_mul_action` coincide with orbits in the ambient space. -/
lemma coe_image_orbit {p : sub_mul_action R M} (m : p) :
  coe '' mul_action.orbit R m = mul_action.orbit R (m : M) := (set.range_comp _ _).symm

/- -- Previously, the relatively useless :
lemma orbit_of_sub_mul {p : sub_mul_action R M} (m : p) :
  (mul_action.orbit R m : set M) = mul_action.orbit R (m : M) := rfl
-/

/-- Stabilizers in monoid sub_mul_action coincide with stabilizers in the ambient space -/
lemma stabilizer_of_sub_mul.submonoid {p : sub_mul_action R M} (m : p) :
  mul_action.stabilizer.submonoid R m = mul_action.stabilizer.submonoid R (m : M) :=
begin
  ext,
  simp only [mul_action.mem_stabilizer_submonoid_iff,
      ← sub_mul_action.coe_smul, set_like.coe_eq_coe]
end

end mul_action_monoid

section mul_action_group

variables [group R] [mul_action R M]

/-- Stabilizers in group sub_mul_action coincide with stabilizers in the ambient space -/
lemma stabilizer_of_sub_mul {p : sub_mul_action R M} (m : p) :
  mul_action.stabilizer R m = mul_action.stabilizer R (m : M) :=
begin
  rw ← subgroup.to_submonoid_eq,
  exact stabilizer_of_sub_mul.submonoid m,
end

end mul_action_group

section module

variables [semiring R] [add_comm_monoid M]
variables [module R M]
variables (p : sub_mul_action R M)

lemma zero_mem (h : (p : set M).nonempty) : (0 : M) ∈ p :=
let ⟨x, hx⟩ := h in zero_smul R (x : M) ▸ p.smul_mem 0 hx

/-- If the scalar product forms a `module`, and the `sub_mul_action` is not `⊥`, then the
subset inherits the zero. -/
instance [n_empty : nonempty p] : has_zero p :=
{ zero := ⟨0, n_empty.elim $ λ x, p.zero_mem ⟨x, x.prop⟩⟩ }

end module

section add_comm_group

variables [ring R] [add_comm_group M]
variables [module R M]
variables (p p' : sub_mul_action R M)
variables {r : R} {x y : M}

lemma neg_mem (hx : x ∈ p) : -x ∈ p := by { rw ← neg_one_smul R, exact p.smul_mem _ hx }

@[simp] lemma neg_mem_iff : -x ∈ p ↔ x ∈ p :=
⟨λ h, by { rw ←neg_neg x, exact neg_mem _ h}, neg_mem _⟩

instance : has_neg p := ⟨λx, ⟨-x.1, neg_mem _ x.2⟩⟩

@[simp, norm_cast] lemma coe_neg (x : p) : ((-x : p) : M) = -x := rfl

end add_comm_group

end sub_mul_action

namespace sub_mul_action

variables [group_with_zero S] [monoid R] [mul_action R M]
variables [has_smul S R] [mul_action S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M) {s : S} {x y : M}

theorem smul_mem_iff (s0 : s ≠ 0) : s • x ∈ p ↔ x ∈ p :=
p.smul_mem_iff' (units.mk0 s s0)

end sub_mul_action


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.monoidal.braided
import category_theory.functor.reflects_isomorphisms
import category_theory.monoidal.coherence

/-!
# Half braidings and the Drinfeld center of a monoidal category

We define `center C` to be pairs `⟨X, b⟩`, where `X : C` and `b` is a half-braiding on `X`.

We show that `center C` is braided monoidal,
and provide the monoidal functor `center.forget` from `center C` back to `C`.

## Future work

Verifying the various axioms here is done by tedious rewriting.
Using the `slice` tactic may make the proofs marginally more readable.

More exciting, however, would be to make possible one of the following options:
1. Integration with homotopy.io / globular to give "picture proofs".
2. The monoidal coherence theorem, so we can ignore associators
   (after which most of these proofs are trivial;
   I'm unsure if the monoidal coherence theorem is even usable in dependent type theory).
3. Automating these proofs using `rewrite_search` or some relative.

-/

open category_theory
open category_theory.monoidal_category

universes v v₁ v₂ v₃ u u₁ u₂ u₃
noncomputable theory

namespace category_theory

variables {C : Type u₁} [category.{v₁} C] [monoidal_category C]

/--
A half-braiding on `X : C` is a family of isomorphisms `X ⊗ U ≅ U ⊗ X`,
monoidally natural in `U : C`.

Thinking of `C` as a 2-category with a single `0`-morphism, these are the same as natural
transformations (in the pseudo- sense) of the identity 2-functor on `C`, which send the unique
`0`-morphism to `X`.
-/
@[nolint has_inhabited_instance]
structure half_braiding (X : C) :=
(β : Π U, X ⊗ U ≅ U ⊗ X)
(monoidal' : ∀ U U', (β (U ⊗ U')).hom =
  (α_ _ _ _).inv ≫ ((β U).hom ⊗ 𝟙 U') ≫ (α_ _ _ _).hom ≫ (𝟙 U ⊗ (β U').hom) ≫ (α_ _ _ _).inv
  . obviously)
(naturality' : ∀ {U U'} (f : U ⟶ U'), (𝟙 X ⊗ f) ≫ (β U').hom = (β U).hom ≫ (f ⊗ 𝟙 X) . obviously)

restate_axiom half_braiding.monoidal'
attribute [reassoc, simp] half_braiding.monoidal -- the reassoc lemma is redundant as a simp lemma
restate_axiom half_braiding.naturality'
attribute [simp, reassoc] half_braiding.naturality

variables (C)
/--
The Drinfeld center of a monoidal category `C` has as objects pairs `⟨X, b⟩`, where `X : C`
and `b` is a half-braiding on `X`.
-/
@[nolint has_inhabited_instance]
def center := Σ X : C, half_braiding X

namespace center

variables {C}

/-- A morphism in the Drinfeld center of `C`. -/
@[ext, nolint has_inhabited_instance]
structure hom (X Y : center C) :=
(f : X.1 ⟶ Y.1)
(comm' : ∀ U, (f ⊗ 𝟙 U) ≫ (Y.2.β U).hom = (X.2.β U).hom ≫ (𝟙 U ⊗ f) . obviously)

restate_axiom hom.comm'
attribute [simp, reassoc] hom.comm

instance : category (center C) :=
{ hom := hom,
  id := λ X, { f := 𝟙 X.1, },
  comp := λ X Y Z f g, { f := f.f ≫ g.f, }, }

@[simp] lemma id_f (X : center C) : hom.f (𝟙 X) = 𝟙 X.1 := rfl
@[simp] lemma comp_f {X Y Z : center C} (f : X ⟶ Y) (g : Y ⟶ Z) : (f ≫ g).f = f.f ≫ g.f := rfl

@[ext]
lemma ext {X Y : center C} (f g : X ⟶ Y) (w : f.f = g.f) : f = g :=
by { cases f, cases g, congr, exact w, }

/--
Construct an isomorphism in the Drinfeld center from
a morphism whose underlying morphism is an isomorphism.
-/
@[simps]
def iso_mk {X Y : center C} (f : X ⟶ Y) [is_iso f.f] : X ≅ Y :=
{ hom := f,
  inv := ⟨inv f.f, λ U, by simp [←cancel_epi (f.f ⊗ 𝟙 U), ←comp_tensor_id_assoc, ←id_tensor_comp]⟩ }

instance is_iso_of_f_is_iso {X Y : center C} (f : X ⟶ Y) [is_iso f.f] : is_iso f :=
begin
  change is_iso (iso_mk f).hom,
  apply_instance,
end

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
@[simps]
def tensor_obj (X Y : center C) : center C :=
⟨X.1 ⊗ Y.1,
  { β := λ U, α_ _ _ _ ≪≫ (iso.refl X.1 ⊗ Y.2.β U) ≪≫ (α_ _ _ _).symm
      ≪≫ (X.2.β U ⊗ iso.refl Y.1) ≪≫ α_ _ _ _,
    monoidal' := λ U U',
    begin
      dsimp,
      simp only [comp_tensor_id, id_tensor_comp, category.assoc, half_braiding.monoidal],
      -- On the RHS, we'd like to commute `((X.snd.β U).hom ⊗ 𝟙 Y.fst) ⊗ 𝟙 U'`
      -- and `𝟙 U ⊗ 𝟙 X.fst ⊗ (Y.snd.β U').hom` past each other,
      -- but there are some associators we need to get out of the way first.
      slice_rhs 6 8 { rw pentagon, },
      slice_rhs 5 6 { rw associator_naturality, },
      slice_rhs 7 8 { rw ←associator_naturality, },
      slice_rhs 6 7 { rw [tensor_id, tensor_id, tensor_id_comp_id_tensor, ←id_tensor_comp_tensor_id,
        ←tensor_id, ←tensor_id], },
      -- Now insert associators as needed to make the four half-braidings look identical
      slice_rhs 10 10 { rw associator_inv_conjugation, },
      slice_rhs 7 7 { rw associator_inv_conjugation, },
      slice_rhs 6 6 { rw associator_conjugation, },
      slice_rhs 3 3 { rw associator_conjugation, },
      -- Finish with an application of the coherence theorem.
      coherence,
    end,
    naturality' := λ U U' f,
    begin
      dsimp,
      rw [category.assoc, category.assoc, category.assoc, category.assoc,
        id_tensor_associator_naturality_assoc, ←id_tensor_comp_assoc, half_braiding.naturality,
        id_tensor_comp_assoc, associator_inv_naturality_assoc, ←comp_tensor_id_assoc,
        half_braiding.naturality, comp_tensor_id_assoc, associator_naturality, ←tensor_id],
    end, }⟩

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
@[simps]
def tensor_hom {X₁ Y₁ X₂ Y₂ : center C} (f : X₁ ⟶ Y₁) (g : X₂ ⟶ Y₂) :
  tensor_obj X₁ X₂ ⟶ tensor_obj Y₁ Y₂ :=
{ f := f.f ⊗ g.f,
  comm' := λ U, begin
    dsimp,
    rw [category.assoc, category.assoc, category.assoc, category.assoc,
      associator_naturality_assoc, ←tensor_id_comp_id_tensor, category.assoc,
      ←id_tensor_comp_assoc, g.comm, id_tensor_comp_assoc, tensor_id_comp_id_tensor_assoc,
      ←id_tensor_comp_tensor_id, category.assoc, associator_inv_naturality_assoc,
      id_tensor_associator_inv_naturality_assoc, tensor_id,
      id_tensor_comp_tensor_id_assoc, ←tensor_id_comp_id_tensor g.f, category.assoc,
      ←comp_tensor_id_assoc, f.comm, comp_tensor_id_assoc, id_tensor_associator_naturality,
      associator_naturality_assoc, ←id_tensor_comp, tensor_id_comp_id_tensor],
  end }

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
@[simps]
def tensor_unit : center C :=
⟨𝟙_ C,
  { β := λ U, (λ_ U) ≪≫ (ρ_ U).symm,
    monoidal' := λ U U', by simp,
    naturality' := λ U U' f, begin
      dsimp,
      rw [left_unitor_naturality_assoc, right_unitor_inv_naturality, category.assoc],
    end, }⟩

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
def associator (X Y Z : center C) : tensor_obj (tensor_obj X Y) Z ≅ tensor_obj X (tensor_obj Y Z) :=
iso_mk ⟨(α_ X.1 Y.1 Z.1).hom, λ U, begin
  dsimp,
  simp only [comp_tensor_id, id_tensor_comp, ←tensor_id, associator_conjugation],
  coherence,
end⟩

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
def left_unitor (X : center C) : tensor_obj tensor_unit X ≅ X :=
iso_mk ⟨(λ_ X.1).hom, λ U, begin
  dsimp,
  simp only [category.comp_id, category.assoc, tensor_inv_hom_id, comp_tensor_id,
    tensor_id_comp_id_tensor, triangle_assoc_comp_right_inv],
  rw [←left_unitor_tensor, left_unitor_naturality, left_unitor_tensor'_assoc],
end⟩

/-- Auxiliary definition for the `monoidal_category` instance on `center C`. -/
def right_unitor (X : center C) : tensor_obj X tensor_unit ≅ X :=
iso_mk ⟨(ρ_ X.1).hom, λ U, begin
  dsimp,
  simp only [tensor_id_comp_id_tensor_assoc, triangle_assoc, id_tensor_comp, category.assoc],
  rw [←tensor_id_comp_id_tensor_assoc (ρ_ U).inv, cancel_epi, ←right_unitor_tensor_inv_assoc,
    ←right_unitor_inv_naturality_assoc],
  simp,
end⟩

section
local attribute [simp] associator_naturality left_unitor_naturality right_unitor_naturality
  pentagon
local attribute [simp] center.associator center.left_unitor center.right_unitor

instance : monoidal_category (center C) :=
{ tensor_obj := λ X Y, tensor_obj X Y,
  tensor_hom := λ X₁ Y₁ X₂ Y₂ f g, tensor_hom f g,
  tensor_unit := tensor_unit,
  associator := associator,
  left_unitor := left_unitor,
  right_unitor := right_unitor, }

@[simp] lemma tensor_fst (X Y : center C) : (X ⊗ Y).1 = X.1 ⊗ Y.1 := rfl

@[simp] lemma tensor_β (X Y : center C) (U : C) :
  (X ⊗ Y).2.β U =
    α_ _ _ _ ≪≫ (iso.refl X.1 ⊗ Y.2.β U) ≪≫ (α_ _ _ _).symm
      ≪≫ (X.2.β U ⊗ iso.refl Y.1) ≪≫ α_ _ _ _ :=
rfl
@[simp]

@[simp] lemma tensor_unit_β (U : C) : (𝟙_ (center C)).2.β U = (λ_ U) ≪≫ (ρ_ U).symm := rfl

@[simp] lemma associator_hom_f (X Y Z : center C) : hom.f (α_ X Y Z).hom = (α_ X.1 Y.1 Z.1).hom :=
rfl

@[simp] lemma associator_inv_f (X Y Z : center C) : hom.f (α_ X Y Z).inv = (α_ X.1 Y.1 Z.1).inv :=
by { ext, rw [←associator_hom_f, ←comp_f, iso.hom_inv_id], refl, }

@[simp] lemma left_unitor_hom_f (X : center C) : hom.f (λ_ X).hom = (λ_ X.1).hom :=
rfl

@[simp] lemma left_unitor_inv_f (X : center C) : hom.f (λ_ X).inv = (λ_ X.1).inv :=
by { ext, rw [←left_unitor_hom_f, ←comp_f, iso.hom_inv_id], refl, }

@[simp] lemma right_unitor_hom_f (X : center C) : hom.f (ρ_ X).hom = (ρ_ X.1).hom :=
rfl

@[simp] lemma right_unitor_inv_f (X : center C) : hom.f (ρ_ X).inv = (ρ_ X.1).inv :=
by { ext, rw [←right_unitor_hom_f, ←comp_f, iso.hom_inv_id], refl, }

end

section
variables (C)

/-- The forgetful monoidal functor from the Drinfeld center to the original category. -/
@[simps]
def forget : monoidal_functor (center C) C :=
{ obj := λ X, X.1,
  map := λ X Y f, f.f,
  ε := 𝟙 (𝟙_ C),
  μ := λ X Y, 𝟙 (X.1 ⊗ Y.1), }

instance : reflects_isomorphisms (forget C).to_functor :=
{ reflects := λ A B f i, by { dsimp at i, resetI, change is_iso (iso_mk f).hom, apply_instance, } }

end

/-- Auxiliary definition for the `braided_category` instance on `center C`. -/
@[simps]
def braiding (X Y : center C) : X ⊗ Y ≅ Y ⊗ X :=
iso_mk ⟨(X.2.β Y.1).hom, λ U, begin
  dsimp,
  simp only [category.assoc],
  rw [←is_iso.inv_comp_eq, is_iso.iso.inv_hom, ←half_braiding.monoidal_assoc,
    ←half_braiding.naturality_assoc, half_braiding.monoidal],
  simp,
end⟩

instance braided_category_center : braided_category (center C) :=
{ braiding := braiding,
  braiding_naturality' := λ X Y X' Y' f g, begin
    ext,
    dsimp,
    rw [←tensor_id_comp_id_tensor, category.assoc, half_braiding.naturality, f.comm_assoc,
      id_tensor_comp_tensor_id],
  end, } -- `obviously` handles the hexagon axioms

section
variables [braided_category C]

open braided_category

/-- Auxiliary construction for `of_braided`. -/
@[simps]
def of_braided_obj (X : C) : center C :=
⟨X, { β := λ Y, β_ X Y,
  monoidal' := λ U U', begin
    rw [iso.eq_inv_comp, ←category.assoc, ←category.assoc, iso.eq_comp_inv,
      category.assoc, category.assoc],
    exact hexagon_forward X U U',
  end }⟩

variables (C)

/--
The functor lifting a braided category to its center, using the braiding as the half-braiding.
-/
@[simps]
def of_braided : monoidal_functor C (center C) :=
{ obj := of_braided_obj,
  map := λ X X' f,
  { f := f,
    comm' := λ U, braiding_naturality _ _, },
  ε :=
  { f := 𝟙 _,
    comm' := λ U, begin
      dsimp,
      rw [tensor_id, category.id_comp, tensor_id, category.comp_id, ←braiding_right_unitor,
        category.assoc, iso.hom_inv_id, category.comp_id],
    end, },
  μ := λ X Y,
  { f := 𝟙 _,
    comm' := λ U, begin
      dsimp,
      rw [tensor_id, tensor_id, category.id_comp, category.comp_id,
        ←iso.inv_comp_eq, ←category.assoc, ←category.assoc, ←iso.comp_inv_eq,
        category.assoc, hexagon_reverse, category.assoc],
    end, }, }

end

end center

end category_theory


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

/-!
# Strong epimorphisms

In this file, we define strong epimorphisms. A strong epimorphism is an epimorphism `f`, such
that for every commutative square with `f` at the top and a monomorphism at the bottom, there is
a diagonal morphism making the two triangles commute. This lift is necessarily unique (as shown in
`comma.lean`).

## Main results

Besides the definition, we show that
* the composition of two strong epimorphisms is a strong epimorphism,
* if `f ≫ g` is a strong epimorphism, then so is `g`,
* if `f` is both a strong epimorphism and a monomorphism, then it is an isomorphism

## TODO

Show that the dual of a strong epimorphism is a strong monomorphism, and vice versa.

## References

* [F. Borceux, *Handbook of Categorical Algebra 1*][borceux-vol1]
-/

universes v u

namespace category_theory
variables {C : Type u} [category.{v} C]

variables {P Q : C}

/-- A strong epimorphism `f` is an epimorphism such that every commutative square with `f` at the
    top and a monomorphism at the bottom has a lift. -/
class strong_epi (f : P ⟶ Q) : Prop :=
(epi : epi f)
(has_lift : Π {X Y : C} {u : P ⟶ X} {v : Q ⟶ Y} {z : X ⟶ Y} [mono z] (h : u ≫ z = f ≫ v),
  arrow.has_lift $ arrow.hom_mk' h)

/-- A strong monomorphism `f` is a monomorphism such that every commutative square with `f` at the
    bottom and an epimorphism at the top has a lift. -/
class strong_mono (f : P ⟶ Q) : Prop :=
(mono : mono f)
(has_lift : Π {X Y : C} {u : X ⟶ P} {v : Y ⟶ Q} {z : X ⟶ Y} [epi z] (h : u ≫ f = z ≫ v),
  arrow.has_lift $ arrow.hom_mk' h)

attribute [instance] strong_epi.has_lift
attribute [instance] strong_mono.has_lift

@[priority 100]
instance epi_of_strong_epi (f : P ⟶ Q) [strong_epi f] : epi f := strong_epi.epi

@[priority 100]
instance mono_of_strong_mono (f : P ⟶ Q) [strong_mono f] : mono f := strong_mono.mono

section
variables {R : C} (f : P ⟶ Q) (g : Q ⟶ R)

/-- The composition of two strong epimorphisms is a strong epimorphism. -/
lemma strong_epi_comp [strong_epi f] [strong_epi g] : strong_epi (f ≫ g) :=
{ epi := epi_comp _ _,
  has_lift :=
  begin
    introsI,
    have h₀ : u ≫ z = f ≫ g ≫ v, by simpa [category.assoc] using h,
    let w : Q ⟶ X := arrow.lift (arrow.hom_mk' h₀),
    have h₁ : w ≫ z = g ≫ v, by rw arrow.lift_mk'_right,
    exact arrow.has_lift.mk ⟨(arrow.lift (arrow.hom_mk' h₁) : R ⟶ X), by simp, by simp⟩
  end }

/-- The composition of two strong monomorphisms is a strong monomorphism. -/
lemma strong_mono_comp [strong_mono f] [strong_mono g] : strong_mono (f ≫ g) :=
{ mono := mono_comp _ _,
  has_lift :=
  begin
    introsI,
    have h₀ : (u ≫ f) ≫ g = z ≫ v, by simpa [category.assoc] using h,
    let w : Y ⟶ Q := arrow.lift (arrow.hom_mk' h₀),
    have h₁ : u ≫ f = z ≫ w, by rw arrow.lift_mk'_left,
    exact arrow.has_lift.mk ⟨(arrow.lift (arrow.hom_mk' h₁) : Y ⟶ P), by simp, by simp⟩
  end }

/-- If `f ≫ g` is a strong epimorphism, then so is `g`. -/
lemma strong_epi_of_strong_epi [strong_epi (f ≫ g)] : strong_epi g :=
{ epi := epi_of_epi f g,
  has_lift :=
  begin
    introsI,
    have h₀ : (f ≫ u) ≫ z = (f ≫ g) ≫ v, by simp only [category.assoc, h],
    exact arrow.has_lift.mk
      ⟨(arrow.lift (arrow.hom_mk' h₀) : R ⟶ X), (cancel_mono z).1 (by simp [h]), by simp⟩,
  end }

/-- If `f ≫ g` is a strong monomorphism, then so is `f`. -/

/-- An isomorphism is in particular a strong epimorphism. -/
@[priority 100] instance strong_epi_of_is_iso [is_iso f] : strong_epi f :=
{ epi := by apply_instance,
  has_lift := λ X Y u v z _ h, arrow.has_lift.mk ⟨inv f ≫ u, by simp, by simp [h]⟩ }

/-- An isomorphism is in particular a strong monomorphism. -/
@[priority 100] instance strong_mono_of_is_iso [is_iso f] : strong_mono f :=
{ mono := by apply_instance,
  has_lift := λ X Y u v z _ h, arrow.has_lift.mk
    ⟨v ≫ inv f, by simp [← category.assoc, ← h], by simp⟩ }

end

/-- A strong epimorphism that is a monomorphism is an isomorphism. -/
lemma is_iso_of_mono_of_strong_epi (f : P ⟶ Q) [mono f] [strong_epi f] : is_iso f :=
⟨⟨arrow.lift $ arrow.hom_mk' $ show 𝟙 P ≫ f = f ≫ 𝟙 Q, by simp, by tidy⟩⟩

/-- A strong monomorphism that is an epimorphism is an isomorphism. -/
lemma is_iso_of_epi_of_strong_mono (f : P ⟶ Q) [epi f] [strong_mono f] : is_iso f :=
⟨⟨arrow.lift $ arrow.hom_mk' $ show 𝟙 P ≫ f = f ≫ 𝟙 Q, by simp, by tidy⟩⟩

end category_theory


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Floris van Doorn
-/
import order.hom.complete_lattice
import topology.bases
import topology.homeomorph
import topology.continuous_function.basic
import order.compactly_generated
import tactic.auto_cases

/-!
# Open sets

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

## Summary

We define the subtype of open sets in a topological space.

## Main Definitions

### Bundled open sets

- `opens α` is the type of open subsets of a topological space `α`.
- `opens.is_basis` is a predicate saying that a set of `opens`s form a topological basis.
- `opens.comap`: preimage of an open set under a continuous map as a `frame_hom`.
- `homeomorph.opens_congr`: order-preserving equivalence between open sets in the domain and the
  codomain of a homeomorphism.

### Bundled open neighborhoods

- `open_nhds_of x` is the type of open subsets of a topological space `α` containing `x : α`.
- `open_nhds_of.comap f x U` is the preimage of open neighborhood `U` of `f x` under `f : C(α, β)`.

## Main results

We define order structures on both `opens α` (`complete_structure`, `frame`) and `open_nhds_of x`
(`order_top`, `distrib_lattice`).
-/

open filter function order set
open_locale topology

variables {ι α β γ : Type*} [topological_space α] [topological_space β] [topological_space γ]

namespace topological_space

variable (α)

/-- The type of open subsets of a topological space. -/
structure opens :=
(carrier : set α)
(is_open' : is_open carrier)

variable {α}

namespace opens

instance : set_like (opens α) α :=
{ coe := opens.carrier,
  coe_injective' := λ ⟨_, _⟩ ⟨_, _⟩ _, by congr; assumption }

instance : can_lift (set α) (opens α) coe is_open :=
⟨λ s h, ⟨⟨s, h⟩, rfl⟩⟩

lemma «forall» {p : opens α → Prop} : (∀ U, p U) ↔ ∀ (U : set α) (hU : is_open U), p ⟨U, hU⟩ :=
⟨λ h _ _, h _, λ h ⟨U, hU⟩, h _ _⟩

@[simp] lemma carrier_eq_coe (U : opens α) : U.1 = ↑U := rfl

/-- the coercion `opens α → set α` applied to a pair is the same as taking the first component -/
@[simp] lemma coe_mk {U : set α} {hU : is_open U} : ↑(⟨U, hU⟩ : opens α) = U := rfl

@[simp] lemma mem_mk {x : α} {U : set α} {h : is_open U} :
  @has_mem.mem _ (opens α) _ x ⟨U, h⟩ ↔ x ∈ U := iff.rfl

-- todo: make it `simp` for a `set_like`?
@[simp] protected lemma nonempty_coe_sort {U : opens α} : nonempty U ↔ (U : set α).nonempty :=
set.nonempty_coe_sort

@[ext] lemma ext {U V : opens α} (h : (U : set α) = V) : U = V := set_like.coe_injective h
@[simp] lemma coe_inj {U V : opens α} : (U : set α) = V ↔ U = V := set_like.ext'_iff.symm

protected lemma is_open (U : opens α) : is_open (U : set α) := U.is_open'

@[simp] lemma mk_coe (U : opens α) : mk ↑U U.is_open = U := by { cases U, refl }

/-- See Note [custom simps projection]. -/
def simps.coe (U : opens α) : set α := U

initialize_simps_projections opens (carrier → coe)

/-- The interior of a set, as an element of `opens`. -/
def interior (s : set α) : opens α := ⟨interior s, is_open_interior⟩

lemma gc : galois_connection (coe : opens α → set α) interior :=
λ U s, ⟨λ h, interior_maximal h U.is_open, λ h, le_trans h interior_subset⟩

/-- The galois coinsertion between sets and opens. -/
def gi : galois_coinsertion coe (@interior α _) :=
{ choice := λ s hs, ⟨s, interior_eq_iff_is_open.mp $ le_antisymm interior_subset hs⟩,
  gc := gc,
  u_l_le := λ _, interior_subset,
  choice_eq := λ s hs, le_antisymm hs interior_subset }

instance : complete_lattice (opens α) :=
complete_lattice.copy (galois_coinsertion.lift_complete_lattice gi)
/- le  -/ (λ U V, (U : set α) ⊆ V) rfl
/- top -/ ⟨univ, is_open_univ⟩ (ext interior_univ.symm)
/- bot -/ ⟨∅, is_open_empty⟩ rfl
/- sup -/ (λ U V, ⟨↑U ∪ ↑V, U.2.union V.2⟩) rfl
/- inf -/ (λ U V, ⟨↑U ∩ ↑V, U.2.inter V.2⟩) (funext₂ $ λ U V, ext (U.2.inter V.2).interior_eq.symm)
/- Sup -/ (λ S, ⟨⋃ s ∈ S, ↑s, is_open_bUnion $ λ s _, s.2⟩) (funext $ λ S, ext Sup_image.symm)
/- Inf -/ _ rfl

@[simp] lemma mk_inf_mk {U V : set α} {hU : is_open U} {hV : is_open V} :
  (⟨U, hU⟩ ⊓ ⟨V, hV⟩ : opens α) = ⟨U ⊓ V, is_open.inter hU hV⟩ := rfl
@[simp, norm_cast] lemma coe_inf (s t : opens α) : (↑(s ⊓ t) : set α) = s ∩ t := rfl
@[simp, norm_cast] lemma coe_sup (s t : opens α) : (↑(s ⊔ t) : set α) = s ∪ t := rfl
@[simp, norm_cast] lemma coe_bot : ((⊥ : opens α) : set α) = ∅ := rfl
@[simp, norm_cast] lemma coe_top : ((⊤ : opens α) : set α) = set.univ := rfl
@[simp, norm_cast] lemma coe_Sup {S : set (opens α)} : (↑(Sup S) : set α) = ⋃ i ∈ S, ↑i := rfl

@[simp, norm_cast] lemma coe_finset_sup (f : ι → opens α) (s : finset ι) :
  (↑(s.sup f) : set α) = s.sup (coe ∘ f) :=
map_finset_sup (⟨⟨coe, coe_sup⟩, coe_bot⟩ : sup_bot_hom (opens α) (set α)) _ _

@[simp, norm_cast] lemma coe_finset_inf (f : ι → opens α) (s : finset ι) :
  (↑(s.inf f) : set α) = s.inf (coe ∘ f) :=
map_finset_inf (⟨⟨coe, coe_inf⟩, coe_top⟩ : inf_top_hom (opens α) (set α)) _ _

instance : inhabited (opens α) := ⟨⊥⟩

lemma supr_def {ι} (s : ι → opens α) : (⨆ i, s i) = ⟨⋃ i, s i, is_open_Union $ λ i, (s i).2⟩ :=
by { ext, simp only [supr, coe_Sup, bUnion_range], refl }

@[simp] lemma supr_mk {ι} (s : ι → set α) (h : Π i, is_open (s i)) :
  (⨆ i, ⟨s i, h i⟩ : opens α) = ⟨⋃ i, s i, is_open_Union h⟩ :=
by { rw supr_def, simp }

@[simp, norm_cast] lemma coe_supr {ι} (s : ι → opens α) :
  ((⨆ i, s i : opens α) : set α) = ⋃ i, s i :=
by simp [supr_def]

@[simp] theorem mem_supr {ι} {x : α} {s : ι → opens α} : x ∈ supr s ↔ ∃ i, x ∈ s i :=
by { rw [← set_like.mem_coe], simp, }

@[simp] lemma mem_Sup {Us : set (opens α)} {x : α} : x ∈ Sup Us ↔ ∃ u ∈ Us, x ∈ u :=
by simp_rw [Sup_eq_supr, mem_supr]

instance : frame (opens α) :=
{ Sup := Sup,
  inf_Sup_le_supr_inf := λ a s,
    (ext $ by simp only [coe_inf, coe_supr, coe_Sup, set.inter_Union₂]).le,
  ..opens.complete_lattice }

lemma open_embedding_of_le {U V : opens α} (i : U ≤ V) :
  open_embedding (set.inclusion i) :=
{ inj := set.inclusion_injective i,
  induced := (@induced_compose _ _ _ _ (set.inclusion i) coe).symm,
  open_range :=
  begin
    rw set.range_inclusion i,
    exact U.is_open.preimage continuous_subtype_val
  end, }

lemma not_nonempty_iff_eq_bot (U : opens α) : ¬ set.nonempty (U : set α) ↔ U = ⊥ :=
by rw [← coe_inj, opens.coe_bot, ← set.not_nonempty_iff_eq_empty]

lemma ne_bot_iff_nonempty (U : opens α) : U ≠ ⊥ ↔ set.nonempty (U : set α) :=
by rw [ne.def, ← opens.not_nonempty_iff_eq_bot, not_not]

/-- An open set in the indiscrete topology is either empty or the whole space. -/
lemma eq_bot_or_top {α} [t : topological_space α] (h : t = ⊤) (U : opens α) : U = ⊥ ∨ U = ⊤ :=
begin
  simp only [← coe_inj],
  unfreezingI { subst h }, letI : topological_space α := ⊤,
  exact (is_open_top_iff _).1 U.2
end

/-- A set of `opens α` is a basis if the set of corresponding sets is a topological basis. -/
def is_basis (B : set (opens α)) : Prop := is_topological_basis ((coe : _ → set α) '' B)

lemma is_basis_iff_nbhd {B : set (opens α)} :
  is_basis B ↔ ∀ {U : opens α} {x}, x ∈ U → ∃ U' ∈ B, x ∈ U' ∧ U' ≤ U :=
begin
  split; intro h,
  { rintros ⟨sU, hU⟩ x hx,
    rcases h.mem_nhds_iff.mp (is_open.mem_nhds hU hx)
      with ⟨sV, ⟨⟨V, H₁, H₂⟩, hsV⟩⟩,
    refine ⟨V, H₁, _⟩,
    cases V, dsimp at H₂, subst H₂, exact hsV },
  { refine is_topological_basis_of_open_of_nhds _ _,
    { rintros sU ⟨U, ⟨H₁, rfl⟩⟩, exact U.2 },
    { intros x sU hx hsU,
      rcases @h (⟨sU, hsU⟩ : opens α) x hx with ⟨V, hV, H⟩,
      exact ⟨V, ⟨V, hV, rfl⟩, H⟩ } }
end

lemma is_basis_iff_cover {B : set (opens α)} :
  is_basis B ↔ ∀ U : opens α, ∃ Us ⊆ B, U = Sup Us :=
begin
  split,
  { intros hB U,
    refine ⟨{V : opens α | V ∈ B ∧ V ≤ U}, λ U hU, hU.left, _⟩,
    apply ext,
    rw [coe_Sup, hB.open_eq_sUnion' U.is_open],
    simp_rw [sUnion_eq_bUnion, Union, supr_and, supr_image],
    refl },
  { intro h,
    rw is_basis_iff_nbhd,
    intros U x hx,
    rcases h U with ⟨Us, hUs, rfl⟩,
    rcases mem_Sup.1 hx with ⟨U, Us, xU⟩,
    exact ⟨U, hUs Us, xU, le_Sup Us⟩ }
end

/-- If `α` has a basis consisting of compact opens, then an open set in `α` is compact open iff
  it is a finite union of some elements in the basis -/
lemma is_basis.is_compact_open_iff_eq_finite_Union
  {ι : Type*} (b : ι → opens α) (hb : is_basis (set.range b))
  (hb' : ∀ i, is_compact (b i : set α)) (U : set α) :
  is_compact U ∧ is_open U ↔ ∃ (s : set ι), s.finite ∧ U = ⋃ i ∈ s, b i :=
begin
  apply is_compact_open_iff_eq_finite_Union_of_is_topological_basis
    (λ i : ι, (b i).1),
  { convert hb, ext, simp },
  { exact hb' }
end

@[simp] lemma is_compact_element_iff (s : opens α) :
  complete_lattice.is_compact_element s ↔ is_compact (s : set α) :=
begin
  rw [is_compact_iff_finite_subcover, complete_lattice.is_compact_element_iff],
  refine ⟨_, λ H ι U hU, _⟩,
  { introv H hU hU',
    obtain ⟨t, ht⟩ := H ι (λ i, ⟨U i, hU i⟩) (by simpa),
    refine ⟨t, set.subset.trans ht _⟩,
    rw [coe_finset_sup, finset.sup_eq_supr],
    refl },
  { obtain ⟨t, ht⟩ := H (λ i, U i) (λ i, (U i).is_open)
      (by simpa using (show (s : set α) ⊆ ↑(supr U), from hU)),
    refine ⟨t, set.subset.trans ht _⟩,
    simp only [set.Union_subset_iff],
    show ∀ i ∈ t, U i ≤ t.sup U, from λ i, finset.le_sup }
end

/-- The preimage of an open set, as an open set. -/
def comap (f : C(α, β)) : frame_hom (opens β) (opens α) :=
{ to_fun := λ s, ⟨f ⁻¹' s, s.2.preimage f.continuous⟩,
  map_Sup' := λ s, ext $ by simp only [coe_Sup, preimage_Union, bUnion_image, coe_mk],
  map_inf' := λ a b, rfl,
  map_top' := rfl }

@[simp] lemma comap_id : comap (continuous_map.id α) = frame_hom.id _ :=
frame_hom.ext $ λ a, ext rfl

lemma comap_mono (f : C(α, β)) {s t : opens β} (h : s ≤ t) : comap f s ≤ comap f t :=
order_hom_class.mono (comap f) h

@[simp] lemma coe_comap (f : C(α, β)) (U : opens β) : ↑(comap f U) = f ⁻¹' U := rfl

protected lemma comap_comp (g : C(β, γ)) (f : C(α, β)) :
  comap (g.comp f) = (comap f).comp (comap g) := rfl

protected lemma comap_comap (g : C(β, γ)) (f : C(α, β)) (U : opens γ) :
  comap f (comap g U) = comap (g.comp f) U := rfl

lemma comap_injective [t0_space β] : injective (comap : C(α, β) → frame_hom (opens β) (opens α)) :=
λ f g h, continuous_map.ext $ λ a, inseparable.eq $ inseparable_iff_forall_open.2 $ λ s hs,
have comap f ⟨s, hs⟩ = comap g ⟨s, hs⟩, from fun_like.congr_fun h ⟨_, hs⟩,
show a ∈ f ⁻¹' s ↔ a ∈ g ⁻¹' s, from set.ext_iff.1 (coe_inj.2 this) a

/-- A homeomorphism induces an order-preserving equivalence on open sets, by taking comaps. -/
@[simps apply { fully_applied := ff }]
def _root_.homeomorph.opens_congr (f : α ≃ₜ β) : opens α ≃o opens β :=
{ to_fun := opens.comap f.symm.to_continuous_map,
  inv_fun := opens.comap f.to_continuous_map,
  left_inv := by { intro U, ext1, exact f.to_equiv.preimage_symm_preimage _ },
  right_inv := by { intro U, ext1, exact f.to_equiv.symm_preimage_preimage _ },
  map_rel_iff' := λ U V, by simp only [← set_like.coe_subset_coe];
    exact f.symm.surjective.preimage_subset_preimage_iff }

@[simp] lemma _root_.homeomorph.opens_congr_symm (f : α ≃ₜ β) :
  f.opens_congr.symm = f.symm.opens_congr :=
rfl

instance [finite α] : finite (opens α) := finite.of_injective _ set_like.coe_injective

end opens

/-- The open neighborhoods of a point. See also `opens` or `nhds`. -/
structure open_nhds_of (x : α) extends opens α :=
(mem' : x ∈ carrier)

namespace open_nhds_of

variables {x : α}

lemma to_opens_injective : injective (to_opens : open_nhds_of x → opens α)
| ⟨_, _⟩ ⟨_, _⟩ rfl := rfl

instance : set_like (open_nhds_of x) α :=
{ coe := λ U, U.1,
  coe_injective' := set_like.coe_injective.comp to_opens_injective }

instance can_lift_set : can_lift (set α) (open_nhds_of x) coe (λ s, is_open s ∧ x ∈ s) :=
⟨λ s hs, ⟨⟨⟨s, hs.1⟩, hs.2⟩, rfl⟩⟩

protected lemma mem (U : open_nhds_of x) : x ∈ U := U.mem'
protected lemma is_open (U : open_nhds_of x) : is_open (U : set α) := U.is_open'

instance : order_top (open_nhds_of x) :=
{ top := ⟨⊤, set.mem_univ _⟩,
  le_top := λ _, subset_univ _ }

instance : inhabited (open_nhds_of x) := ⟨⊤⟩

instance : has_inf (open_nhds_of x) := ⟨λ U V, ⟨U.1 ⊓ V.1, U.2, V.2⟩⟩

instance : has_sup (open_nhds_of x) := ⟨λ U V, ⟨U.1 ⊔ V.1, or.inl U.2⟩⟩

instance : distrib_lattice (open_nhds_of x) :=
to_opens_injective.distrib_lattice _ (λ _ _, rfl) (λ _ _, rfl)

lemma basis_nhds : (𝓝 x).has_basis (λ U : open_nhds_of x, true) coe :=
(nhds_basis_opens x).to_has_basis (λ U hU, ⟨⟨⟨U, hU.2⟩, hU.1⟩, trivial, subset.rfl⟩)
  (λ U _, ⟨U, ⟨⟨U.mem, U.is_open⟩, subset.rfl⟩⟩)

/-- Preimage of an open neighborhood of `f x` under a continuous map `f` as a `lattice_hom`. -/
def comap (f : C(α, β)) (x : α) : lattice_hom (open_nhds_of (f x)) (open_nhds_of x) :=
{ to_fun := λ U, ⟨opens.comap f U.1, U.mem⟩,
  map_sup' := λ U V, rfl,
  map_inf' := λ U V, rfl }

end open_nhds_of

end topological_space

namespace tactic

namespace auto_cases

/-- Find an `auto_cases_tac` which matches `topological_space.opens`. -/
meta def opens_find_tac : expr → option auto_cases_tac
| `(topological_space.opens _)     := tac_cases
| _ := none

end auto_cases

/-- A version of `tactic.auto_cases` that works for `topological_space.opens`. -/
@[hint_tactic]
meta def auto_cases_opens : tactic string :=
auto_cases tactic.auto_cases.opens_find_tac

end tactic


import .core

namespace tts ------------------------------------------------------------------
namespace exp ------------------------------------------------------------------
variables {V : Type} [decidable_eq V] -- Type of variable names
variables {v : V} -- Variable names
variables {x y : tagged V} -- Variables
variables {ea eb ed ef : exp V} -- Expressions

open occurs

/-- Get the free variables of an expression -/
def fv : exp V → finset (tagged V)
| (var bound _)  := ∅
| (var free x)   := {x}
| (app ef ea)    := fv ef ∪ fv ea
| (lam _ eb)     := fv eb
| (let_ _ ed eb) := fv ed ∪ fv eb

@[simp] theorem fv_var_bound : x ∉ fv (var bound y) :=
finset.not_mem_empty x

@[simp] theorem fv_var_free : x ∉ fv (var free y) ↔ x ≠ y :=
⟨finset.not_mem_singleton.mp,
 λ p h, absurd (finset.mem_of_mem_insert_of_ne h p) (finset.not_mem_empty x)⟩

@[simp] theorem fv_app : x ∉ fv (app ef ea) ↔ x ∉ fv ef ∧ x ∉ fv ea :=
finset.not_mem_union

@[simp] theorem fv_lam : x ∉ fv (lam v eb) ↔ x ∉ fv eb :=
⟨by rw fv; exact id, by rw fv; exact id⟩

@[simp] theorem fv_let_ : x ∉ fv (let_ v ed eb) ↔ x ∉ fv ed ∧ x ∉ fv eb :=
finset.not_mem_union

end /- namespace -/ exp --------------------------------------------------------
end /- namespace -/ tts --------------------------------------------------------


import basic

section
parameters (α : Type*) (F : Type*)

structure is_clone (C : set (op F α)) :=
(proj_closed : ∀ k, (λ (x : F → α), x k) ∈ C)
(comp_closed : ∀ f (g : F → op F α), f ∈ C → (∀ i, g i ∈ C) → (λ x, f (λ i, g i x)) ∈ C)

parameter (X : set (op F α))

-- The smallest clone containing X
inductive clo : set (op F α)
| proj (k) : clo (π k)
-- there are like two different ways to make this SEGFAULT lmao
| comp {f} {g : F → op F α} :
    f ∈ X → (∀ i, clo (g i)) → clo (λ x, f (λ i, g i x))

theorem clo_contains : X ⊆ clo :=
begin
  intros _ h,
  apply clo.comp h,
  apply clo.proj
end

theorem clo_is_clone : is_clone clo :=
{ proj_closed := clo.proj,
  comp_closed := begin
    intros _ _ fc gc,
    induction fc with _ f _ _ _ ih,
    { apply gc },
    { apply @clo.comp f,
      assumption,
      apply ih }
  end }

theorem clo_is_smallest (Y : set (op F α)) :
  is_clone Y → X ⊆ Y → clo ⊆ Y :=
begin
  intros hY hX f hf,
  induction hf,
  { apply hY.proj_closed },
  { apply hY.comp_closed,
    apply hX,
    repeat { assumption } }
end

end

section
parameters {S : signature} (A : algebra S)

-- TODO: relate clone of term operations to term algebra

end


import .category

namespace homotopy_theory.topological_spaces

open category_theory topological_space

--- The Sierpisnki space. It represents the (contravariant) functor
--- taking X to its set of open subsets.
def sierpinski : Top := Top.mk_ob (ulift Prop)

def opens_equiv (X : Top) : opens X ≃ (X ⟶ sierpinski) :=
⟨λ s, Top.mk_hom (λ x, ⟨x ∈ s.val⟩)
   (continuous.comp continuous_ulift_up (continuous_Prop.mpr s.property)),
 λ f, ⟨λ x, (f x).down, continuous_Prop.mp (by continuity)⟩,
 λ s, by ext; refl,
 λ f, by ext; refl⟩

lemma opens_equiv_nat {X Y : Top} (f : X ⟶ Y) (s : opens Y) :
  f ≫ opens_equiv Y s = opens_equiv X ⟨f ⁻¹' s, f.2 s.val s.property⟩ :=
rfl

--- The two-point space with the indiscrete topology. It represents
--- the (contravariant) functor taking X to its set of all subsets.
def prop_indisc := @Top.mk_ob (ulift Prop) ⊤

def set_equiv (X : Top) : set X ≃ (X ⟶ prop_indisc) :=
⟨λ s, Top.mk_hom (λ x, ⟨x ∈ s⟩) (by continuity),
 λ f, λ x, (f x).down,
 λ s, by ext; refl,
 λ f, by ext; refl⟩

lemma set_equiv_nat {X Y : Top} (f : X ⟶ Y) (s : set Y) :
  f ≫ set_equiv Y s = set_equiv X (f ⁻¹' s) :=
rfl

def forget_open : sierpinski ⟶ prop_indisc := Top.mk_hom id

instance forget_open_mono : mono forget_open :=
⟨λ X f g h, by ext1; apply Top.hom_congr h⟩

lemma forget_open_map {X : Top} (s : opens X) :
  opens_equiv X s ≫ forget_open = set_equiv X s.val :=
rfl

end homotopy_theory.topological_spaces


/-
Copyright (c) 2019 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.constructions
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Neighborhoods and continuity relative to a subset

This file defines relative versions

* `nhds_within`           of `nhds`
* `continuous_on`         of `continuous`
* `continuous_within_at`  of `continuous_at`

and proves their basic properties, including the relationships between
these restricted notions and the corresponding notions for the subtype
equipped with the subspace topology.

## Notation

* `𝓝 x`: the filter of neighborhoods of a point `x`;
* `𝓟 s`: the principal filter of a set `s`;
* `𝓝[s] x`: the filter `nhds_within x s` of neighborhoods of a point `x` within a set `s`.

-/

/-- The "neighborhood within" filter. Elements of `𝓝[s] a` are sets containing the
intersection of `s` and a neighborhood of `a`. -/
def nhds_within {α : Type u_1} [topological_space α] (a : α) (s : set α) : filter α :=
  nhds a ⊓ filter.principal s

@[simp] theorem nhds_bind_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} : (filter.bind (nhds a) fun (x : α) => nhds_within x s) = nhds_within a s :=
  Eq.trans filter.bind_inf_principal (congr_arg2 has_inf.inf nhds_bind_nhds rfl)

@[simp] theorem eventually_nhds_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} {p : α → Prop} : filter.eventually (fun (y : α) => filter.eventually (fun (x : α) => p x) (nhds_within y s)) (nhds a) ↔
  filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  iff.mp filter.ext_iff nhds_bind_nhds_within (set_of fun (x : α) => p x)

theorem eventually_nhds_within_iff {α : Type u_1} [topological_space α] {a : α} {s : set α} {p : α → Prop} : filter.eventually (fun (x : α) => p x) (nhds_within a s) ↔ filter.eventually (fun (x : α) => x ∈ s → p x) (nhds a) :=
  filter.eventually_inf_principal

@[simp] theorem eventually_nhds_within_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} {p : α → Prop} : filter.eventually (fun (y : α) => filter.eventually (fun (x : α) => p x) (nhds_within y s)) (nhds_within a s) ↔
  filter.eventually (fun (x : α) => p x) (nhds_within a s) := sorry

theorem nhds_within_eq {α : Type u_1} [topological_space α] (a : α) (s : set α) : nhds_within a s =
  infi fun (t : set α) => infi fun (H : t ∈ set_of fun (t : set α) => a ∈ t ∧ is_open t) => filter.principal (t ∩ s) :=
  filter.has_basis.eq_binfi (filter.has_basis.inf_principal (nhds_basis_opens a) s)

theorem nhds_within_univ {α : Type u_1} [topological_space α] (a : α) : nhds_within a set.univ = nhds a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nhds_within a set.univ = nhds a)) (nhds_within.equations._eqn_1 a set.univ)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ filter.principal set.univ = nhds a)) filter.principal_univ))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ ⊤ = nhds a)) inf_top_eq)) (Eq.refl (nhds a))))

theorem nhds_within_has_basis {α : Type u_1} {β : Type u_2} [topological_space α] {p : β → Prop} {s : β → set α} {a : α} (h : filter.has_basis (nhds a) p s) (t : set α) : filter.has_basis (nhds_within a t) p fun (i : β) => s i ∩ t :=
  filter.has_basis.inf_principal h t

theorem nhds_within_basis_open {α : Type u_1} [topological_space α] (a : α) (t : set α) : filter.has_basis (nhds_within a t) (fun (u : set α) => a ∈ u ∧ is_open u) fun (u : set α) => u ∩ t :=
  nhds_within_has_basis (nhds_basis_opens a) t

theorem mem_nhds_within {α : Type u_1} [topological_space α] {t : set α} {a : α} {s : set α} : t ∈ nhds_within a s ↔ ∃ (u : set α), is_open u ∧ a ∈ u ∧ u ∩ s ⊆ t := sorry

theorem mem_nhds_within_iff_exists_mem_nhds_inter {α : Type u_1} [topological_space α] {t : set α} {a : α} {s : set α} : t ∈ nhds_within a s ↔ ∃ (u : set α), ∃ (H : u ∈ nhds a), u ∩ s ⊆ t :=
  filter.has_basis.mem_iff (nhds_within_has_basis (filter.basis_sets (nhds a)) s)

theorem diff_mem_nhds_within_compl {X : Type u_1} [topological_space X] {x : X} {s : set X} (hs : s ∈ nhds x) (t : set X) : s \ t ∈ nhds_within x (tᶜ) :=
  filter.diff_mem_inf_principal_compl hs t

theorem nhds_of_nhds_within_of_nhds {α : Type u_1} [topological_space α] {s : set α} {t : set α} {a : α} (h1 : s ∈ nhds a) (h2 : t ∈ nhds_within a s) : t ∈ nhds a := sorry

theorem mem_nhds_within_of_mem_nhds {α : Type u_1} [topological_space α] {s : set α} {t : set α} {a : α} (h : s ∈ nhds a) : s ∈ nhds_within a t :=
  filter.mem_inf_sets_of_left h

theorem self_mem_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} : s ∈ nhds_within a s :=
  filter.mem_inf_sets_of_right (filter.mem_principal_self s)

theorem inter_mem_nhds_within {α : Type u_1} [topological_space α] (s : set α) {t : set α} {a : α} (h : t ∈ nhds a) : s ∩ t ∈ nhds_within a s :=
  filter.inter_mem_sets (filter.mem_inf_sets_of_right (filter.mem_principal_self s)) (filter.mem_inf_sets_of_left h)

theorem nhds_within_mono {α : Type u_1} [topological_space α] (a : α) {s : set α} {t : set α} (h : s ⊆ t) : nhds_within a s ≤ nhds_within a t :=
  inf_le_inf_left (nhds a) (iff.mpr filter.principal_mono h)

theorem pure_le_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} (ha : a ∈ s) : pure a ≤ nhds_within a s :=
  le_inf (pure_le_nhds a) (iff.mpr filter.le_principal_iff ha)

theorem mem_of_mem_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α} (ha : a ∈ s) (ht : t ∈ nhds_within a s) : a ∈ t :=
  pure_le_nhds_within ha ht

theorem filter.eventually.self_of_nhds_within {α : Type u_1} [topological_space α] {p : α → Prop} {s : set α} {x : α} (h : filter.eventually (fun (y : α) => p y) (nhds_within x s)) (hx : x ∈ s) : p x :=
  mem_of_mem_nhds_within hx h

theorem tendsto_const_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {l : filter β} {s : set α} {a : α} (ha : a ∈ s) : filter.tendsto (fun (x : β) => a) l (nhds_within a s) :=
  filter.tendsto.mono_right filter.tendsto_const_pure (pure_le_nhds_within ha)

theorem nhds_within_restrict'' {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α} (h : t ∈ nhds_within a s) : nhds_within a s = nhds_within a (s ∩ t) :=
  le_antisymm (le_inf inf_le_left (iff.mpr filter.le_principal_iff (filter.inter_mem_sets self_mem_nhds_within h)))
    (inf_le_inf_left (nhds a) (iff.mpr filter.principal_mono (set.inter_subset_left s t)))

theorem nhds_within_restrict' {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α} (h : t ∈ nhds a) : nhds_within a s = nhds_within a (s ∩ t) :=
  nhds_within_restrict'' s (filter.mem_inf_sets_of_left h)

theorem nhds_within_restrict {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α} (h₀ : a ∈ t) (h₁ : is_open t) : nhds_within a s = nhds_within a (s ∩ t) :=
  nhds_within_restrict' s (mem_nhds_sets h₁ h₀)

theorem nhds_within_le_of_mem {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α} (h : s ∈ nhds_within a t) : nhds_within a t ≤ nhds_within a s := sorry

theorem nhds_within_eq_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α} {u : set α} (h₀ : a ∈ s) (h₁ : is_open s) (h₂ : t ∩ s = u ∩ s) : nhds_within a t = nhds_within a u := sorry

theorem nhds_within_eq_of_open {α : Type u_1} [topological_space α] {a : α} {s : set α} (h₀ : a ∈ s) (h₁ : is_open s) : nhds_within a s = nhds a :=
  iff.mpr inf_eq_left (iff.mpr filter.le_principal_iff (mem_nhds_sets h₁ h₀))

@[simp] theorem nhds_within_empty {α : Type u_1} [topological_space α] (a : α) : nhds_within a ∅ = ⊥ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nhds_within a ∅ = ⊥)) (nhds_within.equations._eqn_1 a ∅)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ filter.principal ∅ = ⊥)) filter.principal_empty))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ ⊥ = ⊥)) inf_bot_eq)) (Eq.refl ⊥)))

theorem nhds_within_union {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) : nhds_within a (s ∪ t) = nhds_within a s ⊔ nhds_within a t := sorry

theorem nhds_within_inter {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) : nhds_within a (s ∩ t) = nhds_within a s ⊓ nhds_within a t := sorry

theorem nhds_within_inter' {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) : nhds_within a (s ∩ t) = nhds_within a s ⊓ filter.principal t := sorry

@[simp] theorem nhds_within_singleton {α : Type u_1} [topological_space α] (a : α) : nhds_within a (singleton a) = pure a := sorry

@[simp] theorem nhds_within_insert {α : Type u_1} [topological_space α] (a : α) (s : set α) : nhds_within a (insert a s) = pure a ⊔ nhds_within a s := sorry

theorem mem_nhds_within_insert {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α} : t ∈ nhds_within a (insert a s) ↔ a ∈ t ∧ t ∈ nhds_within a s := sorry

theorem insert_mem_nhds_within_insert {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α} (h : t ∈ nhds_within a s) : insert a t ∈ nhds_within a (insert a s) := sorry

theorem nhds_within_prod_eq {α : Type u_1} [topological_space α] {β : Type u_2} [topological_space β] (a : α) (b : β) (s : set α) (t : set β) : nhds_within (a, b) (set.prod s t) = filter.prod (nhds_within a s) (nhds_within b t) := sorry

theorem nhds_within_prod {α : Type u_1} [topological_space α] {β : Type u_2} [topological_space β] {s : set α} {u : set α} {t : set β} {v : set β} {a : α} {b : β} (hu : u ∈ nhds_within a s) (hv : v ∈ nhds_within b t) : set.prod u v ∈ nhds_within (a, b) (set.prod s t) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (set.prod u v ∈ nhds_within (a, b) (set.prod s t))) (nhds_within_prod_eq a b s t)))
    (filter.prod_mem_prod hu hv)

theorem tendsto_if_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {g : α → β} {p : α → Prop} [decidable_pred p] {a : α} {s : set α} {l : filter β} (h₀ : filter.tendsto f (nhds_within a (s ∩ p)) l) (h₁ : filter.tendsto g (nhds_within a (s ∩ set_of fun (x : α) => ¬p x)) l) : filter.tendsto (fun (x : α) => ite (p x) (f x) (g x)) (nhds_within a s) l := sorry

theorem map_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] (f : α → β) (a : α) (s : set α) : filter.map f (nhds_within a s) =
  infi
    fun (t : set α) => infi fun (H : t ∈ set_of fun (t : set α) => a ∈ t ∧ is_open t) => filter.principal (f '' (t ∩ s)) :=
  filter.has_basis.eq_binfi (filter.has_basis.map f (nhds_within_basis_open a s))

theorem tendsto_nhds_within_mono_left {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {a : α} {s : set α} {t : set α} {l : filter β} (hst : s ⊆ t) (h : filter.tendsto f (nhds_within a t) l) : filter.tendsto f (nhds_within a s) l :=
  filter.tendsto.mono_left h (nhds_within_mono a hst)

theorem tendsto_nhds_within_mono_right {α : Type u_1} {β : Type u_2} [topological_space α] {f : β → α} {l : filter β} {a : α} {s : set α} {t : set α} (hst : s ⊆ t) (h : filter.tendsto f l (nhds_within a s)) : filter.tendsto f l (nhds_within a t) :=
  filter.tendsto.mono_right h (nhds_within_mono a hst)

theorem tendsto_nhds_within_of_tendsto_nhds {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {a : α} {s : set α} {l : filter β} (h : filter.tendsto f (nhds a) l) : filter.tendsto f (nhds_within a s) l :=
  filter.tendsto.mono_left h inf_le_left

theorem principal_subtype {α : Type u_1} (s : set α) (t : set (Subtype fun (x : α) => x ∈ s)) : filter.principal t = filter.comap coe (filter.principal (coe '' t)) := sorry

theorem mem_closure_iff_nhds_within_ne_bot {α : Type u_1} [topological_space α] {s : set α} {x : α} : x ∈ closure s ↔ filter.ne_bot (nhds_within x s) :=
  mem_closure_iff_cluster_pt

theorem nhds_within_ne_bot_of_mem {α : Type u_1} [topological_space α] {s : set α} {x : α} (hx : x ∈ s) : filter.ne_bot (nhds_within x s) :=
  iff.mp mem_closure_iff_nhds_within_ne_bot (subset_closure hx)

theorem is_closed.mem_of_nhds_within_ne_bot {α : Type u_1} [topological_space α] {s : set α} (hs : is_closed s) {x : α} (hx : filter.ne_bot (nhds_within x s)) : x ∈ s := sorry

theorem dense_range.nhds_within_ne_bot {α : Type u_1} [topological_space α] {ι : Type u_2} {f : ι → α} (h : dense_range f) (x : α) : filter.ne_bot (nhds_within x (set.range f)) :=
  iff.mp mem_closure_iff_cluster_pt (h x)

theorem eventually_eq_nhds_within_iff {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {g : α → β} {s : set α} {a : α} : filter.eventually_eq (nhds_within a s) f g ↔ filter.eventually (fun (x : α) => x ∈ s → f x = g x) (nhds a) :=
  filter.mem_inf_principal

theorem eventually_eq_nhds_within_of_eq_on {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {g : α → β} {s : set α} {a : α} (h : set.eq_on f g s) : filter.eventually_eq (nhds_within a s) f g :=
  filter.mem_inf_sets_of_right h

theorem set.eq_on.eventually_eq_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {g : α → β} {s : set α} {a : α} (h : set.eq_on f g s) : filter.eventually_eq (nhds_within a s) f g :=
  eventually_eq_nhds_within_of_eq_on h

theorem tendsto_nhds_within_congr {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β} {g : α → β} {s : set α} {a : α} {l : filter β} (hfg : ∀ (x : α), x ∈ s → f x = g x) (hf : filter.tendsto f (nhds_within a s) l) : filter.tendsto g (nhds_within a s) l :=
  iff.mp (filter.tendsto_congr' (eventually_eq_nhds_within_of_eq_on hfg)) hf

theorem eventually_nhds_with_of_forall {α : Type u_1} [topological_space α] {s : set α} {a : α} {p : α → Prop} (h : ∀ (x : α), x ∈ s → p x) : filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  filter.mem_inf_sets_of_right h

theorem tendsto_nhds_within_of_tendsto_nhds_of_eventually_within {α : Type u_1} [topological_space α] {β : Type u_2} {a : α} {l : filter β} {s : set α} (f : β → α) (h1 : filter.tendsto f l (nhds a)) (h2 : filter.eventually (fun (x : β) => f x ∈ s) l) : filter.tendsto f l (nhds_within a s) :=
  iff.mpr filter.tendsto_inf { left := h1, right := iff.mpr filter.tendsto_principal h2 }

theorem filter.eventually_eq.eq_of_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {s : set α} {f : α → β} {g : α → β} {a : α} (h : filter.eventually_eq (nhds_within a s) f g) (hmem : a ∈ s) : f a = g a :=
  filter.eventually.self_of_nhds_within h hmem

theorem eventually_nhds_within_of_eventually_nhds {α : Type u_1} [topological_space α] {s : set α} {a : α} {p : α → Prop} (h : filter.eventually (fun (x : α) => p x) (nhds a)) : filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  mem_nhds_within_of_mem_nhds h

/-!
### `nhds_within` and subtypes
-/

theorem mem_nhds_within_subtype {α : Type u_1} [topological_space α] {s : set α} {a : Subtype fun (x : α) => x ∈ s} {t : set (Subtype fun (x : α) => x ∈ s)} {u : set (Subtype fun (x : α) => x ∈ s)} : t ∈ nhds_within a u ↔ t ∈ filter.comap coe (nhds_within (↑a) (coe '' u)) := sorry

theorem nhds_within_subtype {α : Type u_1} [topological_space α] (s : set α) (a : Subtype fun (x : α) => x ∈ s) (t : set (Subtype fun (x : α) => x ∈ s)) : nhds_within a t = filter.comap coe (nhds_within (↑a) (coe '' t)) :=
  filter.ext fun (u : set (Subtype fun (x : α) => x ∈ s)) => mem_nhds_within_subtype

theorem nhds_within_eq_map_subtype_coe {α : Type u_1} [topological_space α] {s : set α} {a : α} (h : a ∈ s) : nhds_within a s = filter.map coe (nhds { val := a, property := h }) := sorry

theorem tendsto_nhds_within_iff_subtype {α : Type u_1} {β : Type u_2} [topological_space α] {s : set α} {a : α} (h : a ∈ s) (f : α → β) (l : filter β) : filter.tendsto f (nhds_within a s) l ↔ filter.tendsto (set.restrict f s) (nhds { val := a, property := h }) l := sorry

/-- A function between topological spaces is continuous at a point `x₀` within a subset `s`
if `f x` tends to `f x₀` when `x` tends to `x₀` while staying within `s`. -/
def continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) (s : set α) (x : α) :=
  filter.tendsto f (nhds_within x s) (nhds (f x))

/-- If a function is continuous within `s` at `x`, then it tends to `f x` within `s` by definition.
We register this fact for use with the dot notation, especially to use `tendsto.comp` as
`continuous_within_at.comp` will have a different meaning. -/
theorem continuous_within_at.tendsto {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) : filter.tendsto f (nhds_within x s) (nhds (f x)) :=
  h

/-- A function between topological spaces is continuous on a subset `s`
when it's continuous at every point of `s` within `s`. -/
def continuous_on {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) (s : set α) :=
  ∀ (x : α), x ∈ s → continuous_within_at f s x

theorem continuous_on.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (hf : continuous_on f s) (hx : x ∈ s) : continuous_within_at f s x :=
  hf x hx

theorem continuous_within_at_univ {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) (x : α) : continuous_within_at f set.univ x ↔ continuous_at f x := sorry

theorem continuous_within_at_iff_continuous_at_restrict {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) {x : α} {s : set α} (h : x ∈ s) : continuous_within_at f s x ↔ continuous_at (set.restrict f s) { val := x, property := h } :=
  tendsto_nhds_within_iff_subtype h f (nhds (f x))

theorem continuous_within_at.tendsto_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β} (h : continuous_within_at f s x) (ht : set.maps_to f s t) : filter.tendsto f (nhds_within x s) (nhds_within (f x) t) :=
  iff.mpr filter.tendsto_inf
    { left := h,
      right := iff.mpr filter.tendsto_principal (filter.mem_inf_sets_of_right (iff.mpr filter.mem_principal_sets ht)) }

theorem continuous_within_at.tendsto_nhds_within_image {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} (h : continuous_within_at f s x) : filter.tendsto f (nhds_within x s) (nhds_within (f x) (f '' s)) :=
  continuous_within_at.tendsto_nhds_within h (set.maps_to_image f s)

theorem continuous_within_at.prod_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} [topological_space α] [topological_space β] [topological_space γ] [topological_space δ] {f : α → γ} {g : β → δ} {s : set α} {t : set β} {x : α} {y : β} (hf : continuous_within_at f s x) (hg : continuous_within_at g t y) : continuous_within_at (prod.map f g) (set.prod s t) (x, y) := sorry

theorem continuous_on_iff {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} : continuous_on f s ↔
  ∀ (x : α), x ∈ s → ∀ (t : set β), is_open t → f x ∈ t → ∃ (u : set α), is_open u ∧ x ∈ u ∧ u ∩ s ⊆ f ⁻¹' t := sorry

theorem continuous_on_iff_continuous_restrict {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} : continuous_on f s ↔ continuous (set.restrict f s) := sorry

theorem continuous_on_iff' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} : continuous_on f s ↔ ∀ (t : set β), is_open t → ∃ (u : set α), is_open u ∧ f ⁻¹' t ∩ s = u ∩ s := sorry

theorem continuous_on_iff_is_closed {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} : continuous_on f s ↔ ∀ (t : set β), is_closed t → ∃ (u : set α), is_closed u ∧ f ⁻¹' t ∩ s = u ∩ s := sorry

theorem continuous_on.prod_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} [topological_space α] [topological_space β] [topological_space γ] [topological_space δ] {f : α → γ} {g : β → δ} {s : set α} {t : set β} (hf : continuous_on f s) (hg : continuous_on g t) : continuous_on (prod.map f g) (set.prod s t) := sorry

theorem continuous_on_empty {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) : continuous_on f ∅ :=
  fun (x : α) => false.elim

theorem nhds_within_le_comap {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {x : α} {s : set α} {f : α → β} (ctsf : continuous_within_at f s x) : nhds_within x s ≤ filter.comap f (nhds_within (f x) (f '' s)) :=
  iff.mp filter.map_le_iff_le_comap (continuous_within_at.tendsto_nhds_within_image ctsf)

theorem continuous_within_at_iff_ptendsto_res {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (f : α → β) {x : α} {s : set α} : continuous_within_at f s x ↔ filter.ptendsto (pfun.res f s) (nhds x) (nhds (f x)) :=
  filter.tendsto_iff_ptendsto (nhds x) (nhds (f x)) s f

theorem continuous_iff_continuous_on_univ {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} : continuous f ↔ continuous_on f set.univ := sorry

theorem continuous_within_at.mono {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : continuous_within_at f t x) (hs : s ⊆ t) : continuous_within_at f s x :=
  filter.tendsto.mono_left h (nhds_within_mono x hs)

theorem continuous_within_at.mono_of_mem {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : continuous_within_at f t x) (hs : t ∈ nhds_within x s) : continuous_within_at f s x :=
  filter.tendsto.mono_left h (nhds_within_le_of_mem hs)

theorem continuous_within_at_inter' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : t ∈ nhds_within x s) : continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x := sorry

theorem continuous_within_at_inter {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : t ∈ nhds x) : continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x := sorry

theorem continuous_within_at_union {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} : continuous_within_at f (s ∪ t) x ↔ continuous_within_at f s x ∧ continuous_within_at f t x := sorry

theorem continuous_within_at.union {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (hs : continuous_within_at f s x) (ht : continuous_within_at f t x) : continuous_within_at f (s ∪ t) x :=
  iff.mpr continuous_within_at_union { left := hs, right := ht }

theorem continuous_within_at.mem_closure_image {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) (hx : x ∈ closure s) : f x ∈ closure (f '' s) :=
  mem_closure_of_tendsto h (filter.mem_sets_of_superset self_mem_nhds_within (set.subset_preimage_image f s))

theorem continuous_within_at.mem_closure {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} {A : set β} (h : continuous_within_at f s x) (hx : x ∈ closure s) (hA : s ⊆ f ⁻¹' A) : f x ∈ closure A :=
  closure_mono (iff.mpr set.image_subset_iff hA) (continuous_within_at.mem_closure_image h hx)

theorem continuous_within_at.image_closure {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} (hf : ∀ (x : α), x ∈ closure s → continuous_within_at f s x) : f '' closure s ⊆ closure (f '' s) := sorry

@[simp] theorem continuous_within_at_singleton {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} : continuous_within_at f (singleton x) x := sorry

@[simp] theorem continuous_within_at_insert_self {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} : continuous_within_at f (insert x s) x ↔ continuous_within_at f s x := sorry

theorem continuous_within_at.insert_self {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} : continuous_within_at f s x → continuous_within_at f (insert x s) x :=
  iff.mpr continuous_within_at_insert_self

theorem continuous_within_at.diff_iff {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (ht : continuous_within_at f t x) : continuous_within_at f (s \ t) x ↔ continuous_within_at f s x := sorry

@[simp] theorem continuous_within_at_diff_self {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} : continuous_within_at f (s \ singleton x) x ↔ continuous_within_at f s x :=
  continuous_within_at.diff_iff continuous_within_at_singleton

theorem is_open_map.continuous_on_image_of_left_inv_on {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} (h : is_open_map (set.restrict f s)) {finv : β → α} (hleft : set.left_inv_on finv f s) : continuous_on finv (f '' s) := sorry

theorem is_open_map.continuous_on_range_of_left_inverse {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} (hf : is_open_map f) {finv : β → α} (hleft : function.left_inverse finv f) : continuous_on finv (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (continuous_on finv (set.range f))) (Eq.symm set.image_univ)))
    (is_open_map.continuous_on_image_of_left_inv_on (is_open_map.restrict hf is_open_univ)
      fun (x : α) (_x : x ∈ set.univ) => hleft x)

theorem continuous_on.congr_mono {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {g : α → β} {s : set α} {s₁ : set α} (h : continuous_on f s) (h' : set.eq_on g f s₁) (h₁ : s₁ ⊆ s) : continuous_on g s₁ := sorry

theorem continuous_on.congr {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {g : α → β} {s : set α} (h : continuous_on f s) (h' : set.eq_on g f s) : continuous_on g s :=
  continuous_on.congr_mono h h' (set.subset.refl s)

theorem continuous_on_congr {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {g : α → β} {s : set α} (h' : set.eq_on g f s) : continuous_on g s ↔ continuous_on f s :=
  { mp := fun (h : continuous_on g s) => continuous_on.congr h (set.eq_on.symm h'),
    mpr := fun (h : continuous_on f s) => continuous_on.congr h h' }

theorem continuous_at.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_at f x) : continuous_within_at f s x :=
  continuous_within_at.mono (iff.mpr (continuous_within_at_univ f x) h) (set.subset_univ s)

theorem continuous_within_at.continuous_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) (hs : s ∈ nhds x) : continuous_at f x := sorry

theorem continuous_on.continuous_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_on f s) (hx : s ∈ nhds x) : continuous_at f x :=
  continuous_within_at.continuous_at (h x (mem_of_nhds hx)) hx

theorem continuous_within_at.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β} {x : α} (hg : continuous_within_at g t (f x)) (hf : continuous_within_at f s x) (h : s ⊆ f ⁻¹' t) : continuous_within_at (g ∘ f) s x := sorry

theorem continuous_within_at.comp' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β} {x : α} (hg : continuous_within_at g t (f x)) (hf : continuous_within_at f s x) : continuous_within_at (g ∘ f) (s ∩ f ⁻¹' t) x :=
  continuous_within_at.comp hg (continuous_within_at.mono hf (set.inter_subset_left s (f ⁻¹' t)))
    (set.inter_subset_right s (f ⁻¹' t))

theorem continuous_on.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β} (hg : continuous_on g t) (hf : continuous_on f s) (h : s ⊆ f ⁻¹' t) : continuous_on (g ∘ f) s :=
  fun (x : α) (hx : x ∈ s) => continuous_within_at.comp (hg (f x) (h hx)) (hf x hx) h

theorem continuous_on.mono {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set α} (hf : continuous_on f s) (h : t ⊆ s) : continuous_on f t :=
  fun (x : α) (hx : x ∈ t) => filter.tendsto.mono_left (hf x (h hx)) (nhds_within_mono x h)

theorem continuous_on.comp' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β} (hg : continuous_on g t) (hf : continuous_on f s) : continuous_on (g ∘ f) (s ∩ f ⁻¹' t) :=
  continuous_on.comp hg (continuous_on.mono hf (set.inter_subset_left s (f ⁻¹' t))) (set.inter_subset_right s (f ⁻¹' t))

theorem continuous.continuous_on {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} (h : continuous f) : continuous_on f s :=
  continuous_on.mono (eq.mp (Eq._oldrec (Eq.refl (continuous f)) (propext continuous_iff_continuous_on_univ)) h)
    (set.subset_univ s)

theorem continuous.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous f) : continuous_within_at f s x :=
  continuous_at.continuous_within_at (continuous.continuous_at h)

theorem continuous.comp_continuous_on {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} (hg : continuous g) (hf : continuous_on f s) : continuous_on (g ∘ f) s :=
  continuous_on.comp (continuous.continuous_on hg) hf set.subset_preimage_univ

theorem continuous_on.comp_continuous {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set β} (hg : continuous_on g s) (hf : continuous f) (hs : ∀ (x : α), f x ∈ s) : continuous (g ∘ f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (continuous (g ∘ f))) (propext continuous_iff_continuous_on_univ)))
    (continuous_on.comp hg (eq.mp (Eq._oldrec (Eq.refl (continuous f)) (propext continuous_iff_continuous_on_univ)) hf)
      fun (x : α) (_x : x ∈ set.univ) => hs x)

theorem continuous_within_at.preimage_mem_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β} (h : continuous_within_at f s x) (ht : t ∈ nhds (f x)) : f ⁻¹' t ∈ nhds_within x s :=
  h ht

theorem continuous_within_at.preimage_mem_nhds_within' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β} (h : continuous_within_at f s x) (ht : t ∈ nhds_within (f x) (f '' s)) : f ⁻¹' t ∈ nhds_within x s := sorry

theorem continuous_within_at.congr_of_eventually_eq {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {f₁ : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) (h₁ : filter.eventually_eq (nhds_within x s) f₁ f) (hx : f₁ x = f x) : continuous_within_at f₁ s x := sorry

theorem continuous_within_at.congr {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {f₁ : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) (h₁ : ∀ (y : α), y ∈ s → f₁ y = f y) (hx : f₁ x = f x) : continuous_within_at f₁ s x :=
  continuous_within_at.congr_of_eventually_eq h (filter.mem_sets_of_superset self_mem_nhds_within h₁) hx

theorem continuous_within_at.congr_mono {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {g : α → β} {s : set α} {s₁ : set α} {x : α} (h : continuous_within_at f s x) (h' : set.eq_on g f s₁) (h₁ : s₁ ⊆ s) (hx : g x = f x) : continuous_within_at g s₁ x :=
  continuous_within_at.congr (continuous_within_at.mono h h₁) h' hx

theorem continuous_on_const {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set α} {c : β} : continuous_on (fun (x : α) => c) s :=
  continuous.continuous_on continuous_const

theorem continuous_within_at_const {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {b : β} {s : set α} {x : α} : continuous_within_at (fun (_x : α) => b) s x :=
  continuous.continuous_within_at continuous_const

theorem continuous_on_id {α : Type u_1} [topological_space α] {s : set α} : continuous_on id s :=
  continuous.continuous_on continuous_id

theorem continuous_within_at_id {α : Type u_1} [topological_space α] {s : set α} {x : α} : continuous_within_at id s x :=
  continuous.continuous_within_at continuous_id

theorem continuous_on_open_iff {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} (hs : is_open s) : continuous_on f s ↔ ∀ (t : set β), is_open t → is_open (s ∩ f ⁻¹' t) := sorry

theorem continuous_on.preimage_open_of_open {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s) (hs : is_open s) (ht : is_open t) : is_open (s ∩ f ⁻¹' t) :=
  iff.mp (continuous_on_open_iff hs) hf t ht

theorem continuous_on.preimage_closed_of_closed {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s) (hs : is_closed s) (ht : is_closed t) : is_closed (s ∩ f ⁻¹' t) := sorry

theorem continuous_on.preimage_interior_subset_interior_preimage {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s) (hs : is_open s) : s ∩ f ⁻¹' interior t ⊆ s ∩ interior (f ⁻¹' t) := sorry

theorem continuous_on_of_locally_continuous_on {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} (h : ∀ (x : α), x ∈ s → ∃ (t : set α), is_open t ∧ x ∈ t ∧ continuous_on f (s ∩ t)) : continuous_on f s := sorry

theorem continuous_on_open_of_generate_from {α : Type u_1} [topological_space α] {β : Type u_2} {s : set α} {T : set (set β)} {f : α → β} (hs : is_open s) (h : ∀ (t : set β), t ∈ T → is_open (s ∩ f ⁻¹' t)) : continuous_on f s := sorry

theorem continuous_within_at.prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : α → γ} {s : set α} {x : α} (hf : continuous_within_at f s x) (hg : continuous_within_at g s x) : continuous_within_at (fun (x : α) => (f x, g x)) s x :=
  filter.tendsto.prod_mk_nhds hf hg

theorem continuous_on.prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : α → γ} {s : set α} (hf : continuous_on f s) (hg : continuous_on g s) : continuous_on (fun (x : α) => (f x, g x)) s :=
  fun (x : α) (hx : x ∈ s) => continuous_within_at.prod (hf x hx) (hg x hx)

theorem inducing.continuous_on_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : β → γ} (hg : inducing g) {s : set α} : continuous_on f s ↔ continuous_on (g ∘ f) s := sorry

theorem embedding.continuous_on_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : β → γ} (hg : embedding g) {s : set α} : continuous_on f s ↔ continuous_on (g ∘ f) s :=
  inducing.continuous_on_iff (embedding.to_inducing hg)

theorem continuous_within_at_of_not_mem_closure {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {f : α → β} {s : set α} {x : α} : ¬x ∈ closure s → continuous_within_at f s x := sorry

theorem continuous_on_if' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set α} {p : α → Prop} {f : α → β} {g : α → β} {h : (a : α) → Decidable (p a)} (hpf : ∀ (a : α),
  a ∈ s ∩ frontier (set_of fun (a : α) => p a) →
    filter.tendsto f (nhds_within a (s ∩ set_of fun (a : α) => p a)) (nhds (ite (p a) (f a) (g a)))) (hpg : ∀ (a : α),
  a ∈ s ∩ frontier (set_of fun (a : α) => p a) →
    filter.tendsto g (nhds_within a (s ∩ set_of fun (a : α) => ¬p a)) (nhds (ite (p a) (f a) (g a)))) (hf : continuous_on f (s ∩ set_of fun (a : α) => p a)) (hg : continuous_on g (s ∩ set_of fun (a : α) => ¬p a)) : continuous_on (fun (a : α) => ite (p a) (f a) (g a)) s := sorry

theorem continuous_on_if {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {p : α → Prop} {h : (a : α) → Decidable (p a)} {s : set α} {f : α → β} {g : α → β} (hp : ∀ (a : α), a ∈ s ∩ frontier (set_of fun (a : α) => p a) → f a = g a) (hf : continuous_on f (s ∩ closure (set_of fun (a : α) => p a))) (hg : continuous_on g (s ∩ closure (set_of fun (a : α) => ¬p a))) : continuous_on (fun (a : α) => ite (p a) (f a) (g a)) s := sorry

theorem continuous_if' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {p : α → Prop} {f : α → β} {g : α → β} {h : (a : α) → Decidable (p a)} (hpf : ∀ (a : α),
  a ∈ frontier (set_of fun (x : α) => p x) →
    filter.tendsto f (nhds_within a (set_of fun (x : α) => p x)) (nhds (ite (p a) (f a) (g a)))) (hpg : ∀ (a : α),
  a ∈ frontier (set_of fun (x : α) => p x) →
    filter.tendsto g (nhds_within a (set_of fun (x : α) => ¬p x)) (nhds (ite (p a) (f a) (g a)))) (hf : continuous_on f (set_of fun (x : α) => p x)) (hg : continuous_on g (set_of fun (x : α) => ¬p x)) : continuous fun (a : α) => ite (p a) (f a) (g a) := sorry

theorem continuous_on_fst {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set (α × β)} : continuous_on prod.fst s :=
  continuous.continuous_on continuous_fst

theorem continuous_within_at_fst {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set (α × β)} {p : α × β} : continuous_within_at prod.fst s p :=
  continuous.continuous_within_at continuous_fst

theorem continuous_on_snd {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set (α × β)} : continuous_on prod.snd s :=
  continuous.continuous_on continuous_snd

theorem continuous_within_at_snd {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] {s : set (α × β)} {p : α × β} : continuous_within_at prod.snd s p :=
  continuous.continuous_within_at continuous_snd