import tactic

universes u v

structure edge : Type 1 :=
( op_type : Type )
( type : Type )
( R : op_type → type → Prop )

namespace edge

def op (E : edge) : edge :=
{ op_type := E.type,
  type := E.op_type,
  R := λ x y, E.R y x }

structure hom (X Y : edge) : Type :=
( op_hom : Y.op_type → X.op_type )
( hom : X.type → Y.type )
( adj : ∀ (x : X.type) (y : Y.op_type), X.R (op_hom y) x ↔ Y.R y (hom x) )

def functional (E : edge) : Prop :=
∃ f : E.op_type → E.type, ∀ x y, E.R x y ↔ f x = y

def id (X : edge) : hom X X :=
{ op_hom := id,
  hom := id,
  adj := λ _ _, iff.rfl }

def comp {X Y Z : edge} (f : hom Y Z) (g : hom X Y) : hom X Z :=
{ op_hom := g.op_hom ∘ f.op_hom,
  hom := f.hom ∘ g.hom,
  adj := λ x y, by simp [f.adj, g.adj] }

def pullback (E : edge) : Type := { x : E.op_type × E.type // E.R x.1 x.2 }

def id_edge (X : Type) : edge :=
{ op_type := X,
  type := X,
  R := eq }

def of_fun {X Y : Type} (f : X → Y) : edge :=
{ op_type := X,
  type := Y,
  R := λ x y, f x = y }

def id_to_of_fun {X Y : Type} (f : X → Y) : hom (id_edge Y) (of_fun f) :=
{ op_hom := f,
  hom := _root_.id,
  adj := begin
    intros, refl,
  end }

-- def thing (X : Type) : Type :=
-- Π (f : Π x : X, {l : list X // x ∈ l}) (x : X), {l : list (list X) // (f x).1 ∈ l}

-- inductive list.rel_lift {A B : Type} (R : A → B → Prop) : list A → list B → Prop
-- | nil : list.rel_lift [] []
-- | cons : ∀ a b l₁ l₂, list.rel_lift l₁ l₂ → R a b → list.rel_lift (a :: l₁) (b::l₂)

-- def thing' (E : edge) : Type :=
-- Π (f : Π x : E.type, { l : list E.op_type // ∃ (y : E.op_type) (h : E.R y x), y ∈ l }) (x : E.op_type),
--   {l : list (list E.type) // ∃ (y : E.type) (h : E.R x y)
--     (l' : list E.type) (h : list.rel_lift E.R (f y).1 l'), l' ∈ l  }

-- def thing'_map {X Y : edge} (f : hom X Y) (t : thing' X) : thing' Y :=
-- λ g y, begin
--   dsimp only [thing', thing] at t,
--   let g' : Π (x : X.type), { l : list X.op_type // ∃ (y : X.op_type) (h : X.R y x ), y ∈ l },
--     from λ x, ⟨(g (f.hom x)).1.map f.op_hom, begin
--       rcases (g (f.hom x)).2 with ⟨y, hy₁, hy₂⟩,
--       existsi f.op_hom y,
--       rw [f.adj],
--       use hy₁,
--       rw [list.mem_map],
--       use y,
--       simpa using hy₂,
--     end⟩,
--   let l := t g' (f.op_hom y),
--   fsplit,
--   exact l.1.map (list.map f.hom),
--   rcases l.2 with ⟨x, hxy, l', hl', hll⟩,
--   use f.hom x,
--   rw ← f.adj,
--   use hxy,
--   use l'.map f.hom,
--   split,
--   intros,
--   clear_except hl' ,
--   dsimp [g'] at hl',
--   simp,
--   generalize hk : (↑(g (f.hom x)) : list _) = k,
--   rw hk at hl',
--   clear_except hl',
--   generalize hm : list.map f.op_hom k = m,
--   rw hm at hl',
--   induction hl' generalizing k,
--   { simp * at *, constructor },
--   { cases k,
--     simp * at *,
--     simp at hm,
--     cases hm with hm1 hm2,
--     subst hm1,
--     constructor,
--     apply hl'_ih,
--     assumption,
--     rwa ← f.adj },
--   simp,
--   use l',
--   simp * at *
-- end

-- @[simp] lemma list.map_id {A : Type} : list.map (_root_.id : A → A) = _root_.id :=
-- by funext; simp

-- lemma list.map_comp {A B C : Type} (f : A → B) (g : B → C) :
--   list.map (g ∘ f) = list.map g ∘ list.map f :=
-- by funext; simp

-- lemma thing'_map_id (X : edge) : thing'_map (edge.id X) = _root_.id :=
-- begin
--   cases X, funext, simp [thing'_map],
--   refine subtype.ext _,
--   dsimp [id] at *,
--   rw [list.map_id, list.map_id],
--   simp,
--   congr,
--   simp,
--   simp,
--   simp,
--   simp,
--   simp,
-- end

-- lemma thing'_map_comp  (X Y Z : edge) (f : hom X Y) (g : hom Y Z) :
--   thing'_map (comp g f) =  thing'_map g ∘ thing'_map f :=
-- begin
--   cases X, cases Y, cases Z, cases f, cases g, funext, simp [thing'_map, comp],
--   congr,
--   simp,
--   simp,
--   simp,
--   simp,
--   simp,
-- end

def id2 (E : edge) : Type := E.type

def id2_map {E₁ E₂ : edge} (f : hom E₁ E₂) : id2 E₁ → id2 E₂ := f.hom

lemma id2_map_id (E : edge) : id2_map (id E) = _root_.id := rfl

lemma id2_map_comp (E₁ E₂ E₃ : edge) (f : hom E₁ E₂) (g : hom E₂ E₃) :
  id2_map (comp g f) = id2_map g ∘ id2_map f := rfl

def set2 (E : edge) : Type := E.op_type → Prop

def set2_map {E₁ E₂ : edge} (f : hom E₁ E₂) : set2 E₁ → set2 E₂ := (∘ f.op_hom)

lemma set2_map_id (E : edge) : set2_map (id E) = _root_.id := rfl

lemma set2_map_comp (E₁ E₂ E₃ : edge) (f : hom E₁ E₂) (g : hom E₂ E₃) :
  set2_map (comp g f) = set2_map g ∘ set2_map f := rfl

structure group2 (E : edge) : Type :=
( one : E.type )
( inv : E.op_type → E.type )
( mul : E.op_type → E.op_type → E.type )
( one_mul : ∀ (x : E.op_type) (one' : E.op_type) (h1 : E.R one' one), mul one' x = one  )
(inv_mul : ∀ (x : E.op_type), ∀ (inv_y : E.op_type) (hi : E.R inv_y (inv x)), mul inv_y x = one )
( mul_assoc : ∀ (x y z : E.op_type),
    ∀ (mul_y_z : E.op_type) (mul_x_y : E.op_type) (hyz : E.R mul_y_z (mul y z))
      (hxy : E.R mul_x_y (mul x y)),
    mul x mul_y_z = mul mul_x_y z )

def group2_map {X Y : edge} (f : hom X Y) (G : group2 X) : group2 Y :=
{ one := f.hom G.one,
  inv := λ x, f.hom (G.inv (f.op_hom x)),
  mul := λ x y, f.hom (G.mul (f.op_hom x) (f.op_hom y)),
  one_mul := λ x one' h1,
    by rw G.one_mul (f.op_hom x) (f.op_hom one') ((f.adj _ _).2 h1),
  inv_mul := λ x y hxy,
    by rw (G.inv_mul (f.op_hom x) (f.op_hom y)) ((f.adj _ _).2 hxy),
  mul_assoc := λ x y z mul_y_z mul_x_y h1 h2, begin
    rw G.mul_assoc,
    apply (f.adj _ _).2,
    assumption,
    apply (f.adj _ _).2,
    assumption
  end, }

lemma group2_map_id {X : edge} : group2_map (id X) = _root_.id :=
begin
  funext,
  cases G,
  refl,
end

lemma group2_map_comp {X Y Z : edge} (f : hom X Y) (g : hom Y Z ) :
   group2_map (comp g f) = group2_map g ∘ group2_map f :=
begin
  funext,
  cases G,
  refl,
end

end edge


/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Nicolò Cavalleri.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.subset_properties
import Mathlib.topology.tactic
import Mathlib.PostPort

universes u_1 u_2 l u_3

namespace Mathlib

/-!
# Continuous bundled map

In this file we define the type `continuous_map` of continuous bundled maps.
-/

/-- Bundled continuous maps. -/
structure continuous_map (α : Type u_1) (β : Type u_2) [topological_space α] [topological_space β]
    where
  to_fun : α → β
  continuous_to_fun :
    autoParam (continuous to_fun)
      (Lean.Syntax.ident Lean.SourceInfo.none
        (String.toSubstring "Mathlib.tactic.interactive.continuity'")
        (Lean.Name.mkStr
          (Lean.Name.mkStr
            (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
            "interactive")
          "continuity'")
        [])

namespace continuous_map

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] : has_coe_to_fun (continuous_map α β) :=
  has_coe_to_fun.mk (fun (x : continuous_map α β) => α → β) continuous_map.to_fun

protected theorem continuous {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] (f : continuous_map α β) : continuous ⇑f :=
  continuous_map.continuous_to_fun f

theorem coe_continuous {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : continuous_map α β} : continuous ⇑f :=
  continuous_map.continuous_to_fun f

theorem ext {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : continuous_map α β} {g : continuous_map α β} (H : ∀ (x : α), coe_fn f x = coe_fn g x) :
    f = g :=
  sorry

protected instance inhabited {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] [Inhabited β] : Inhabited (continuous_map α β) :=
  { default := mk fun (_x : α) => Inhabited.default }

theorem coe_inj {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : continuous_map α β} {g : continuous_map α β} (h : ⇑f = ⇑g) : f = g :=
  sorry

/-- The identity as a continuous map. -/
def id {α : Type u_1} [topological_space α] : continuous_map α α := mk id

/-- The composition of continuous maps, as a continuous map. -/
def comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α] [topological_space β]
    [topological_space γ] (f : continuous_map β γ) (g : continuous_map α β) : continuous_map α γ :=
  mk (⇑f ∘ ⇑g)

/-- Constant map as a continuous map -/
def const {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β] (b : β) :
    continuous_map α β :=
  mk fun (x : α) => b

end Mathlib


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Mario Carneiro, Reid Barton, Andrew Yang
-/
import category_theory.limits.kan_extension
import category_theory.adjunction
import topology.category.Top.opens

/-!
# Presheaves on a topological space

We define `presheaf C X` simply as `(opens X)ᵒᵖ ⥤ C`,
and inherit the category structure with natural transformations as morphisms.

We define
* `pushforward_obj {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : X.presheaf C) : Y.presheaf C`
with notation `f _* ℱ`
and for `ℱ : X.presheaf C` provide the natural isomorphisms
* `pushforward.id : (𝟙 X) _* ℱ ≅ ℱ`
* `pushforward.comp : (f ≫ g) _* ℱ ≅ g _* (f _* ℱ)`
along with their `@[simp]` lemmas.

We also define the functors `pushforward` and `pullback` between the categories
`X.presheaf C` and `Y.presheaf C`, and provide their adjunction at
`pushforward_pullback_adjunction`.
-/

universes v u

open category_theory
open topological_space
open opposite

variables (C : Type u) [category.{v} C]

namespace Top

/-- The category of `C`-valued presheaves on a (bundled) topological space `X`. -/
@[derive category, nolint has_inhabited_instance]
def presheaf (X : Top.{v}) := (opens X)ᵒᵖ ⥤ C

variables {C}

namespace presheaf

/-- Pushforward a presheaf on `X` along a continuous map `f : X ⟶ Y`, obtaining a presheaf
on `Y`. -/
def pushforward_obj {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : X.presheaf C) : Y.presheaf C :=
(opens.map f).op ⋙ ℱ

infix ` _* `: 80 := pushforward_obj

@[simp] lemma pushforward_obj_obj {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : X.presheaf C) (U : (opens Y)ᵒᵖ) :
  (f _* ℱ).obj U = ℱ.obj ((opens.map f).op.obj U) := rfl

@[simp] lemma pushforward_obj_map {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : X.presheaf C)
  {U V : (opens Y)ᵒᵖ} (i : U ⟶ V) :
  (f _* ℱ).map i = ℱ.map ((opens.map f).op.map i) := rfl

/--
An equality of continuous maps induces a natural isomorphism between the pushforwards of a presheaf
along those maps.
-/
def pushforward_eq {X Y : Top.{v}} {f g : X ⟶ Y} (h : f = g) (ℱ : X.presheaf C) :
  f _* ℱ ≅ g _* ℱ :=
iso_whisker_right (nat_iso.op (opens.map_iso f g h).symm) ℱ

lemma pushforward_eq' {X Y : Top.{v}} {f g : X ⟶ Y} (h : f = g) (ℱ : X.presheaf C) :
  f _* ℱ = g _* ℱ :=
by rw h

@[simp] lemma pushforward_eq_hom_app
  {X Y : Top.{v}} {f g : X ⟶ Y} (h : f = g) (ℱ : X.presheaf C) (U) :
  (pushforward_eq h ℱ).hom.app U =
    ℱ.map (begin dsimp [functor.op], apply quiver.hom.op, apply eq_to_hom, rw h, end) :=
by simp [pushforward_eq]

@[simp]
lemma pushforward_eq_rfl {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : X.presheaf C) (U) :
  (pushforward_eq (rfl : f = f) ℱ).hom.app (op U) = 𝟙 _ :=
begin
  dsimp [pushforward_eq],
  simp,
end

lemma pushforward_eq_eq {X Y : Top.{v}} {f g : X ⟶ Y} (h₁ h₂ : f = g) (ℱ : X.presheaf C) :
  ℱ.pushforward_eq h₁ = ℱ.pushforward_eq h₂ :=
rfl

namespace pushforward
variables {X : Top.{v}} (ℱ : X.presheaf C)

/-- The natural isomorphism between the pushforward of a presheaf along the identity continuous map
and the original presheaf. -/
def id : (𝟙 X) _* ℱ ≅ ℱ :=
(iso_whisker_right (nat_iso.op (opens.map_id X).symm) ℱ) ≪≫ functor.left_unitor _

lemma id_eq : (𝟙 X) _* ℱ = ℱ :=
by { unfold pushforward_obj, rw opens.map_id_eq, erw functor.id_comp }

@[simp] lemma id_hom_app' (U) (p) :
  (id ℱ).hom.app (op ⟨U, p⟩) = ℱ.map (𝟙 (op ⟨U, p⟩)) :=
by { dsimp [id], simp, }

local attribute [tidy] tactic.op_induction'

@[simp, priority 990] lemma id_hom_app (U) :
  (id ℱ).hom.app U = ℱ.map (eq_to_hom (opens.op_map_id_obj U)) := by tidy

@[simp] lemma id_inv_app' (U) (p) : (id ℱ).inv.app (op ⟨U, p⟩) = ℱ.map (𝟙 (op ⟨U, p⟩)) :=
by { dsimp [id], simp, }

/-- The natural isomorphism between
the pushforward of a presheaf along the composition of two continuous maps and
the corresponding pushforward of a pushforward. -/
def comp {Y Z : Top.{v}} (f : X ⟶ Y) (g : Y ⟶ Z) : (f ≫ g) _* ℱ ≅ g _* (f _* ℱ) :=
iso_whisker_right (nat_iso.op (opens.map_comp f g).symm) ℱ

@[simp] lemma comp_hom_app {Y Z : Top.{v}} (f : X ⟶ Y) (g : Y ⟶ Z) (U) :
  (comp ℱ f g).hom.app U = 𝟙 _ :=
by { dsimp [comp], tidy, }

@[simp] lemma comp_inv_app {Y Z : Top.{v}} (f : X ⟶ Y) (g : Y ⟶ Z) (U) :
  (comp ℱ f g).inv.app U = 𝟙 _ :=
by { dsimp [comp], tidy, }

end pushforward

/--
A morphism of presheaves gives rise to a morphisms of the pushforwards of those presheaves.
-/
@[simps]
def pushforward_map {X Y : Top.{v}} (f : X ⟶ Y) {ℱ 𝒢 : X.presheaf C} (α : ℱ ⟶ 𝒢) :
  f _* ℱ ⟶ f _* 𝒢 :=
{ app := λ U, α.app _,
  naturality' := λ U V i, by { erw α.naturality, refl, } }

open category_theory.limits
section pullback
variable [has_colimits C]
noncomputable theory

/--
Pullback a presheaf on `Y` along a continuous map `f : X ⟶ Y`, obtaining a presheaf on `X`.

This is defined in terms of left Kan extensions, which is just a fancy way of saying
"take the colimits over the open sets whose preimage contains U".
-/
@[simps]
def pullback_obj {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : Y.presheaf C) : X.presheaf C :=
(Lan (opens.map f).op).obj ℱ

/-- Pulling back along continuous maps is functorial. -/
def pullback_map {X Y : Top.{v}} (f : X ⟶ Y) {ℱ 𝒢 : Y.presheaf C} (α : ℱ ⟶ 𝒢) :
  pullback_obj f ℱ ⟶ pullback_obj f 𝒢 :=
(Lan (opens.map f).op).map α

/-- If `f '' U` is open, then `f⁻¹ℱ U ≅ ℱ (f '' U)`.  -/
@[simps]
def pullback_obj_obj_of_image_open {X Y : Top.{v}} (f : X ⟶ Y) (ℱ : Y.presheaf C) (U : opens X)
  (H : is_open (f '' U)) : (pullback_obj f ℱ).obj (op U) ≅ ℱ.obj (op ⟨_, H⟩) :=
begin
  let x : costructured_arrow (opens.map f).op (op U) :=
  { left := op ⟨f '' U, H⟩,
    hom := ((@hom_of_le _ _ _ ((opens.map f).obj ⟨_, H⟩) (set.image_preimage.le_u_l _)).op :
    op ((opens.map f).obj (⟨⇑f '' ↑U, H⟩)) ⟶ op U) },
  have hx : is_terminal x :=
  { lift := λ s,
    begin
      fapply costructured_arrow.hom_mk,
      change op (unop _) ⟶ op (⟨_, H⟩ : opens _),
      refine (hom_of_le _).op,
      exact (set.image_subset f s.X.hom.unop.le).trans (set.image_preimage.l_u_le ↑(unop s.X.left)),
      simp
    end },
  exact is_colimit.cocone_point_unique_up_to_iso
    (colimit.is_colimit _)
    (colimit_of_diagram_terminal hx _),
end

namespace pullback
variables {X Y : Top.{v}} (ℱ : Y.presheaf C)

/-- The pullback along the identity is isomorphic to the original presheaf. -/
def id : pullback_obj (𝟙 _) ℱ ≅ ℱ :=
nat_iso.of_components
  (λ U, pullback_obj_obj_of_image_open (𝟙 _) ℱ (unop U) (by simpa using U.unop.2) ≪≫
    ℱ.map_iso (eq_to_iso (by simp)))
  (λ U V i,
  begin
      ext, simp [-eq_to_hom_map,-eq_to_iso_map],
      erw colimit.pre_desc_assoc,
      erw colimit.ι_desc_assoc,
      erw colimit.ι_desc_assoc,
      dsimp, simp only [←ℱ.map_comp], congr
  end)

lemma id_inv_app (U : opens Y) :
  (id ℱ).inv.app (op U) = colimit.ι (Lan.diagram (opens.map (𝟙 Y)).op ℱ (op U))
    (@costructured_arrow.mk _ _ _ _ _ (op U) _ (eq_to_hom (by simp))) :=
begin
  dsimp[id], simp[-eq_to_hom_map,-eq_to_iso_map],dsimp[colimit_of_diagram_terminal],
  delta Lan.diagram,
  refine eq.trans _ (category.id_comp _),
  rw ← ℱ.map_id,
  congr,
  any_goals { apply subsingleton.helim },
  all_goals { simp }
end

end pullback
end pullback
variable (C)

/--
The pushforward functor.
-/
def pushforward {X Y : Top.{v}} (f : X ⟶ Y) : X.presheaf C ⥤ Y.presheaf C :=
{ obj := pushforward_obj f,
  map := @pushforward_map _ _ X Y f }

lemma id_pushforward {X : Top.{v}} : pushforward C (𝟙 X) = 𝟭 (X.presheaf C) :=
begin
  apply category_theory.functor.ext,
  { intros, ext U, have h := f.congr,
    erw h (opens.op_map_id_obj U), simpa },
  { intros, apply pushforward.id_eq },
end

section iso

/-- A homeomorphism of spaces gives an equivalence of categories of presheaves. -/
@[simps] def presheaf_equiv_of_iso {X Y : Top} (H : X ≅ Y) :
  X.presheaf C ≌ Y.presheaf C :=
equivalence.congr_left (opens.map_map_iso H).symm.op

variable {C}

/--
If `H : X ≅ Y` is a homeomorphism,
then given an `H _* ℱ ⟶ 𝒢`, we may obtain an `ℱ ⟶ H ⁻¹ _* 𝒢`.
-/
def to_pushforward_of_iso {X Y : Top} (H : X ≅ Y) {ℱ : X.presheaf C} {𝒢 : Y.presheaf C}
  (α : H.hom _* ℱ ⟶ 𝒢) : ℱ ⟶ H.inv _* 𝒢 :=
(presheaf_equiv_of_iso _ H).to_adjunction.hom_equiv ℱ 𝒢 α

@[simp]
lemma to_pushforward_of_iso_app {X Y : Top} (H₁ : X ≅ Y) {ℱ : X.presheaf C} {𝒢 : Y.presheaf C}
  (H₂ : H₁.hom _* ℱ ⟶ 𝒢) (U : (opens X)ᵒᵖ) :
(to_pushforward_of_iso H₁ H₂).app U =
  ℱ.map (eq_to_hom (by simp [opens.map, set.preimage_preimage])) ≫
  H₂.app (op ((opens.map H₁.inv).obj (unop U))) :=
begin
  delta to_pushforward_of_iso,
  simp only [equiv.to_fun_as_coe, nat_trans.comp_app, equivalence.equivalence_mk'_unit,
    eq_to_hom_map, presheaf_equiv_of_iso_unit_iso_hom_app_app, equivalence.to_adjunction,
    equivalence.equivalence_mk'_counit, presheaf_equiv_of_iso_inverse_map_app,
    adjunction.mk_of_unit_counit_hom_equiv_apply],
  congr
end

/--
If `H : X ≅ Y` is a homeomorphism,
then given an `H _* ℱ ⟶ 𝒢`, we may obtain an `ℱ ⟶ H ⁻¹ _* 𝒢`.
-/
def pushforward_to_of_iso {X Y : Top} (H₁ : X ≅ Y) {ℱ : Y.presheaf C} {𝒢 : X.presheaf C}
  (H₂ : ℱ ⟶ H₁.hom _* 𝒢) : H₁.inv _* ℱ ⟶ 𝒢 :=
((presheaf_equiv_of_iso _ H₁.symm).to_adjunction.hom_equiv ℱ 𝒢).symm H₂

@[simp]
lemma pushforward_to_of_iso_app {X Y : Top} (H₁ : X ≅ Y) {ℱ : Y.presheaf C} {𝒢 : X.presheaf C}
  (H₂ : ℱ ⟶ H₁.hom _* 𝒢) (U : (opens X)ᵒᵖ) :
(pushforward_to_of_iso H₁ H₂).app U =
  H₂.app (op ((opens.map H₁.inv).obj (unop U))) ≫
  𝒢.map (eq_to_hom (by simp [opens.map, set.preimage_preimage])) :=
by simpa [pushforward_to_of_iso, equivalence.to_adjunction]

end iso

variables (C) [has_colimits C]

/-- Pullback a presheaf on `Y` along a continuous map `f : X ⟶ Y`, obtaining a presheaf
on `X`. -/
@[simps map_app]
def pullback {X Y : Top.{v}} (f : X ⟶ Y) : Y.presheaf C ⥤ X.presheaf C := Lan (opens.map f).op

@[simp] lemma pullback_obj_eq_pullback_obj {C} [category C] [has_colimits C] {X Y : Top.{v}}
  (f : X ⟶ Y) (ℱ : Y.presheaf C) : (pullback C f).obj ℱ = pullback_obj f ℱ := rfl

/-- The pullback and pushforward along a continuous map are adjoint to each other. -/
@[simps unit_app_app counit_app_app]
def pushforward_pullback_adjunction {X Y : Top.{v}} (f : X ⟶ Y) :
  pullback C f ⊣ pushforward C f := Lan.adjunction _ _

/-- Pulling back along a homeomorphism is the same as pushing forward along its inverse. -/
def pullback_hom_iso_pushforward_inv {X Y : Top.{v}} (H : X ≅ Y) :
  pullback C H.hom ≅ pushforward C H.inv :=
adjunction.left_adjoint_uniq
  (pushforward_pullback_adjunction C H.hom)
  (presheaf_equiv_of_iso C H.symm).to_adjunction

/-- Pulling back along the inverse of a homeomorphism is the same as pushing forward along it. -/
def pullback_inv_iso_pushforward_hom {X Y : Top.{v}} (H : X ≅ Y) :
  pullback C H.inv ≅ pushforward C H.hom :=
adjunction.left_adjoint_uniq
  (pushforward_pullback_adjunction C H.inv)
  (presheaf_equiv_of_iso C H).to_adjunction

end presheaf
end Top