/-
Copyright (c) 2020 Scott Morrison, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.limits.preserves.shapes.products
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Shapes.Products
import Mathbin.CategoryTheory.Limits.Preserves.Basic

/-!
# Preserving products

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

Constructions to relate the notions of preserving products and reflecting products
to concrete fans.

In particular, we show that `pi_comparison G f` is an isomorphism iff `G` preserves
the limit of `f`.
-/

noncomputable section

universe w v₁ v₂ u₁ u₂

open CategoryTheory CategoryTheory.Category CategoryTheory.Limits

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

variable {D : Type u₂} [Category.{v₂} D]

variable (G : C ⥤ D)

namespace CategoryTheory.Limits

variable {J : Type w} (f : J → C)

/- warning: category_theory.limits.is_limit_map_cone_fan_mk_equiv -> CategoryTheory.Limits.isLimitMapConeFanMkEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), Equiv.{max (succ u1) (succ u5) (succ u3), max (succ u1) (succ u5) (succ u3)} (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Functor.comp.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G) (CategoryTheory.Functor.mapCone.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))) (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P (f j) (g j))))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), Equiv.{max (max (succ u5) (succ u1)) (succ u3), max (max (succ u5) (succ u1)) (succ u3)} (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Functor.comp.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G) (CategoryTheory.Functor.mapCone.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 G (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (b : J) => f b)) (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))) (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P (f j) (g j))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_map_cone_fan_mk_equiv CategoryTheory.Limits.isLimitMapConeFanMkEquivₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `fan.mk` with `functor.map_cone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (G.mapCone (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) :=
  by
  refine' (is_limit.postcompose_hom_equiv _ _).symm.trans (is_limit.equiv_iso_limit _)
  refine' discrete.nat_iso fun j => iso.refl (G.obj (f j.as))
  refine'
    cones.ext (iso.refl _) fun j =>
      by
      trace
        "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
      dsimp
      simp
#align category_theory.limits.is_limit_map_cone_fan_mk_equiv CategoryTheory.Limits.isLimitMapConeFanMkEquiv

/- warning: category_theory.limits.is_limit_fan_mk_obj_of_is_limit -> CategoryTheory.Limits.isLimitFanMkObjOfIsLimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), (CategoryTheory.Limits.IsLimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g)) -> (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P (f j) (g j))))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), (CategoryTheory.Limits.IsLimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g)) -> (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P (f j) (g j))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_fan_mk_obj_of_is_limit CategoryTheory.Limits.isLimitFanMkObjOfIsLimitₓ'. -/
/-- The property of preserving products expressed in terms of fans. -/
def isLimitFanMkObjOfIsLimit [PreservesLimit (Discrete.functor f) G] {P : C} (g : ∀ j, P ⟶ f j)
    (t : IsLimit (Fan.mk _ g)) :
    IsLimit (Fan.mk (G.obj P) fun j => G.map (g j) : Fan fun j => G.obj (f j)) :=
  isLimitMapConeFanMkEquiv _ _ _ (PreservesLimit.preserves t)
#align category_theory.limits.is_limit_fan_mk_obj_of_is_limit CategoryTheory.Limits.isLimitFanMkObjOfIsLimit

/- warning: category_theory.limits.is_limit_of_is_limit_fan_mk_obj -> CategoryTheory.Limits.isLimitOfIsLimitFanMkObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.ReflectsLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P (f j) (g j)))) -> (CategoryTheory.Limits.IsLimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.ReflectsLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), (CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P (f j) (g j)))) -> (CategoryTheory.Limits.IsLimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Fan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_of_is_limit_fan_mk_obj CategoryTheory.Limits.isLimitOfIsLimitFanMkObjₓ'. -/
/-- The property of reflecting products expressed in terms of fans. -/
def isLimitOfIsLimitFanMkObj [ReflectsLimit (Discrete.functor f) G] {P : C} (g : ∀ j, P ⟶ f j)
    (t : IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j))) :
    IsLimit (Fan.mk P g) :=
  ReflectsLimit.reflects ((isLimitMapConeFanMkEquiv _ _ _).symm t)
#align category_theory.limits.is_limit_of_is_limit_fan_mk_obj CategoryTheory.Limits.isLimitOfIsLimitFanMkObj

section

variable [HasProduct f]

/- warning: category_theory.limits.is_limit_of_has_product_of_preserves_limit -> CategoryTheory.Limits.isLimitOfHasProductOfPreservesLimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3) (f j) (CategoryTheory.Limits.Pi.π.{u1, u2, u4} J C _inst_1 f _inst_3 j)))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Limits.IsLimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Fan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3) (f j) (CategoryTheory.Limits.Pi.π.{u1, u2, u4} J C _inst_1 f _inst_3 j)))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_of_has_product_of_preserves_limit CategoryTheory.Limits.isLimitOfHasProductOfPreservesLimitₓ'. -/
/--
If `G` preserves products and `C` has them, then the fan constructed of the mapped projection of a
product is a limit.
-/
def isLimitOfHasProductOfPreservesLimit [PreservesLimit (Discrete.functor f) G] :
    IsLimit (Fan.mk _ fun j : J => G.map (Pi.π f j) : Fan fun j => G.obj (f j)) :=
  isLimitFanMkObjOfIsLimit G f _ (productIsProduct _)
#align category_theory.limits.is_limit_of_has_product_of_preserves_limit CategoryTheory.Limits.isLimitOfHasProductOfPreservesLimit

variable [HasProduct fun j : J => G.obj (f j)]

/- warning: category_theory.limits.preserves_product.of_iso_comparison -> CategoryTheory.Limits.PreservesProduct.ofIsoComparison is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [i : CategoryTheory.IsIso.{u3, u5} D _inst_2 (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (b : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)], CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [i : CategoryTheory.IsIso.{u3, u5} D _inst_2 (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (b : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f b)) _inst_4) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)], CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_product.of_iso_comparison CategoryTheory.Limits.PreservesProduct.ofIsoComparisonₓ'. -/
/-- If `pi_comparison G f` is an isomorphism, then `G` preserves the limit of `f`. -/
def PreservesProduct.ofIsoComparison [i : IsIso (piComparison G f)] :
    PreservesLimit (Discrete.functor f) G :=
  by
  apply preserves_limit_of_preserves_limit_cone (product_is_product f)
  apply (is_limit_map_cone_fan_mk_equiv _ _ _).symm _
  apply is_limit.of_point_iso (limit.is_limit (discrete.functor fun j : J => G.obj (f j)))
  apply i
#align category_theory.limits.preserves_product.of_iso_comparison CategoryTheory.Limits.PreservesProduct.ofIsoComparison

variable [PreservesLimit (Discrete.functor f) G]

/- warning: category_theory.limits.preserves_product.iso -> CategoryTheory.Limits.PreservesProduct.iso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Iso.{u3, u5} D _inst_2 (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4)
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Iso.{u3, u5} D _inst_2 (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_product.iso CategoryTheory.Limits.PreservesProduct.isoₓ'. -/
/--
If `G` preserves limits, we have an isomorphism from the image of a product to the product of the
images.
-/
def PreservesProduct.iso : G.obj (∏ f) ≅ ∏ fun j => G.obj (f j) :=
  IsLimit.conePointUniqueUpToIso (isLimitOfHasProductOfPreservesLimit G f) (limit.isLimit _)
#align category_theory.limits.preserves_product.iso CategoryTheory.Limits.PreservesProduct.iso

/- warning: category_theory.limits.preserves_product.iso_hom -> CategoryTheory.Limits.PreservesProduct.iso_hom is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], Eq.{succ u3} (Quiver.Hom.{succ u3, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4)) (CategoryTheory.Iso.hom.{u3, u5} D _inst_2 (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4) (CategoryTheory.Limits.PreservesProduct.iso.{u1, u2, u3, u4, u5} C _inst_1 D _inst_2 G J f _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], Eq.{succ u3} (Quiver.Hom.{succ u3, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4)) (CategoryTheory.Iso.hom.{u3, u5} D _inst_2 (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.piObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4) (CategoryTheory.Limits.PreservesProduct.iso.{u1, u2, u3, u4, u5} C _inst_1 D _inst_2 G J f _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_product.iso_hom CategoryTheory.Limits.PreservesProduct.iso_homₓ'. -/
@[simp]
theorem PreservesProduct.iso_hom : (PreservesProduct.iso G f).Hom = piComparison G f :=
  rfl
#align category_theory.limits.preserves_product.iso_hom CategoryTheory.Limits.PreservesProduct.iso_hom

instance : IsIso (piComparison G f) :=
  by
  rw [← preserves_product.iso_hom]
  infer_instance

end

/- warning: category_theory.limits.is_colimit_map_cocone_cofan_mk_equiv -> CategoryTheory.Limits.isColimitMapCoconeCofanMkEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), Equiv.{max (succ u1) (succ u5) (succ u3), max (succ u1) (succ u5) (succ u3)} (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Functor.comp.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G) (CategoryTheory.Functor.mapCocone.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))) (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j) P (g j))))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), Equiv.{max (max (succ u5) (succ u1)) (succ u3), max (max (succ u5) (succ u1)) (succ u3)} (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Functor.comp.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) G) (CategoryTheory.Functor.mapCocone.{u1, u2, u3, u1, u4, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 D _inst_2 G (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (b : J) => f b)) (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))) (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j) P (g j))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_map_cocone_cofan_mk_equiv CategoryTheory.Limits.isColimitMapCoconeCofanMkEquivₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `cofan.mk` with `functor.map_cocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (G.mapCocone (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) :=
  by
  refine' (is_colimit.precompose_hom_equiv _ _).symm.trans (is_colimit.equiv_iso_colimit _)
  refine' discrete.nat_iso fun j => iso.refl (G.obj (f j.as))
  refine'
    cocones.ext (iso.refl _) fun j =>
      by
      trace
        "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
      dsimp
      simp
#align category_theory.limits.is_colimit_map_cocone_cofan_mk_equiv CategoryTheory.Limits.isColimitMapCoconeCofanMkEquiv

/- warning: category_theory.limits.is_colimit_cofan_mk_obj_of_is_colimit -> CategoryTheory.Limits.isColimitCofanMkObjOfIsColimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), (CategoryTheory.Limits.IsColimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g)) -> (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j) P (g j))))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), (CategoryTheory.Limits.IsColimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g)) -> (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j) P (g j))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_cofan_mk_obj_of_is_colimit CategoryTheory.Limits.isColimitCofanMkObjOfIsColimitₓ'. -/
/-- The property of preserving coproducts expressed in terms of cofans. -/
def isColimitCofanMkObjOfIsColimit [PreservesColimit (Discrete.functor f) G] {P : C}
    (g : ∀ j, f j ⟶ P) (t : IsColimit (Cofan.mk _ g)) :
    IsColimit (Cofan.mk (G.obj P) fun j => G.map (g j) : Cofan fun j => G.obj (f j)) :=
  isColimitMapCoconeCofanMkEquiv _ _ _ (PreservesColimit.preserves t)
#align category_theory.limits.is_colimit_cofan_mk_obj_of_is_colimit CategoryTheory.Limits.isColimitCofanMkObjOfIsColimit

/- warning: category_theory.limits.is_colimit_of_is_colimit_cofan_mk_obj -> CategoryTheory.Limits.isColimitOfIsColimitCofanMkObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.ReflectsColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j) P (g j)))) -> (CategoryTheory.Limits.IsColimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.ReflectsColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G] {P : C} (g : forall (j : J), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), (CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) P) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j) P (g j)))) -> (CategoryTheory.Limits.IsColimit.{u1, u2, u1, u4} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J (fun (j : J) => f j)) (CategoryTheory.Limits.Cofan.mk.{u1, u2, u4} J C _inst_1 (fun (j : J) => f j) P g))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_of_is_colimit_cofan_mk_obj CategoryTheory.Limits.isColimitOfIsColimitCofanMkObjₓ'. -/
/-- The property of reflecting coproducts expressed in terms of cofans. -/
def isColimitOfIsColimitCofanMkObj [ReflectsColimit (Discrete.functor f) G] {P : C}
    (g : ∀ j, f j ⟶ P)
    (t : IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j))) :
    IsColimit (Cofan.mk P g) :=
  ReflectsColimit.reflects ((isColimitMapCoconeCofanMkEquiv _ _ _).symm t)
#align category_theory.limits.is_colimit_of_is_colimit_cofan_mk_obj CategoryTheory.Limits.isColimitOfIsColimitCofanMkObj

section

variable [HasCoproduct f]

/- warning: category_theory.limits.is_colimit_of_has_coproduct_of_preserves_colimit -> CategoryTheory.Limits.isColimitOfHasCoproductOfPreservesColimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (fun (j : J) => CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3) (CategoryTheory.Limits.Sigma.ι.{u1, u2, u4} J C _inst_1 f _inst_3 j)))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Limits.IsColimit.{u1, u3, u1, u5} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) D _inst_2 (CategoryTheory.Discrete.functor.{u3, u1, u5} D _inst_2 J (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))) (CategoryTheory.Limits.Cofan.mk.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (fun (j : J) => Prefunctor.map.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3) (CategoryTheory.Limits.Sigma.ι.{u1, u2, u4} J C _inst_1 f _inst_3 j)))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_of_has_coproduct_of_preserves_colimit CategoryTheory.Limits.isColimitOfHasCoproductOfPreservesColimitₓ'. -/
/-- If `G` preserves coproducts and `C` has them,
then the cofan constructed of the mapped inclusion of a coproduct is a colimit.
-/
def isColimitOfHasCoproductOfPreservesColimit [PreservesColimit (Discrete.functor f) G] :
    IsColimit (Cofan.mk _ fun j : J => G.map (Sigma.ι f j) : Cofan fun j => G.obj (f j)) :=
  isColimitCofanMkObjOfIsColimit G f _ (coproductIsCoproduct _)
#align category_theory.limits.is_colimit_of_has_coproduct_of_preserves_colimit CategoryTheory.Limits.isColimitOfHasCoproductOfPreservesColimit

variable [HasCoproduct fun j : J => G.obj (f j)]

/- warning: category_theory.limits.preserves_coproduct.of_iso_comparison -> CategoryTheory.Limits.PreservesCoproduct.ofIsoComparison is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [i : CategoryTheory.IsIso.{u3, u5} D _inst_2 (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (b : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)], CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [i : CategoryTheory.IsIso.{u3, u5} D _inst_2 (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (b : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f b)) _inst_4) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)], CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_coproduct.of_iso_comparison CategoryTheory.Limits.PreservesCoproduct.ofIsoComparisonₓ'. -/
/-- If `sigma_comparison G f` is an isomorphism, then `G` preserves the colimit of `f`. -/
def PreservesCoproduct.ofIsoComparison [i : IsIso (sigmaComparison G f)] :
    PreservesColimit (Discrete.functor f) G :=
  by
  apply preserves_colimit_of_preserves_colimit_cocone (coproduct_is_coproduct f)
  apply (is_colimit_map_cocone_cofan_mk_equiv _ _ _).symm _
  apply is_colimit.of_point_iso (colimit.is_colimit (discrete.functor fun j : J => G.obj (f j)))
  apply i
#align category_theory.limits.preserves_coproduct.of_iso_comparison CategoryTheory.Limits.PreservesCoproduct.ofIsoComparison

variable [PreservesColimit (Discrete.functor f) G]

/- warning: category_theory.limits.preserves_coproduct.iso -> CategoryTheory.Limits.PreservesCoproduct.iso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Iso.{u3, u5} D _inst_2 (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4)
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], CategoryTheory.Iso.{u3, u5} D _inst_2 (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_coproduct.iso CategoryTheory.Limits.PreservesCoproduct.isoₓ'. -/
/-- If `G` preserves colimits,
we have an isomorphism from the image of a coproduct to the coproduct of the images.
-/
def PreservesCoproduct.iso : G.obj (∐ f) ≅ ∐ fun j => G.obj (f j) :=
  IsColimit.coconePointUniqueUpToIso (isColimitOfHasCoproductOfPreservesColimit G f)
    (colimit.isColimit _)
#align category_theory.limits.preserves_coproduct.iso CategoryTheory.Limits.PreservesCoproduct.iso

/- warning: category_theory.limits.preserves_coproduct.inv_hom -> CategoryTheory.Limits.PreservesCoproduct.inv_hom is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j))] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], Eq.{succ u3} (Quiver.Hom.{succ u3, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3))) (CategoryTheory.Iso.inv.{u3, u5} D _inst_2 (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f j)) _inst_4) (CategoryTheory.Limits.PreservesCoproduct.iso.{u1, u2, u3, u4, u5} C _inst_1 D _inst_2 G J f _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u2, u4} C] {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u3, u5} D] (G : CategoryTheory.Functor.{u2, u3, u4, u5} C _inst_1 D _inst_2) {J : Type.{u1}} (f : J -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} J C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j))] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{u1, u1, u2, u3, u4, u5} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) (CategoryTheory.Discrete.functor.{u2, u1, u4} C _inst_1 J f) G], Eq.{succ u3} (Quiver.Hom.{succ u3, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4) (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3))) (CategoryTheory.Iso.inv.{u3, u5} D _inst_2 (Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} J C _inst_1 f _inst_3)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} J D _inst_2 (fun (j : J) => Prefunctor.obj.{succ u2, succ u3, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u3, u4, u5} C _inst_1 D _inst_2 G) (f j)) _inst_4) (CategoryTheory.Limits.PreservesCoproduct.iso.{u1, u2, u3, u4, u5} C _inst_1 D _inst_2 G J f _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} J C _inst_1 D _inst_2 G f _inst_3 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_coproduct.inv_hom CategoryTheory.Limits.PreservesCoproduct.inv_homₓ'. -/
@[simp]
theorem PreservesCoproduct.inv_hom : (PreservesCoproduct.iso G f).inv = sigmaComparison G f :=
  rfl
#align category_theory.limits.preserves_coproduct.inv_hom CategoryTheory.Limits.PreservesCoproduct.inv_hom

instance : IsIso (sigmaComparison G f) :=
  by
  rw [← preserves_coproduct.inv_hom]
  infer_instance

end

end CategoryTheory.Limits


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import data.equiv.encodable.basic
import data.finset.basic
import data.set.pairwise

/-!
# Lattice operations on encodable types

Lemmas about lattice and set operations on encodable types

## Implementation Notes

This is a separate file, to avoid unnecessary imports in basic files.

Previously some of these results were in the `measure_theory` folder.
-/

open set

namespace encodable

variables {α : Type*} {β : Type*} [encodable β]

lemma supr_decode₂ [complete_lattice α] (f : β → α) :
  (⨆ (i : ℕ) (b ∈ decode₂ β i), f b) = (⨆ b, f b) :=
by { rw [supr_comm], simp [mem_decode₂] }

lemma Union_decode₂ (f : β → set α) : (⋃ (i : ℕ) (b ∈ decode₂ β i), f b) = (⋃ b, f b) :=
supr_decode₂ f

@[elab_as_eliminator] lemma Union_decode₂_cases
  {f : β → set α} {C : set α → Prop}
  (H0 : C ∅) (H1 : ∀ b, C (f b)) {n} :
  C (⋃ b ∈ decode₂ β n, f b) :=
match decode₂ β n with
| none := by { simp, apply H0 }
| (some b) := by { convert H1 b, simp [ext_iff] }
end

theorem Union_decode₂_disjoint_on {f : β → set α} (hd : pairwise (disjoint on f)) :
  pairwise (disjoint on λ i, ⋃ b ∈ decode₂ β i, f b) :=
begin
  rintro i j ij x ⟨h₁, h₂⟩,
  revert h₁ h₂,
  simp, intros b₁ e₁ h₁ b₂ e₂ h₂,
  refine hd _ _ _ ⟨h₁, h₂⟩,
  cases encodable.mem_decode₂.1 e₁,
  cases encodable.mem_decode₂.1 e₂,
  exact mt (congr_arg _) ij
end

end encodable

namespace finset

lemma nonempty_encodable {α} (t : finset α) : nonempty $ encodable {i // i ∈ t} :=
begin
  classical, induction t using finset.induction with x t hx ih,
  { refine ⟨⟨λ _, 0, λ _, none, λ ⟨x,y⟩, y.rec _⟩⟩ },
  { cases ih with ih, exactI ⟨encodable.of_equiv _ (finset.subtype_insert_equiv_option hx)⟩ }
end

end finset


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

The integers, with addition, multiplication, and subtraction.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.data.nat.lemmas
import Mathlib.Lean3Lib.init.data.nat.gcd

universes l

namespace Mathlib

/- the type, coercions, and notation -/

not found

notation:1024 "ℤ" => Mathlib.int

protected instance int.has_coe : has_coe ℕ ℤ := has_coe.mk Int.ofNat

protected def int.repr : ℤ → string := sorry

protected instance int.has_repr : has_repr ℤ := has_repr.mk int.repr

protected instance int.has_to_string : has_to_string ℤ := has_to_string.mk int.repr

namespace int

protected theorem coe_nat_eq (n : ℕ) : ↑n = Int.ofNat n := rfl

protected def zero : ℤ := Int.ofNat 0

protected def one : ℤ := Int.ofNat 1

protected instance has_zero : HasZero ℤ := { zero := int.zero }

protected instance has_one : HasOne ℤ := { one := int.one }

theorem of_nat_zero : Int.ofNat 0 = 0 := rfl

theorem of_nat_one : Int.ofNat 1 = 1 := rfl

/- definitions of basic functions -/

def neg_of_nat : ℕ → ℤ := sorry

def sub_nat_nat (m : ℕ) (n : ℕ) : ℤ := sorry

theorem sub_nat_nat_of_sub_eq_zero {m : ℕ} {n : ℕ} (h : n - m = 0) :
    sub_nat_nat m n = Int.ofNat (m - n) :=
  sorry

theorem sub_nat_nat_of_sub_eq_succ {m : ℕ} {n : ℕ} {k : ℕ} (h : n - m = Nat.succ k) :
    sub_nat_nat m n = Int.negSucc k :=
  sorry

protected def neg : ℤ → ℤ := sorry

protected def add : ℤ → ℤ → ℤ := sorry

protected def mul : ℤ → ℤ → ℤ := sorry

protected instance has_neg : Neg ℤ := { neg := int.neg }

protected instance has_add : Add ℤ := { add := int.add }

protected instance has_mul : Mul ℤ := { mul := int.mul }

-- defeq to algebra.sub which gives subtraction for arbitrary `add_group`s

protected def sub : ℤ → ℤ → ℤ := fun (m n : ℤ) => m + -n

protected instance has_sub : Sub ℤ := { sub := int.sub }

protected theorem neg_zero : -0 = 0 := rfl

theorem of_nat_add (n : ℕ) (m : ℕ) : Int.ofNat (n + m) = Int.ofNat n + Int.ofNat m := rfl

theorem of_nat_mul (n : ℕ) (m : ℕ) : Int.ofNat (n * m) = Int.ofNat n * Int.ofNat m := rfl

theorem of_nat_succ (n : ℕ) : Int.ofNat (Nat.succ n) = Int.ofNat n + 1 := rfl

theorem neg_of_nat_zero : -Int.ofNat 0 = 0 := rfl

theorem neg_of_nat_of_succ (n : ℕ) : -Int.ofNat (Nat.succ n) = Int.negSucc n := rfl

theorem neg_neg_of_nat_succ (n : ℕ) : -Int.negSucc n = Int.ofNat (Nat.succ n) := rfl

theorem of_nat_eq_coe (n : ℕ) : Int.ofNat n = ↑n := rfl

theorem neg_succ_of_nat_coe (n : ℕ) : Int.negSucc n = -↑(n + 1) := rfl

protected theorem coe_nat_add (m : ℕ) (n : ℕ) : ↑(m + n) = ↑m + ↑n := rfl

protected theorem coe_nat_mul (m : ℕ) (n : ℕ) : ↑(m * n) = ↑m * ↑n := rfl

protected theorem coe_nat_zero : ↑0 = 0 := rfl

protected theorem coe_nat_one : ↑1 = 1 := rfl

protected theorem coe_nat_succ (n : ℕ) : ↑(Nat.succ n) = ↑n + 1 := rfl

protected theorem coe_nat_add_out (m : ℕ) (n : ℕ) : ↑m + ↑n = ↑m + ↑n := rfl

protected theorem coe_nat_mul_out (m : ℕ) (n : ℕ) : ↑m * ↑n = ↑(m * n) := rfl

protected theorem coe_nat_add_one_out (n : ℕ) : ↑n + 1 = ↑(Nat.succ n) := rfl

/- these are only for internal use -/

theorem of_nat_add_of_nat (m : ℕ) (n : ℕ) : Int.ofNat m + Int.ofNat n = Int.ofNat (m + n) := rfl

theorem of_nat_add_neg_succ_of_nat (m : ℕ) (n : ℕ) :
    Int.ofNat m + Int.negSucc n = sub_nat_nat m (Nat.succ n) :=
  rfl

theorem neg_succ_of_nat_add_of_nat (m : ℕ) (n : ℕ) :
    Int.negSucc m + Int.ofNat n = sub_nat_nat n (Nat.succ m) :=
  rfl

theorem neg_succ_of_nat_add_neg_succ_of_nat (m : ℕ) (n : ℕ) :
    Int.negSucc m + Int.negSucc n = Int.negSucc (Nat.succ (m + n)) :=
  rfl

theorem of_nat_mul_of_nat (m : ℕ) (n : ℕ) : Int.ofNat m * Int.ofNat n = Int.ofNat (m * n) := rfl

theorem of_nat_mul_neg_succ_of_nat (m : ℕ) (n : ℕ) :
    Int.ofNat m * Int.negSucc n = neg_of_nat (m * Nat.succ n) :=
  rfl

theorem neg_succ_of_nat_of_nat (m : ℕ) (n : ℕ) :
    Int.negSucc m * Int.ofNat n = neg_of_nat (Nat.succ m * n) :=
  rfl

theorem mul_neg_succ_of_nat_neg_succ_of_nat (m : ℕ) (n : ℕ) :
    Int.negSucc m * Int.negSucc n = Int.ofNat (Nat.succ m * Nat.succ n) :=
  rfl

/- some basic functions and properties -/

protected theorem coe_nat_inj {m : ℕ} {n : ℕ} (h : ↑m = ↑n) : m = n := of_nat.inj h

theorem of_nat_eq_of_nat_iff (m : ℕ) (n : ℕ) : Int.ofNat m = Int.ofNat n ↔ m = n :=
  { mp := of_nat.inj, mpr := congr_arg fun (m : ℕ) => Int.ofNat m }

protected theorem coe_nat_eq_coe_nat_iff (m : ℕ) (n : ℕ) : ↑m = ↑n ↔ m = n :=
  of_nat_eq_of_nat_iff m n

theorem neg_succ_of_nat_inj_iff {m : ℕ} {n : ℕ} : Int.negSucc m = Int.negSucc n ↔ m = n := sorry

theorem neg_succ_of_nat_eq (n : ℕ) : Int.negSucc n = -(↑n + 1) := rfl

/- neg -/

protected theorem neg_neg (a : ℤ) : --a = a := sorry

protected theorem neg_inj {a : ℤ} {b : ℤ} (h : -a = -b) : a = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = b)) (Eq.symm (int.neg_neg a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ( --a = b)) (Eq.symm (int.neg_neg b))))
      (eq.mpr (id (Eq._oldrec (Eq.refl ( --a = --b)) h)) (Eq.refl ( --b))))

protected theorem sub_eq_add_neg {a : ℤ} {b : ℤ} : a - b = a + -b := rfl

/- basic properties of sub_nat_nat -/

theorem sub_nat_nat_elim (m : ℕ) (n : ℕ) (P : ℕ → ℕ → ℤ → Prop)
    (hp : ∀ (i n : ℕ), P (n + i) n (Int.ofNat i))
    (hn : ∀ (i m : ℕ), P m (m + i + 1) (Int.negSucc i)) : P m n (sub_nat_nat m n) :=
  sorry

theorem sub_nat_nat_add_left {m : ℕ} {n : ℕ} : sub_nat_nat (m + n) m = Int.ofNat n := sorry

theorem sub_nat_nat_add_right {m : ℕ} {n : ℕ} : sub_nat_nat m (m + n + 1) = Int.negSucc n := sorry

theorem sub_nat_nat_add_add (m : ℕ) (n : ℕ) (k : ℕ) :
    sub_nat_nat (m + k) (n + k) = sub_nat_nat m n :=
  sorry

theorem sub_nat_nat_of_ge {m : ℕ} {n : ℕ} (h : m ≥ n) : sub_nat_nat m n = Int.ofNat (m - n) :=
  sub_nat_nat_of_sub_eq_zero (nat.sub_eq_zero_of_le h)

theorem sub_nat_nat_of_lt {m : ℕ} {n : ℕ} (h : m < n) :
    sub_nat_nat m n = Int.negSucc (Nat.pred (n - m)) :=
  sorry

/- nat_abs -/

@[simp] def nat_abs : ℤ → ℕ := sorry

theorem nat_abs_of_nat (n : ℕ) : nat_abs ↑n = n := rfl

theorem eq_zero_of_nat_abs_eq_zero {a : ℤ} : nat_abs a = 0 → a = 0 := sorry

theorem nat_abs_pos_of_ne_zero {a : ℤ} (h : a ≠ 0) : nat_abs a > 0 :=
  or.resolve_left (nat.eq_zero_or_pos (nat_abs a)) (mt eq_zero_of_nat_abs_eq_zero h)

theorem nat_abs_zero : nat_abs 0 = 0 := rfl

theorem nat_abs_one : nat_abs 1 = 1 := rfl

theorem nat_abs_mul_self {a : ℤ} : ↑(nat_abs a * nat_abs a) = a * a := sorry

@[simp] theorem nat_abs_neg (a : ℤ) : nat_abs (-a) = nat_abs a := sorry

theorem nat_abs_eq (a : ℤ) : a = ↑(nat_abs a) ∨ a = -↑(nat_abs a) := sorry

theorem eq_coe_or_neg (a : ℤ) : ∃ (n : ℕ), a = ↑n ∨ a = -↑n :=
  Exists.intro (nat_abs a) (nat_abs_eq a)

/- sign -/

def sign : ℤ → ℤ := sorry

@[simp] theorem sign_zero : sign 0 = 0 := rfl

@[simp] theorem sign_one : sign 1 = 1 := rfl

@[simp] theorem sign_neg_one : sign (-1) = -1 := rfl

/- Quotient and remainder -/

-- There are three main conventions for integer division,

-- referred here as the E, F, T rounding conventions.

-- All three pairs satisfy the identity x % y + (x / y) * y = x

-- unconditionally.

-- E-rounding: This pair satisfies 0 ≤ mod x y < nat_abs y for y ≠ 0

protected def div : ℤ → ℤ → ℤ := sorry

protected def mod : ℤ → ℤ → ℤ := sorry

-- F-rounding: This pair satisfies fdiv x y = floor (x / y)

def fdiv : ℤ → ℤ → ℤ := sorry

def fmod : ℤ → ℤ → ℤ := sorry

-- T-rounding: This pair satisfies quot x y = round_to_zero (x / y)

def quot : ℤ → ℤ → ℤ := sorry

def rem : ℤ → ℤ → ℤ := sorry

protected instance has_div : Div ℤ := { div := int.div }

protected instance has_mod : Mod ℤ := { mod := int.mod }

/- gcd -/

def gcd (m : ℤ) (n : ℤ) : ℕ := nat.gcd (nat_abs m) (nat_abs n)

/-
   int is a ring
-/

/- addition -/

protected theorem add_comm (a : ℤ) (b : ℤ) : a + b = b + a := sorry

protected theorem add_zero (a : ℤ) : a + 0 = a :=
  int.cases_on a (fun (a : ℕ) => idRhs (Int.ofNat a + 0 = Int.ofNat a + 0) rfl)
    fun (a : ℕ) => idRhs (Int.negSucc a + 0 = Int.negSucc a + 0) rfl

protected theorem zero_add (a : ℤ) : 0 + a = a := int.add_comm a 0 ▸ int.add_zero a

theorem sub_nat_nat_sub {m : ℕ} {n : ℕ} (h : m ≥ n) (k : ℕ) :
    sub_nat_nat (m - n) k = sub_nat_nat m (k + n) :=
  sorry

theorem sub_nat_nat_add (m : ℕ) (n : ℕ) (k : ℕ) :
    sub_nat_nat (m + n) k = Int.ofNat m + sub_nat_nat n k :=
  sorry

theorem sub_nat_nat_add_neg_succ_of_nat (m : ℕ) (n : ℕ) (k : ℕ) :
    sub_nat_nat m n + Int.negSucc k = sub_nat_nat m (n + Nat.succ k) :=
  sorry

theorem add_assoc_aux1 (m : ℕ) (n : ℕ) (c : ℤ) :
    Int.ofNat m + Int.ofNat n + c = Int.ofNat m + (Int.ofNat n + c) :=
  sorry

theorem add_assoc_aux2 (m : ℕ) (n : ℕ) (k : ℕ) :
    Int.negSucc m + Int.negSucc n + Int.ofNat k = Int.negSucc m + (Int.negSucc n + Int.ofNat k) :=
  sorry

protected theorem add_assoc (a : ℤ) (b : ℤ) (c : ℤ) : a + b + c = a + (b + c) := sorry

/- negation -/

theorem sub_nat_self (n : ℕ) : sub_nat_nat n n = 0 := sorry

protected theorem add_left_neg (a : ℤ) : -a + a = 0 := sorry

protected theorem add_right_neg (a : ℤ) : a + -a = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + -a = 0)) (int.add_comm a (-a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-a + a = 0)) (int.add_left_neg a))) (Eq.refl 0))

/- multiplication -/

protected theorem mul_comm (a : ℤ) (b : ℤ) : a * b = b * a := sorry

theorem of_nat_mul_neg_of_nat (m : ℕ) (n : ℕ) : Int.ofNat m * neg_of_nat n = neg_of_nat (m * n) :=
  sorry

theorem neg_of_nat_mul_of_nat (m : ℕ) (n : ℕ) : neg_of_nat m * Int.ofNat n = neg_of_nat (m * n) :=
  sorry

theorem neg_succ_of_nat_mul_neg_of_nat (m : ℕ) (n : ℕ) :
    Int.negSucc m * neg_of_nat n = Int.ofNat (Nat.succ m * n) :=
  sorry

theorem neg_of_nat_mul_neg_succ_of_nat (m : ℕ) (n : ℕ) :
    neg_of_nat n * Int.negSucc m = Int.ofNat (n * Nat.succ m) :=
  sorry

protected theorem mul_assoc (a : ℤ) (b : ℤ) (c : ℤ) : a * b * c = a * (b * c) := sorry

protected theorem mul_zero (a : ℤ) : a * 0 = 0 :=
  int.cases_on a (fun (a : ℕ) => idRhs (Int.ofNat a * 0 = Int.ofNat a * 0) rfl)
    fun (a : ℕ) => idRhs (Int.negSucc a * 0 = Int.negSucc a * 0) rfl

protected theorem zero_mul (a : ℤ) : 0 * a = 0 := int.mul_comm a 0 ▸ int.mul_zero a

theorem neg_of_nat_eq_sub_nat_nat_zero (n : ℕ) : neg_of_nat n = sub_nat_nat 0 n :=
  nat.cases_on n (idRhs (neg_of_nat 0 = neg_of_nat 0) rfl)
    fun (n : ℕ) => idRhs (neg_of_nat (Nat.succ n) = neg_of_nat (Nat.succ n)) rfl

theorem of_nat_mul_sub_nat_nat (m : ℕ) (n : ℕ) (k : ℕ) :
    Int.ofNat m * sub_nat_nat n k = sub_nat_nat (m * n) (m * k) :=
  sorry

theorem neg_of_nat_add (m : ℕ) (n : ℕ) : neg_of_nat m + neg_of_nat n = neg_of_nat (m + n) := sorry

theorem neg_succ_of_nat_mul_sub_nat_nat (m : ℕ) (n : ℕ) (k : ℕ) :
    Int.negSucc m * sub_nat_nat n k = sub_nat_nat (Nat.succ m * k) (Nat.succ m * n) :=
  sorry

protected theorem distrib_left (a : ℤ) (b : ℤ) (c : ℤ) : a * (b + c) = a * b + a * c := sorry

protected theorem distrib_right (a : ℤ) (b : ℤ) (c : ℤ) : (a + b) * c = a * c + b * c := sorry

protected theorem zero_ne_one : 0 ≠ 1 :=
  fun (h : 0 = 1) => nat.succ_ne_zero 0 (Eq.symm (of_nat.inj h))

theorem of_nat_sub {n : ℕ} {m : ℕ} (h : m ≤ n) : Int.ofNat (n - m) = Int.ofNat n - Int.ofNat m :=
  sorry

protected theorem add_left_comm (a : ℤ) (b : ℤ) (c : ℤ) : a + (b + c) = b + (a + c) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + (b + c) = b + (a + c))) (Eq.symm (int.add_assoc a b c))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + b + c = b + (a + c))) (int.add_comm a b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b + a + c = b + (a + c))) (int.add_assoc b a c)))
        (Eq.refl (b + (a + c)))))

protected theorem add_left_cancel {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b = a + c) : b = c := sorry

protected theorem neg_add {a : ℤ} {b : ℤ} : -(a + b) = -a + -b := sorry

theorem neg_succ_of_nat_coe' (n : ℕ) : Int.negSucc n = -↑n - 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Int.negSucc n = -↑n - 1)) int.sub_eq_add_neg))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Int.negSucc n = -↑n + -1)) (Eq.symm int.neg_add)))
      (Eq.refl (Int.negSucc n)))

protected theorem coe_nat_sub {n : ℕ} {m : ℕ} : n ≤ m → ↑(m - n) = ↑m - ↑n := of_nat_sub

protected theorem sub_nat_nat_eq_coe {m : ℕ} {n : ℕ} : sub_nat_nat m n = ↑m - ↑n := sorry

def to_nat : ℤ → ℕ := sorry

theorem to_nat_sub (m : ℕ) (n : ℕ) : to_nat (↑m - ↑n) = m - n := sorry

-- Since mod x y is always nonnegative when y ≠ 0, we can make a nat version of it

def nat_mod (m : ℤ) (n : ℤ) : ℕ := to_nat (m % n)

protected theorem one_mul (a : ℤ) : 1 * a = a := sorry

protected theorem mul_one (a : ℤ) : a * 1 = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * 1 = a)) (int.mul_comm a 1)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * a = a)) (int.one_mul a))) (Eq.refl a))

protected theorem neg_eq_neg_one_mul (a : ℤ) : -a = -1 * a := sorry

theorem sign_mul_nat_abs (a : ℤ) : sign a * ↑(nat_abs a) = a := sorry

end Mathlib


/-
Copyright (c) 2022 Thomas Browning. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Thomas Browning
-/
import algebra.group.conj_finite
import group_theory.abelianization
import group_theory.group_action.conj_act
import group_theory.group_action.quotient
import group_theory.index

/-!
# Commuting Probability

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
This file introduces the commuting probability of finite groups.

## Main definitions
* `comm_prob`: The commuting probability of a finite type with a multiplication operation.

## Todo
* Neumann's theorem.
-/

noncomputable theory
open_locale classical
open_locale big_operators

open fintype

variables (M : Type*) [has_mul M]

/-- The commuting probability of a finite type with a multiplication operation -/
def comm_prob : ℚ := nat.card {p : M × M // p.1 * p.2 = p.2 * p.1} / nat.card M ^ 2

lemma comm_prob_def :
  comm_prob M = nat.card {p : M × M // p.1 * p.2 = p.2 * p.1} / nat.card M ^ 2 :=
rfl

variables [finite M]

lemma comm_prob_pos [h : nonempty M] : 0 < comm_prob M :=
h.elim (λ x, div_pos (nat.cast_pos.mpr (finite.card_pos_iff.mpr ⟨⟨(x, x), rfl⟩⟩))
  (pow_pos (nat.cast_pos.mpr finite.card_pos) 2))

lemma comm_prob_le_one : comm_prob M ≤ 1 :=
begin
  refine div_le_one_of_le _ (sq_nonneg (nat.card M)),
  rw [←nat.cast_pow, nat.cast_le, sq, ←nat.card_prod],
  apply finite.card_subtype_le,
end

variables {M}

lemma comm_prob_eq_one_iff [h : nonempty M] : comm_prob M = 1 ↔ commutative ((*) : M → M → M) :=
begin
  haveI := fintype.of_finite M,
  rw [comm_prob, ←set.coe_set_of, nat.card_eq_fintype_card, nat.card_eq_fintype_card],
  rw [div_eq_one_iff_eq, ←nat.cast_pow, nat.cast_inj, sq, ←card_prod,
      set_fintype_card_eq_univ_iff, set.eq_univ_iff_forall],
  { exact ⟨λ h x y, h (x, y), λ h x, h x.1 x.2⟩ },
  { exact pow_ne_zero 2 (nat.cast_ne_zero.mpr card_ne_zero) },
end

variables (G : Type*) [group G] [finite G]

lemma card_comm_eq_card_conj_classes_mul_card :
  nat.card {p : G × G // p.1 * p.2 = p.2 * p.1} = nat.card (conj_classes G) * nat.card G :=
begin
  haveI := fintype.of_finite G,
  simp only [nat.card_eq_fintype_card],
  convert calc card {p : G × G // p.1 * p.2 = p.2 * p.1} = card (Σ g, {h // g * h = h * g}) :
  card_congr (equiv.subtype_prod_equiv_sigma_subtype (λ g h : G, g * h = h * g))
... = ∑ g, card {h // g * h = h * g} : card_sigma _
... = ∑ g, card (mul_action.fixed_by (conj_act G) G g) : sum_equiv conj_act.to_conj_act.to_equiv
  _ _ (λ g, card_congr' $ congr_arg _ $ funext $ λ h, mul_inv_eq_iff_eq_mul.symm.to_eq)
... = card (quotient (mul_action.orbit_rel (conj_act G) G)) * card G :
  mul_action.sum_card_fixed_by_eq_card_orbits_mul_card_group (conj_act G) G
... = card (quotient (is_conj.setoid G)) * card G :
  have this : mul_action.orbit_rel (conj_act G) G = is_conj.setoid G :=
    setoid.ext (λ g h, (setoid.comm' _).trans is_conj_iff.symm),
  by cc
end

lemma comm_prob_def' : comm_prob G = nat.card (conj_classes G) / nat.card G :=
begin
  rw [comm_prob, card_comm_eq_card_conj_classes_mul_card, nat.cast_mul, sq],
  exact mul_div_mul_right _ _ (nat.cast_ne_zero.mpr finite.card_pos.ne'),
end

variables {G} (H : subgroup G)

lemma subgroup.comm_prob_subgroup_le : comm_prob H ≤ comm_prob G * H.index ^ 2 :=
begin
  /- After rewriting with `comm_prob_def`, we reduce to showing that `G` has at least as many
    commuting pairs as `H`. -/
  rw [comm_prob_def, comm_prob_def,  div_le_iff, mul_assoc, ←mul_pow, ←nat.cast_mul,
      mul_comm H.index, H.card_mul_index, div_mul_cancel, nat.cast_le],
  { refine finite.card_le_of_injective (λ p, ⟨⟨p.1.1, p.1.2⟩, subtype.ext_iff.mp p.2⟩) _,
    exact λ p q h, by simpa only [subtype.ext_iff, prod.ext_iff] using h },
  { exact pow_ne_zero 2 (nat.cast_ne_zero.mpr finite.card_pos.ne') },
  { exact pow_pos (nat.cast_pos.mpr finite.card_pos) 2 },
end

lemma subgroup.comm_prob_quotient_le [H.normal] : comm_prob (G ⧸ H) ≤ comm_prob G * nat.card H :=
begin
  /- After rewriting with `comm_prob_def'`, we reduce to showing that `G` has at least as many
    conjugacy classes as `G ⧸ H`. -/
  rw [comm_prob_def', comm_prob_def', div_le_iff, mul_assoc, ←nat.cast_mul, ←subgroup.index,
      H.card_mul_index, div_mul_cancel, nat.cast_le],
  { apply finite.card_le_of_surjective,
    show function.surjective (conj_classes.map (quotient_group.mk' H)),
    exact (conj_classes.map_surjective quotient.surjective_quotient_mk') },
  { exact nat.cast_ne_zero.mpr finite.card_pos.ne' },
  { exact nat.cast_pos.mpr finite.card_pos },
end

variables (G)

lemma inv_card_commutator_le_comm_prob : (↑(nat.card (commutator G)))⁻¹ ≤ comm_prob G :=
(inv_pos_le_iff_one_le_mul (by exact nat.cast_pos.mpr finite.card_pos)).mpr
  (le_trans (ge_of_eq (comm_prob_eq_one_iff.mpr (abelianization.comm_group G).mul_comm))
    (commutator G).comm_prob_quotient_le)