/-
Copyright (c) 2020 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import ring_theory.prime
import ring_theory.polynomial.content
/-!
# Eisenstein's criterion

A proof of a slight generalisation of Eisenstein's criterion for the irreducibility of
a polynomial over an integral domain.
-/

open polynomial ideal.quotient
variables {R : Type*} [comm_ring R]

namespace polynomial
namespace eisenstein_criterion_aux
/- Section for auxiliary lemmas used in the proof of `irreducible_of_eisenstein_criterion`-/

lemma map_eq_C_mul_X_pow_of_forall_coeff_mem {f : polynomial R} {P : ideal R}
  (hfP : ∀ (n : ℕ), ↑n < f.degree → f.coeff n ∈ P) :
  map (mk P) f = C ((mk P) f.leading_coeff) * X ^ f.nat_degree :=
polynomial.ext (λ n, begin
  by_cases hf0 : f = 0, { simp [hf0], },
  rcases lt_trichotomy ↑n (degree f) with h | h | h,
  { erw [coeff_map, eq_zero_iff_mem.2 (hfP n h), coeff_C_mul, coeff_X_pow, if_neg, mul_zero],
    rintro rfl, exact not_lt_of_ge degree_le_nat_degree h },
  { have : nat_degree f = n, from nat_degree_eq_of_degree_eq_some h.symm,
    rw [coeff_C_mul, coeff_X_pow, if_pos this.symm, mul_one, leading_coeff, this, coeff_map] },
  { rw [coeff_eq_zero_of_degree_lt, coeff_eq_zero_of_degree_lt],
    { refine lt_of_le_of_lt (degree_C_mul_X_pow_le _ _) _,
      rwa ← degree_eq_nat_degree hf0 },
    { exact lt_of_le_of_lt (degree_map_le _ _) h } }
end)

lemma le_nat_degree_of_map_eq_mul_X_pow {n : ℕ}
  {P : ideal R} (hP : P.is_prime) {q : polynomial R} {c : polynomial (R ⧸ P)}
  (hq : map (mk P) q = c * X ^ n) (hc0 : c.degree = 0) : n ≤ q.nat_degree :=
with_bot.coe_le_coe.1
  (calc ↑n = degree (q.map (mk P)) :
      by rw [hq, degree_mul, hc0, zero_add, degree_pow, degree_X, nsmul_one, nat.cast_with_bot]
      ... ≤ degree q : degree_map_le _ _
      ... ≤ nat_degree q : degree_le_nat_degree)

lemma eval_zero_mem_ideal_of_eq_mul_X_pow {n : ℕ} {P : ideal R}
  {q : polynomial R} {c : polynomial (R ⧸ P)}
  (hq : map (mk P) q = c * X ^ n) (hn0 : 0 < n) : eval 0 q ∈ P :=
by rw [← coeff_zero_eq_eval_zero, ← eq_zero_iff_mem, ← coeff_map,
    coeff_zero_eq_eval_zero, hq, eval_mul, eval_pow, eval_X, zero_pow hn0, mul_zero]

lemma is_unit_of_nat_degree_eq_zero_of_forall_dvd_is_unit {p q : polynomial R}
  (hu : ∀ (x : R), C x ∣ p * q → is_unit x) (hpm : p.nat_degree = 0) :
  is_unit p :=
begin
  rw [eq_C_of_degree_le_zero (nat_degree_eq_zero_iff_degree_le_zero.1 hpm), is_unit_C],
  refine hu _ _,
  rw [← eq_C_of_degree_le_zero (nat_degree_eq_zero_iff_degree_le_zero.1 hpm)],
  exact dvd_mul_right _ _
end

end eisenstein_criterion_aux

open eisenstein_criterion_aux

variables [is_domain R]

/-- If `f` is a non constant polynomial with coefficients in `R`, and `P` is a prime ideal in `R`,
then if every coefficient in `R` except the leading coefficient is in `P`, and
the trailing coefficient is not in `P^2` and no non units in `R` divide `f`, then `f` is
irreducible. -/
theorem irreducible_of_eisenstein_criterion {f : polynomial R} {P : ideal R} (hP : P.is_prime)
  (hfl : f.leading_coeff ∉ P)
  (hfP : ∀ n : ℕ, ↑n < degree f → f.coeff n ∈ P)
  (hfd0 : 0 < degree f) (h0 : f.coeff 0 ∉ P^2)
  (hu : f.is_primitive) : irreducible f :=
have hf0 : f ≠ 0, from λ _, by simp only [*, not_true, submodule.zero_mem, coeff_zero] at *,
have hf : f.map (mk P) = C (mk P (leading_coeff f)) * X ^ nat_degree f,
  from map_eq_C_mul_X_pow_of_forall_coeff_mem hfP,
have hfd0 : 0 < f.nat_degree, from with_bot.coe_lt_coe.1
  (lt_of_lt_of_le hfd0 degree_le_nat_degree),
⟨mt degree_eq_zero_of_is_unit (λ h, by simp only [*, lt_irrefl] at *),
begin
  rintros p q rfl,
  rw [map_mul] at hf,
  rcases mul_eq_mul_prime_pow (show prime (X : polynomial (R ⧸ P)),
    from monic_X.prime_of_degree_eq_one degree_X) hf with
      ⟨m, n, b, c, hmnd, hbc, hp, hq⟩,
  have hmn : 0 < m → 0 < n → false,
  { assume hm0 hn0,
    refine h0 _,
    rw [coeff_zero_eq_eval_zero, eval_mul, sq],
    exact ideal.mul_mem_mul
      (eval_zero_mem_ideal_of_eq_mul_X_pow hp hm0)
      (eval_zero_mem_ideal_of_eq_mul_X_pow hq hn0) },
  have hpql0 : (mk P) (p * q).leading_coeff ≠ 0,
  { rwa [ne.def, eq_zero_iff_mem] },
  have hp0 : p ≠ 0, from λ h, by simp only [*, zero_mul, eq_self_iff_true, not_true, ne.def] at *,
  have hq0 : q ≠ 0, from λ h, by simp only [*, eq_self_iff_true, not_true, ne.def, mul_zero] at *,
  have hbc0 : degree b = 0 ∧ degree c = 0,
  { apply_fun degree at hbc,
    rwa [degree_C hpql0, degree_mul, eq_comm, nat.with_bot.add_eq_zero_iff] at hbc },
  have hmp : m ≤ nat_degree p,
    from le_nat_degree_of_map_eq_mul_X_pow hP hp hbc0.1,
  have hnq : n ≤ nat_degree q,
    from le_nat_degree_of_map_eq_mul_X_pow hP hq hbc0.2,
  have hpmqn : p.nat_degree = m ∧ q.nat_degree = n,
  { rw [nat_degree_mul hp0 hq0] at hmnd,
    clear_except hmnd hmp hnq,
    contrapose hmnd,
    apply ne_of_lt,
    rw not_and_distrib at hmnd,
    cases hmnd,
    { exact add_lt_add_of_lt_of_le (lt_of_le_of_ne hmp (ne.symm hmnd)) hnq },
    { exact add_lt_add_of_le_of_lt hmp (lt_of_le_of_ne hnq (ne.symm hmnd)) } },
  obtain rfl | rfl : m = 0 ∨ n = 0,
  { rwa [pos_iff_ne_zero, pos_iff_ne_zero, imp_false, not_not,
      ← or_iff_not_imp_left] at hmn },
  { exact or.inl (is_unit_of_nat_degree_eq_zero_of_forall_dvd_is_unit hu hpmqn.1) },
  { exact or.inr (is_unit_of_nat_degree_eq_zero_of_forall_dvd_is_unit
      (by simpa only [mul_comm] using hu) hpmqn.2) }
end⟩

end polynomial


/-
Copyright (c) 2020 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Anatole Dedecker
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.separation
import Mathlib.PostPort

universes u_2 u_1

namespace Mathlib

/-!
# Extending a function from a subset

The main definition of this file is `extend_from A f` where `f : X → Y`
and `A : set X`. This defines a new function `g : X → Y` which maps any
`x₀ : X` to the limit of `f` as `x` tends to `x₀`, if such a limit exists.

This is analoguous to the way `dense_inducing.extend` "extends" a function
`f : X → Z` to a function `g : Y → Z` along a dense inducing `i : X → Y`.

The main theorem we prove about this definition is `continuous_on_extend_from`
which states that, for `extend_from A f` to be continuous on a set `B ⊆ closure A`,
it suffices that `f` converges within `A` at any point of `B`, provided that
`f` is a function to a regular space.

-/

/-- Extend a function from a set `A`. The resulting function `g` is such that
at any `x₀`, if `f` converges to some `y` as `x` tends to `x₀` within `A`,
then `g x₀` is defined to be one of these `y`. Else, `g x₀` could be anything. -/
def extend_from {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] (A : set X) (f : X → Y) : X → Y :=
  fun (x : X) => lim (nhds_within x A) f

/-- If `f` converges to some `y` as `x` tends to `x₀` within `A`,
then `f` tends to `extend_from A f x` as `x` tends to `x₀`. -/
theorem tendsto_extend_from {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] {A : set X} {f : X → Y} {x : X} (h : ∃ (y : Y), filter.tendsto f (nhds_within x A) (nhds y)) : filter.tendsto f (nhds_within x A) (nhds (extend_from A f x)) :=
  tendsto_nhds_lim h

theorem extend_from_eq {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] [t2_space Y] {A : set X} {f : X → Y} {x : X} {y : Y} (hx : x ∈ closure A) (hf : filter.tendsto f (nhds_within x A) (nhds y)) : extend_from A f x = y :=
  tendsto_nhds_unique (tendsto_nhds_lim (Exists.intro y hf)) hf

theorem extend_from_extends {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] [t2_space Y] {f : X → Y} {A : set X} (hf : continuous_on f A) (x : X) (H : x ∈ A) : extend_from A f x = f x :=
  extend_from_eq (subset_closure x_in) (hf x x_in)

/-- If `f` is a function to a regular space `Y` which has a limit within `A` at any
point of a set `B ⊆ closure A`, then `extend_from A f` is continuous on `B`. -/
theorem continuous_on_extend_from {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] [regular_space Y] {f : X → Y} {A : set X} {B : set X} (hB : B ⊆ closure A) (hf : ∀ (x : X), x ∈ B → ∃ (y : Y), filter.tendsto f (nhds_within x A) (nhds y)) : continuous_on (extend_from A f) B := sorry

/-- If a function `f` to a regular space `Y` has a limit within a
dense set `A` for any `x`, then `extend_from A f` is continuous. -/
theorem continuous_extend_from {X : Type u_1} {Y : Type u_2} [topological_space X] [topological_space Y] [regular_space Y] {f : X → Y} {A : set X} (hA : dense A) (hf : ∀ (x : X), ∃ (y : Y), filter.tendsto f (nhds_within x A) (nhds y)) : continuous (extend_from A f) := sorry


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes

Definition of splitting fields, and definition of homomorphism into any field that splits
-/

import ring_theory.adjoin_root ring_theory.unique_factorization_domain

universes u v w

variables {α : Type u} {β : Type v} {γ : Type w}

namespace polynomial

noncomputable theory
local attribute [instance, priority 0] classical.prop_decidable
variables [discrete_field α] [discrete_field β] [discrete_field γ]
open polynomial adjoin_root

section splits

variables (i : α → β) [is_field_hom i]

/-- a polynomial `splits` iff it is zero or all of its irreducible factors have `degree` 1 -/
def splits (f : polynomial α) : Prop :=
f = 0 ∨ ∀ {g : polynomial β}, irreducible g → g ∣ f.map i → degree g = 1

@[simp] lemma splits_zero : splits i (0 : polynomial α) := or.inl rfl

@[simp] lemma splits_C (a : α) : splits i (C a) :=
if ha : a = 0 then ha.symm ▸ (@C_0 α _ _).symm ▸ splits_zero i
else
have hia : i a ≠ 0, from mt ((is_add_group_hom.injective_iff i).1
  (is_field_hom.injective i) _) ha,
or.inr $ λ g hg ⟨p, hp⟩, absurd hg.1 (classical.not_not.2 (is_unit_iff_degree_eq_zero.2 $
  by have := congr_arg degree hp;
    simp [degree_C hia, @eq_comm (with_bot ℕ) 0,
      nat.with_bot.add_eq_zero_iff] at this; clear _fun_match; tautology))

lemma splits_of_degree_eq_one {f : polynomial α} (hf : degree f = 1) : splits i f :=
or.inr $ λ g hg ⟨p, hp⟩,
  by have := congr_arg degree hp;
  simp [nat.with_bot.add_eq_one_iff, hf, @eq_comm (with_bot ℕ) 1,
    mt is_unit_iff_degree_eq_zero.2 hg.1] at this;
  clear _fun_match; tauto

lemma splits_of_degree_le_one {f : polynomial α} (hf : degree f ≤ 1) : splits i f :=
begin
  cases h : degree f with n,
  { rw [degree_eq_bot.1 h]; exact splits_zero i },
  { cases n with n,
    { rw [eq_C_of_degree_le_zero (trans_rel_right (≤) h (le_refl _))];
      exact splits_C _ _ },
    { have hn : n = 0,
      { rw h at hf,
        cases n, { refl }, { exact absurd hf dec_trivial } },
      exact splits_of_degree_eq_one _ (by rw [h, hn]; refl) } }
end

lemma splits_mul {f g : polynomial α} (hf : splits i f) (hg : splits i g) : splits i (f * g) :=
if h : f * g = 0 then by simp [h]
else or.inr $ λ p hp hpf, ((principal_ideal_domain.irreducible_iff_prime.1 hp).2.2 _ _
    (show p ∣ map i f * map i g, by convert hpf; rw map_mul)).elim
  (hf.resolve_left (λ hf, by simpa [hf] using h) hp)
  (hg.resolve_left (λ hg, by simpa [hg] using h) hp)

lemma splits_of_splits_mul {f g : polynomial α} (hfg : f * g ≠ 0) (h : splits i (f * g)) :
  splits i f ∧ splits i g :=
⟨or.inr $ λ g hgi hg, or.resolve_left h hfg hgi (by rw map_mul; exact dvd.trans hg (dvd_mul_right _ _)),
 or.inr $ λ g hgi hg, or.resolve_left h hfg hgi (by rw map_mul; exact dvd.trans hg (dvd_mul_left _ _))⟩

lemma splits_map_iff (j : β → γ) [is_field_hom j] {f : polynomial α} :
  splits j (f.map i) ↔ splits (λ x, j (i x)) f :=
by simp [splits, polynomial.map_map]

lemma exists_root_of_splits {f : polynomial α} (hs : splits i f) (hf0 : degree f ≠ 0) :
  ∃ x, eval₂ i x f = 0 :=
if hf0 : f = 0 then ⟨37, by simp [hf0]⟩
else
  let ⟨g, hg⟩ := is_noetherian_ring.exists_irreducible_factor
    (show ¬ is_unit (f.map i), from mt is_unit_iff_degree_eq_zero.1 (by rwa degree_map))
    (by rw [ne.def, map_eq_zero]; exact hf0) in
  let ⟨x, hx⟩ := exists_root_of_degree_eq_one (hs.resolve_left hf0 hg.1 hg.2) in
  let ⟨i, hi⟩ := hg.2 in
  ⟨x, by rw [← eval_map, hi, eval_mul, show _ = _, from hx, zero_mul]⟩

lemma exists_multiset_of_splits {f : polynomial α} : splits i f →
  ∃ (s : multiset β), f.map i = C (i f.leading_coeff) *
  (s.map (λ a : β, (X : polynomial β) - C a)).prod :=
suffices splits id (f.map i) → ∃ s : multiset β, f.map i =
  (C (f.map i).leading_coeff) * (s.map (λ a : β, (X : polynomial β) - C a)).prod,
by rwa [splits_map_iff, leading_coeff_map i] at this,
is_noetherian_ring.irreducible_induction_on (f.map i)
  (λ _, ⟨{37}, by simp [is_ring_hom.map_zero i]⟩)
  (λ u hu _, ⟨0,
    by conv_lhs { rw eq_C_of_degree_eq_zero (is_unit_iff_degree_eq_zero.1 hu) };
      simp [leading_coeff, nat_degree_eq_of_degree_eq_some (is_unit_iff_degree_eq_zero.1 hu)]⟩)
  (λ f p hf0 hp ih hfs,
    have hpf0 : p * f ≠ 0, from mul_ne_zero (nonzero_of_irreducible hp) hf0,
    let ⟨s, hs⟩ := ih (splits_of_splits_mul _ hpf0 hfs).2 in
    ⟨-(p * norm_unit p).coeff 0 :: s,
      have hp1 : degree p = 1, from hfs.resolve_left hpf0 hp (by simp),
      begin
        rw [multiset.map_cons, multiset.prod_cons, leading_coeff_mul, C_mul, mul_assoc,
          mul_left_comm (C f.leading_coeff), ← hs, ← mul_assoc, domain.mul_right_inj hf0],
        conv_lhs {rw eq_X_add_C_of_degree_eq_one hp1},
        simp only [mul_add, coe_norm_unit (nonzero_of_irreducible hp), mul_comm p, coeff_neg,
          C_neg, sub_eq_add_neg, neg_neg, coeff_C_mul, (mul_assoc _ _ _).symm, C_mul.symm,
          mul_inv_cancel (show p.leading_coeff ≠ 0, from mt leading_coeff_eq_zero.1
            (nonzero_of_irreducible hp)), one_mul],
      end⟩)

section UFD

local attribute [instance, priority 0] principal_ideal_domain.to_unique_factorization_domain
local infix ` ~ᵤ ` : 50 := associated

open unique_factorization_domain associates

lemma splits_of_exists_multiset {f : polynomial α} {s : multiset β}
  (hs : f.map i = C (i f.leading_coeff) * (s.map (λ a : β, (X : polynomial β) - C a)).prod) :
  splits i f :=
if hf0 : f = 0 then or.inl hf0
else
  or.inr $ λ p hp hdp,
    have ht : multiset.rel associated
      (factors (f.map i)) (s.map (λ a : β, (X : polynomial β) - C a)) :=
    unique
      (λ p hp, irreducible_factors (mt (map_eq_zero i).1 hf0) _ hp)
      (λ p, by simp [@eq_comm _ _ p, -sub_eq_add_neg,
          irreducible_of_degree_eq_one (degree_X_sub_C _)] {contextual := tt})
      (associated.symm $ calc _ ~ᵤ f.map i :
        ⟨units.map C (units.mk0 (f.map i).leading_coeff
            (mt leading_coeff_eq_zero.1 (mt (map_eq_zero i).1 hf0))),
          by conv_rhs {rw [hs, ← leading_coeff_map i, mul_comm]}; refl⟩
        ... ~ᵤ _ : associated.symm (unique_factorization_domain.factors_prod (by simpa using hf0))),
  let ⟨q, hq, hpq⟩ := exists_mem_factors_of_dvd (by simpa) hp hdp in
  let ⟨q', hq', hqq'⟩ := multiset.exists_mem_of_rel_of_mem ht hq in
  let ⟨a, ha⟩ := multiset.mem_map.1 hq' in
  by rw [← degree_X_sub_C a, ha.2];
    exact degree_eq_degree_of_associated (hpq.trans hqq')

lemma splits_of_splits_id {f : polynomial α} : splits id f → splits i f :=
unique_factorization_domain.induction_on_prime f (λ _, splits_zero _)
  (λ _ hu _, splits_of_degree_le_one _
    ((is_unit_iff_degree_eq_zero.1 hu).symm ▸ dec_trivial))
  (λ a p ha0 hp ih hfi, splits_mul _
    (splits_of_degree_eq_one _
      ((splits_of_splits_mul _ (mul_ne_zero hp.1 ha0) hfi).1.resolve_left
        hp.1 (irreducible_of_prime hp) (by rw map_id)))
    (ih (splits_of_splits_mul _ (mul_ne_zero hp.1 ha0) hfi).2))

end UFD

lemma splits_iff_exists_multiset {f : polynomial α} : splits i f ↔
  ∃ (s : multiset β), f.map i = C (i f.leading_coeff) *
  (s.map (λ a : β, (X : polynomial β) - C a)).prod :=
⟨exists_multiset_of_splits i, λ ⟨s, hs⟩, splits_of_exists_multiset i hs⟩

lemma splits_comp_of_splits (j : β → γ) [is_field_hom j] {f : polynomial α}
  (h : splits i f) : splits (λ x, j (i x)) f :=
begin
  change i with (λ x, id (i x)) at h,
  rw [← splits_map_iff],
  rw [← splits_map_iff i id] at h,
  exact splits_of_splits_id _ h
end

end splits

end polynomial


import pseudo_normed_group.basic
import analysis.normed_space.basic
/-!

# A seminormed group is pseudo-normed

This file contains the construction of a pseudo-normed group from a seminormed group.

-/
open_locale nnreal

namespace semi_normed_group

instance (V : Type*) [semi_normed_group V] : pseudo_normed_group V :=
{ filtration := λ c, {v | ∥v∥₊ ≤ c},
  filtration_mono := λ c₁ c₂ h v (hv : ∥v∥ ≤ c₁), le_trans hv h,
  zero_mem_filtration := λ c, by simp only [set.mem_set_of_eq, nnnorm_zero, zero_le],
  neg_mem_filtration := λ c v hv, by simpa only [set.mem_set_of_eq, nnnorm_neg] using hv,
  add_mem_filtration := λ c₁ c₂ v₁ v₂ hv₁ hv₂,
    calc ∥v₁ + v₂∥
        ≤ ∥v₁∥ + ∥v₂∥ : norm_add_le _ _
    ... ≤ c₁ + c₂ : add_le_add hv₁ hv₂ }

variables {V : Type*} [semi_normed_group V]

open pseudo_normed_group

lemma mem_filtration_nnnorm (v : V) : v ∈ filtration V (∥v∥₊) :=
show ∥v∥₊ ≤ ∥v∥₊, from le_rfl

@[simp] lemma mem_filtration_iff (v : V) (c : ℝ≥0) :
  v ∈ filtration V c ↔ ∥v∥₊ ≤ c := iff.rfl

end semi_normed_group


variables A B C D : Prop

example : A ∧ (A → B) → B :=
assume : A ∧ (A → B),
show B, from (and.right this) (and.left this)

example : A → ¬ (¬ A ∧ B) :=
assume h: A,
assume h₁ : ¬A ∧ B,
show false, from (and.left h₁) h

example : ¬ (A ∧ B) → (A → ¬ B) :=
assume h: ¬ (A ∧ B),
assume h₁ : A,
assume h₂ : B,
show false, from h (and.intro h₁ h₂)

example (h₁ : A ∨ B) (h₂ : A → C) (h₃ : B → D) : C ∨ D :=
show C ∨ D,
  from or.elim h₁
    (assume h: A, show C ∨ D, from or.inl (h₂ h))
    (assume h: B, show C ∨ D, from or.inr (h₃ h))

theorem or_resolve_left (h1 : A ∨ B) (h2 : ¬ A) : B :=
or.elim h1
  (assume h3 : A, show B, from false.elim (h2 h3))
  (assume h3 : B, show B, from h3)

example (h : ¬ A ∧ ¬ B) : ¬ (A ∨ B) :=
assume h₁ : A ∨ B,
show false,
  from or.elim h₁
    (assume h₂ : A, show false, from (and.left h) h₂)
    (assume h₂ : B, show false, from (and.right h) h₂)

open classical

example : ¬ (A ↔ ¬ A) :=
assume h1 : A ↔ ¬ A,
have h2: ¬A, from
  assume h4: A,
  have h5: ¬ A, from iff.elim_left h1 h4,
  show false, from h5 h4,
have h3: A, from
  by_contradiction
    (assume h: ¬A,
    have h6: A, from iff.elim_right h1 h,
    show false, from h h6),
show false, from h2 h3


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

! This file was ported from Lean 3 source module data.int.char_zero
! leanprover-community/mathlib commit acee671f47b8e7972a1eb6f4eed74b4b3abce829
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Int.Cast.Field

/-!
# Injectivity of `Int.Cast` into characteristic zero rings and fields.

-/

variable {α : Type _}

open Nat

namespace Int

@[simp]
theorem cast_eq_zero [AddGroupWithOne α] [CharZero α] {n : ℤ} : (n : α) = 0 ↔ n = 0 :=
  ⟨fun h => by
    cases n
    · erw [Int.cast_ofNat] at h
      exact congr_arg _ (Nat.cast_eq_zero.1 h)
    · rw [cast_negSucc, neg_eq_zero, Nat.cast_eq_zero] at h
      contradiction,
    fun h => by rw [h, cast_zero]⟩
#align int.cast_eq_zero Int.cast_eq_zero

@[simp, norm_cast]
theorem cast_inj [AddGroupWithOne α] [CharZero α] {m n : ℤ} : (m : α) = n ↔ m = n := by
  rw [← sub_eq_zero, ← cast_sub, cast_eq_zero, sub_eq_zero]
#align int.cast_inj Int.cast_inj

theorem cast_injective [AddGroupWithOne α] [CharZero α] : Function.Injective (Int.cast : ℤ → α)
  | _, _ => cast_inj.1
#align int.cast_injective Int.cast_injective

theorem cast_ne_zero [AddGroupWithOne α] [CharZero α] {n : ℤ} : (n : α) ≠ 0 ↔ n ≠ 0 :=
  not_congr cast_eq_zero
#align int.cast_ne_zero Int.cast_ne_zero

@[simp, norm_cast]
theorem cast_div_charZero {k : Type _} [DivisionRing k] [CharZero k] {m n : ℤ} (n_dvd : n ∣ m) :
    ((m / n : ℤ) : k) = m / n := by
  rcases eq_or_ne n 0 with (rfl | hn)
  · simp [Int.ediv_zero]
  · exact cast_div n_dvd (cast_ne_zero.mpr hn)
#align int.cast_div_char_zero Int.cast_div_charZero

end Int

theorem RingHom.injective_int {α : Type _} [NonAssocRing α] (f : ℤ →+* α) [CharZero α] :
    Function.Injective f :=
  Subsingleton.elim (Int.castRingHom _) f ▸ Int.cast_injective
#align ring_hom.injective_int RingHom.injective_int


/-
Copyright (c) 2018 Scott Morrison. 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.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.HasLimits
import Mathbin.CategoryTheory.DiscreteCategory

/-!
# Categorical (co)products

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

This file defines (co)products as special cases of (co)limits.

A product is the categorical generalization of the object `Π i, f i` where `f : ι → C`. It is a
limit cone over the diagram formed by `f`, implemented by converting `f` into a functor
`discrete ι ⥤ C`.

A coproduct is the dual concept.

## Main definitions

* a `fan` is a cone over a discrete category
* `fan.mk` constructs a fan from an indexed collection of maps
* a `pi` is a `limit (discrete.functor f)`

Each of these has a dual.

## Implementation notes
As with the other special shapes in the limits library, all the definitions here are given as
`abbreviation`s of the general statements for limits, so all the `simp` lemmas and theorems about
general limits can be used.
-/

noncomputable section

universe w v v₂ u u₂

open CategoryTheory

namespace CategoryTheory.Limits

variable {β : Type w}

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

-- We don't need an analogue of `pair` (for binary products), `parallel_pair` (for equalizers),
-- or `(co)span`, since we already have `discrete.functor`.
attribute [local tidy] tactic.discrete_cases

#print CategoryTheory.Limits.Fan /-
/-- A fan over `f : β → C` consists of a collection of maps from an object `P` to every `f b`. -/
abbrev Fan (f : β → C) :=
  Cone (Discrete.functor f)
#align category_theory.limits.fan CategoryTheory.Limits.Fan
-/

#print CategoryTheory.Limits.Cofan /-
/-- A cofan over `f : β → C` consists of a collection of maps from every `f b` to an object `P`. -/
abbrev Cofan (f : β → C) :=
  Cocone (Discrete.functor f)
#align category_theory.limits.cofan CategoryTheory.Limits.Cofan
-/

#print CategoryTheory.Limits.Fan.mk /-
/-- A fan over `f : β → C` consists of a collection of maps from an object `P` to every `f b`. -/
@[simps]
def Fan.mk {f : β → C} (P : C) (p : ∀ b, P ⟶ f b) : Fan f
    where
  pt := P
  π := { app := fun X => p X.as }
#align category_theory.limits.fan.mk CategoryTheory.Limits.Fan.mk
-/

#print CategoryTheory.Limits.Cofan.mk /-
/-- A cofan over `f : β → C` consists of a collection of maps from every `f b` to an object `P`. -/
@[simps]
def Cofan.mk {f : β → C} (P : C) (p : ∀ b, f b ⟶ P) : Cofan f
    where
  pt := P
  ι := { app := fun X => p X.as }
#align category_theory.limits.cofan.mk CategoryTheory.Limits.Cofan.mk
-/

#print CategoryTheory.Limits.Fan.proj /-
-- FIXME dualize as needed below (and rename?)
/-- Get the `j`th map in the fan -/
def Fan.proj {f : β → C} (p : Fan f) (j : β) : p.pt ⟶ f j :=
  p.π.app (Discrete.mk j)
#align category_theory.limits.fan.proj CategoryTheory.Limits.Fan.proj
-/

#print CategoryTheory.Limits.fan_mk_proj /-
@[simp]
theorem fan_mk_proj {f : β → C} (P : C) (p : ∀ b, P ⟶ f b) (j : β) : (Fan.mk P p).proj j = p j :=
  rfl
#align category_theory.limits.fan_mk_proj CategoryTheory.Limits.fan_mk_proj
-/

#print CategoryTheory.Limits.HasProduct /-
/-- An abbreviation for `has_limit (discrete.functor f)`. -/
abbrev HasProduct (f : β → C) :=
  HasLimit (Discrete.functor f)
#align category_theory.limits.has_product CategoryTheory.Limits.HasProduct
-/

#print CategoryTheory.Limits.HasCoproduct /-
/-- An abbreviation for `has_colimit (discrete.functor f)`. -/
abbrev HasCoproduct (f : β → C) :=
  HasColimit (Discrete.functor f)
#align category_theory.limits.has_coproduct CategoryTheory.Limits.HasCoproduct
-/

#print CategoryTheory.Limits.mkFanLimit /-
/-- Make a fan `f` into a limit fan by providing `lift`, `fac`, and `uniq` --
  just a convenience lemma to avoid having to go through `discrete` -/
@[simps]
def mkFanLimit {f : β → C} (t : Fan f) (lift : ∀ s : Fan f, s.pt ⟶ t.pt)
    (fac : ∀ (s : Fan f) (j : β), lift s ≫ t.proj j = s.proj j)
    (uniq : ∀ (s : Fan f) (m : s.pt ⟶ t.pt) (w : ∀ j : β, m ≫ t.proj j = s.proj j), m = lift s) :
    IsLimit t :=
  { lift
    fac := fun s j => by convert fac s j.as <;> simp
    uniq := fun s m w => uniq s m fun j => w (Discrete.mk j) }
#align category_theory.limits.mk_fan_limit CategoryTheory.Limits.mkFanLimit
-/

section

variable (C)

#print CategoryTheory.Limits.HasProductsOfShape /-
/-- An abbreviation for `has_limits_of_shape (discrete f)`. -/
abbrev HasProductsOfShape (β : Type v) :=
  HasLimitsOfShape.{v} (Discrete β)
#align category_theory.limits.has_products_of_shape CategoryTheory.Limits.HasProductsOfShape
-/

#print CategoryTheory.Limits.HasCoproductsOfShape /-
/-- An abbreviation for `has_colimits_of_shape (discrete f)`. -/
abbrev HasCoproductsOfShape (β : Type v) :=
  HasColimitsOfShape.{v} (Discrete β)
#align category_theory.limits.has_coproducts_of_shape CategoryTheory.Limits.HasCoproductsOfShape
-/

end

#print CategoryTheory.Limits.piObj /-
/-- `pi_obj f` computes the product of a family of elements `f`.
(It is defined as an abbreviation for `limit (discrete.functor f)`,
so for most facts about `pi_obj f`, you will just use general facts about limits.) -/
abbrev piObj (f : β → C) [HasProduct f] :=
  limit (Discrete.functor f)
#align category_theory.limits.pi_obj CategoryTheory.Limits.piObj
-/

#print CategoryTheory.Limits.sigmaObj /-
/-- `sigma_obj f` computes the coproduct of a family of elements `f`.
(It is defined as an abbreviation for `colimit (discrete.functor f)`,
so for most facts about `sigma_obj f`, you will just use general facts about colimits.) -/
abbrev sigmaObj (f : β → C) [HasCoproduct f] :=
  colimit (Discrete.functor f)
#align category_theory.limits.sigma_obj CategoryTheory.Limits.sigmaObj
-/

-- mathport name: «expr∏ »
notation "∏ " f:20 => piObj f

-- mathport name: «expr∐ »
notation "∐ " f:20 => sigmaObj f

#print CategoryTheory.Limits.Pi.π /-
/-- The `b`-th projection from the pi object over `f` has the form `∏ f ⟶ f b`. -/
abbrev Pi.π (f : β → C) [HasProduct f] (b : β) : ∏ f ⟶ f b :=
  limit.π (Discrete.functor f) (Discrete.mk b)
#align category_theory.limits.pi.π CategoryTheory.Limits.Pi.π
-/

#print CategoryTheory.Limits.Sigma.ι /-
/-- The `b`-th inclusion into the sigma object over `f` has the form `f b ⟶ ∐ f`. -/
abbrev Sigma.ι (f : β → C) [HasCoproduct f] (b : β) : f b ⟶ ∐ f :=
  colimit.ι (Discrete.functor f) (Discrete.mk b)
#align category_theory.limits.sigma.ι CategoryTheory.Limits.Sigma.ι
-/

#print CategoryTheory.Limits.productIsProduct /-
/-- The fan constructed of the projections from the product is limiting. -/
def productIsProduct (f : β → C) [HasProduct f] : IsLimit (Fan.mk _ (Pi.π f)) :=
  IsLimit.ofIsoLimit (limit.isLimit (Discrete.functor f)) (Cones.ext (Iso.refl _) (by tidy))
#align category_theory.limits.product_is_product CategoryTheory.Limits.productIsProduct
-/

#print CategoryTheory.Limits.coproductIsCoproduct /-
/-- The cofan constructed of the inclusions from the coproduct is colimiting. -/
def coproductIsCoproduct (f : β → C) [HasCoproduct f] : IsColimit (Cofan.mk _ (Sigma.ι f)) :=
  IsColimit.ofIsoColimit (colimit.isColimit (Discrete.functor f))
    (Cocones.ext (Iso.refl _) (by tidy))
#align category_theory.limits.coproduct_is_coproduct CategoryTheory.Limits.coproductIsCoproduct
-/

#print CategoryTheory.Limits.Pi.lift /-
/-- A collection of morphisms `P ⟶ f b` induces a morphism `P ⟶ ∏ f`. -/
abbrev Pi.lift {f : β → C} [HasProduct f] {P : C} (p : ∀ b, P ⟶ f b) : P ⟶ ∏ f :=
  limit.lift _ (Fan.mk P p)
#align category_theory.limits.pi.lift CategoryTheory.Limits.Pi.lift
-/

#print CategoryTheory.Limits.Sigma.desc /-
/-- A collection of morphisms `f b ⟶ P` induces a morphism `∐ f ⟶ P`. -/
abbrev Sigma.desc {f : β → C} [HasCoproduct f] {P : C} (p : ∀ b, f b ⟶ P) : ∐ f ⟶ P :=
  colimit.desc _ (Cofan.mk P p)
#align category_theory.limits.sigma.desc CategoryTheory.Limits.Sigma.desc
-/

#print CategoryTheory.Limits.Pi.map /-
/-- Construct a morphism between categorical products (indexed by the same type)
from a family of morphisms between the factors.
-/
abbrev Pi.map {f g : β → C} [HasProduct f] [HasProduct g] (p : ∀ b, f b ⟶ g b) : ∏ f ⟶ ∏ g :=
  limMap (Discrete.natTrans fun X => p X.as)
#align category_theory.limits.pi.map CategoryTheory.Limits.Pi.map
-/

#print CategoryTheory.Limits.Pi.map_mono /-
instance Pi.map_mono {f g : β → C} [HasProduct f] [HasProduct g] (p : ∀ b, f b ⟶ g b)
    [∀ i, Mono (p i)] : Mono <| Pi.map p :=
  @Limits.limMap_mono _ _ _ _ _
    (by
      dsimp
      infer_instance)
#align category_theory.limits.pi.map_mono CategoryTheory.Limits.Pi.map_mono
-/

#print CategoryTheory.Limits.Pi.mapIso /-
/-- Construct an isomorphism between categorical products (indexed by the same type)
from a family of isomorphisms between the factors.
-/
abbrev Pi.mapIso {f g : β → C} [HasProductsOfShape β C] (p : ∀ b, f b ≅ g b) : ∏ f ≅ ∏ g :=
  lim.mapIso (Discrete.natIso fun X => p X.as)
#align category_theory.limits.pi.map_iso CategoryTheory.Limits.Pi.mapIso
-/

#print CategoryTheory.Limits.Sigma.map /-
/-- Construct a morphism between categorical coproducts (indexed by the same type)
from a family of morphisms between the factors.
-/
abbrev Sigma.map {f g : β → C} [HasCoproduct f] [HasCoproduct g] (p : ∀ b, f b ⟶ g b) : ∐ f ⟶ ∐ g :=
  colimMap (Discrete.natTrans fun X => p X.as)
#align category_theory.limits.sigma.map CategoryTheory.Limits.Sigma.map
-/

#print CategoryTheory.Limits.Sigma.map_epi /-
instance Sigma.map_epi {f g : β → C} [HasCoproduct f] [HasCoproduct g] (p : ∀ b, f b ⟶ g b)
    [∀ i, Epi (p i)] : Epi <| Sigma.map p :=
  @Limits.colimMap_epi _ _ _ _ _
    (by
      dsimp
      infer_instance)
#align category_theory.limits.sigma.map_epi CategoryTheory.Limits.Sigma.map_epi
-/

#print CategoryTheory.Limits.Sigma.mapIso /-
/-- Construct an isomorphism between categorical coproducts (indexed by the same type)
from a family of isomorphisms between the factors.
-/
abbrev Sigma.mapIso {f g : β → C} [HasCoproductsOfShape β C] (p : ∀ b, f b ≅ g b) : ∐ f ≅ ∐ g :=
  colim.mapIso (Discrete.natIso fun X => p X.as)
#align category_theory.limits.sigma.map_iso CategoryTheory.Limits.Sigma.mapIso
-/

section Comparison

variable {D : Type u₂} [Category.{v₂} D] (G : C ⥤ D)

variable (f : β → C)

/- warning: category_theory.limits.pi_comparison -> CategoryTheory.Limits.piComparison is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))], 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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4)
but is expected to have type
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))], 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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.pi_comparison CategoryTheory.Limits.piComparisonₓ'. -/
/-- The comparison morphism for the product of `f`. This is an iso iff `G` preserves the product
of `f`, see `preserves_product.of_iso_comparison`. -/
def piComparison [HasProduct f] [HasProduct fun b => G.obj (f b)] :
    G.obj (∏ f) ⟶ ∏ fun b => G.obj (f b) :=
  Pi.lift fun b => G.map (Pi.π f b)
#align category_theory.limits.pi_comparison CategoryTheory.Limits.piComparison

/- warning: category_theory.limits.pi_comparison_comp_π -> CategoryTheory.Limits.piComparison_comp_π is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))] (b : β), 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} β C _inst_1 f _inst_3)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))) (CategoryTheory.CategoryStruct.comp.{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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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 (f b)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4) (CategoryTheory.Limits.Pi.π.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4 b)) (CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} β C _inst_1 f _inst_3) (f b) (CategoryTheory.Limits.Pi.π.{u1, u2, u4} β C _inst_1 f _inst_3 b))
but is expected to have type
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))] (b : β), 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} β C _inst_1 f _inst_3)) (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))) (CategoryTheory.CategoryStruct.comp.{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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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) (f b)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4) (CategoryTheory.Limits.Pi.π.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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 b)) (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} β C _inst_1 f _inst_3) (f b) (CategoryTheory.Limits.Pi.π.{u1, u2, u4} β C _inst_1 f _inst_3 b))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.pi_comparison_comp_π CategoryTheory.Limits.piComparison_comp_πₓ'. -/
@[simp, reassoc.1]
theorem piComparison_comp_π [HasProduct f] [HasProduct fun b => G.obj (f b)] (b : β) :
    piComparison G f ≫ Pi.π _ b = G.map (Pi.π f b) :=
  limit.lift_π _ (Discrete.mk b)
#align category_theory.limits.pi_comparison_comp_π CategoryTheory.Limits.piComparison_comp_π

/- warning: category_theory.limits.map_lift_pi_comparison -> CategoryTheory.Limits.map_lift_piComparison is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))] (P : C) (g : forall (j : β), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), 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 P) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4)) (CategoryTheory.CategoryStruct.comp.{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 P) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.piObj.{u1, u2, u4} β C _inst_1 (fun (j : β) => f j) _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4) (CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P (CategoryTheory.Limits.piObj.{u1, u2, u4} β C _inst_1 (fun (j : β) => f j) _inst_3) (CategoryTheory.Limits.Pi.lift.{u1, u2, u4} β C _inst_1 (fun (j : β) => f j) _inst_3 P g)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4)) (CategoryTheory.Limits.Pi.lift.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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 P) (fun (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 {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasProduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasProduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))] (P : C) (g : forall (j : β), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) P (f j)), 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) P) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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.CategoryStruct.comp.{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) P) (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} β C _inst_1 (fun (j : β) => f j) _inst_3)) (CategoryTheory.Limits.piObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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.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 (CategoryTheory.Limits.piObj.{u1, u2, u4} β C _inst_1 (fun (j : β) => f j) _inst_3) (CategoryTheory.Limits.Pi.lift.{u1, u2, u4} β C _inst_1 (fun (j : β) => f j) _inst_3 P g)) (CategoryTheory.Limits.piComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4)) (CategoryTheory.Limits.Pi.lift.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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) P) (fun (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.map_lift_pi_comparison CategoryTheory.Limits.map_lift_piComparisonₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
@[simp, reassoc.1]
theorem map_lift_piComparison [HasProduct f] [HasProduct fun b => G.obj (f b)] (P : C)
    (g : ∀ j, P ⟶ f j) : G.map (Pi.lift g) ≫ piComparison G f = Pi.lift fun j => G.map (g j) :=
  by
  ext
  trace
    "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
  simp [← G.map_comp]
#align category_theory.limits.map_lift_pi_comparison CategoryTheory.Limits.map_lift_piComparison

/- warning: category_theory.limits.sigma_comparison -> CategoryTheory.Limits.sigmaComparison is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))], 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} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3))
but is expected to have type
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))], 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} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.sigma_comparison CategoryTheory.Limits.sigmaComparisonₓ'. -/
/-- The comparison morphism for the coproduct of `f`. This is an iso iff `G` preserves the coproduct
of `f`, see `preserves_coproduct.of_iso_comparison`. -/
def sigmaComparison [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] :
    (∐ fun b => G.obj (f b)) ⟶ G.obj (∐ f) :=
  Sigma.desc fun b => G.map (Sigma.ι f b)
#align category_theory.limits.sigma_comparison CategoryTheory.Limits.sigmaComparison

/- warning: category_theory.limits.ι_comp_sigma_comparison -> CategoryTheory.Limits.ι_comp_sigmaComparison is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))] (b : β), 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 (f b)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} β C _inst_1 f _inst_3))) (CategoryTheory.CategoryStruct.comp.{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 (f b)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.Sigma.ι.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b)) _inst_4 b) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4)) (CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} β C _inst_1 f _inst_3) (CategoryTheory.Limits.Sigma.ι.{u1, u2, u4} β C _inst_1 f _inst_3 b))
but is expected to have type
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))] (b : β), 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) (f b)) (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} β C _inst_1 f _inst_3))) (CategoryTheory.CategoryStruct.comp.{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) (f b)) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3)) (CategoryTheory.Limits.Sigma.ι.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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 b) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4)) (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 b) (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} β C _inst_1 f _inst_3) (CategoryTheory.Limits.Sigma.ι.{u1, u2, u4} β C _inst_1 f _inst_3 b))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.ι_comp_sigma_comparison CategoryTheory.Limits.ι_comp_sigmaComparisonₓ'. -/
@[simp, reassoc.1]
theorem ι_comp_sigmaComparison [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] (b : β) :
    Sigma.ι _ b ≫ sigmaComparison G f = G.map (Sigma.ι f b) :=
  colimit.ι_desc _ (Discrete.mk b)
#align category_theory.limits.ι_comp_sigma_comparison CategoryTheory.Limits.ι_comp_sigmaComparison

/- warning: category_theory.limits.sigma_comparison_map_desc -> CategoryTheory.Limits.sigmaComparison_map_desc is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (f b))] (P : C) (g : forall (j : β), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), 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} β D _inst_2 (fun (b : β) => 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 P)) (CategoryTheory.CategoryStruct.comp.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3)) (CategoryTheory.Functor.obj.{u2, u3, u4, u5} C _inst_1 D _inst_2 G P) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4) (CategoryTheory.Functor.map.{u2, u3, u4, u5} C _inst_1 D _inst_2 G (CategoryTheory.Limits.sigmaObj.{u1, u2, u4} β C _inst_1 f _inst_3) P (CategoryTheory.Limits.Sigma.desc.{u1, u2, u4} β C _inst_1 f _inst_3 P g))) (CategoryTheory.Limits.Sigma.desc.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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 P) (fun (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 {β : Type.{u1}} {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) (f : β -> C) [_inst_3 : CategoryTheory.Limits.HasCoproduct.{u1, u2, u4} β C _inst_1 f] [_inst_4 : CategoryTheory.Limits.HasCoproduct.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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))] (P : C) (g : forall (j : β), Quiver.Hom.{succ u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} C (CategoryTheory.Category.toCategoryStruct.{u2, u4} C _inst_1)) (f j) P), 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} β D _inst_2 (fun (b : β) => 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) P)) (CategoryTheory.CategoryStruct.comp.{u3, u5} D (CategoryTheory.Category.toCategoryStruct.{u3, u5} D _inst_2) (CategoryTheory.Limits.sigmaObj.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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} β C _inst_1 f _inst_3)) (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) (CategoryTheory.Limits.sigmaComparison.{u1, u2, u3, u4, u5} β C _inst_1 D _inst_2 G f _inst_3 _inst_4) (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.sigmaObj.{u1, u2, u4} β C _inst_1 f _inst_3) P (CategoryTheory.Limits.Sigma.desc.{u1, u2, u4} β C _inst_1 f _inst_3 P g))) (CategoryTheory.Limits.Sigma.desc.{u1, u3, u5} β D _inst_2 (fun (b : β) => 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) P) (fun (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.sigma_comparison_map_desc CategoryTheory.Limits.sigmaComparison_map_descₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
@[simp, reassoc.1]
theorem sigmaComparison_map_desc [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] (P : C)
    (g : ∀ j, f j ⟶ P) :
    sigmaComparison G f ≫ G.map (Sigma.desc g) = Sigma.desc fun j => G.map (g j) :=
  by
  ext
  trace
    "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
  simp [← G.map_comp]
#align category_theory.limits.sigma_comparison_map_desc CategoryTheory.Limits.sigmaComparison_map_desc

end Comparison

variable (C)

#print CategoryTheory.Limits.HasProducts /-
/-- An abbreviation for `Π J, has_limits_of_shape (discrete J) C` -/
abbrev HasProducts :=
  ∀ J : Type w, HasLimitsOfShape (Discrete J) C
#align category_theory.limits.has_products CategoryTheory.Limits.HasProducts
-/

#print CategoryTheory.Limits.HasCoproducts /-
/-- An abbreviation for `Π J, has_colimits_of_shape (discrete J) C` -/
abbrev HasCoproducts :=
  ∀ J : Type w, HasColimitsOfShape (Discrete J) C
#align category_theory.limits.has_coproducts CategoryTheory.Limits.HasCoproducts
-/

variable {C}

#print CategoryTheory.Limits.has_smallest_products_of_hasProducts /-
theorem has_smallest_products_of_hasProducts [HasProducts.{w} C] : HasProducts.{0} C := fun J =>
  hasLimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift : Discrete (ULift.{w} J) ≌ _)
#align category_theory.limits.has_smallest_products_of_has_products CategoryTheory.Limits.has_smallest_products_of_hasProducts
-/

#print CategoryTheory.Limits.has_smallest_coproducts_of_hasCoproducts /-
theorem has_smallest_coproducts_of_hasCoproducts [HasCoproducts.{w} C] : HasCoproducts.{0} C :=
  fun J =>
  hasColimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift : Discrete (ULift.{w} J) ≌ _)
#align category_theory.limits.has_smallest_coproducts_of_has_coproducts CategoryTheory.Limits.has_smallest_coproducts_of_hasCoproducts
-/

#print CategoryTheory.Limits.hasProducts_of_limit_fans /-
theorem hasProducts_of_limit_fans (lf : ∀ {J : Type w} (f : J → C), Fan f)
    (lf_is_limit : ∀ {J : Type w} (f : J → C), IsLimit (lf f)) : HasProducts.{w} C :=
  fun J : Type w =>
  {
    HasLimit := fun F =>
      HasLimit.mk
        ⟨(Cones.postcompose Discrete.natIsoFunctor.inv).obj (lf fun j => F.obj ⟨j⟩),
          (IsLimit.postcomposeInvEquiv _ _).symm (lf_is_limit _)⟩ }
#align category_theory.limits.has_products_of_limit_fans CategoryTheory.Limits.hasProducts_of_limit_fans
-/

/-!
(Co)products over a type with a unique term.
-/

section Unique

variable {C} [Unique β] (f : β → C)

#print CategoryTheory.Limits.limitConeOfUnique /-
/-- The limit cone for the product over an index type with exactly one term. -/
@[simps]
def limitConeOfUnique : LimitCone (Discrete.functor f)
    where
  Cone :=
    { pt := f default
      π :=
        {
          app := fun j =>
            eqToHom
              (by
                dsimp
                congr ) } }
  IsLimit :=
    { lift := fun s => s.π.app default
      fac := fun s j =>
        by
        have w := (s.π.naturality (eq_to_hom (Unique.default_eq _))).symm
        dsimp at w
        simpa [eq_to_hom_map] using w
      uniq := fun s m w => by
        specialize w default
        dsimp at w
        simpa using w }
#align category_theory.limits.limit_cone_of_unique CategoryTheory.Limits.limitConeOfUnique
-/

#print CategoryTheory.Limits.hasProduct_unique /-
instance (priority := 100) hasProduct_unique : HasProduct f :=
  HasLimit.mk (limitConeOfUnique f)
#align category_theory.limits.has_product_unique CategoryTheory.Limits.hasProduct_unique
-/

#print CategoryTheory.Limits.productUniqueIso /-
/-- A product over a index type with exactly one term is just the object over that term. -/
@[simps]
def productUniqueIso : ∏ f ≅ f default :=
  IsLimit.conePointUniqueUpToIso (limit.isLimit _) (limitConeOfUnique f).IsLimit
#align category_theory.limits.product_unique_iso CategoryTheory.Limits.productUniqueIso
-/

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
#print CategoryTheory.Limits.colimitCoconeOfUnique /-
/-- The colimit cocone for the coproduct over an index type with exactly one term. -/
@[simps]
def colimitCoconeOfUnique : ColimitCocone (Discrete.functor f)
    where
  Cocone :=
    { pt := f default
      ι :=
        {
          app := fun j =>
            eqToHom
              (by
                trace
                  "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
                dsimp
                congr ) } }
  IsColimit :=
    { desc := fun s => s.ι.app default
      fac := fun s j =>
        by
        have w := s.ι.naturality (eq_to_hom (Unique.eq_default _))
        dsimp at w
        simpa [eq_to_hom_map] using w
      uniq := fun s m w => by
        specialize w default
        dsimp at w
        simpa using w }
#align category_theory.limits.colimit_cocone_of_unique CategoryTheory.Limits.colimitCoconeOfUnique
-/

#print CategoryTheory.Limits.hasCoproduct_unique /-
instance (priority := 100) hasCoproduct_unique : HasCoproduct f :=
  HasColimit.mk (colimitCoconeOfUnique f)
#align category_theory.limits.has_coproduct_unique CategoryTheory.Limits.hasCoproduct_unique
-/

#print CategoryTheory.Limits.coproductUniqueIso /-
/-- A coproduct over a index type with exactly one term is just the object over that term. -/
@[simps]
def coproductUniqueIso : ∐ f ≅ f default :=
  IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) (colimitCoconeOfUnique f).IsColimit
#align category_theory.limits.coproduct_unique_iso CategoryTheory.Limits.coproductUniqueIso
-/

end Unique

section Reindex

variable {C} {γ : Type v} (ε : β ≃ γ) (f : γ → C)

section

variable [HasProduct f] [HasProduct (f ∘ ε)]

#print CategoryTheory.Limits.Pi.reindex /-
/-- Reindex a categorical product via an equivalence of the index types. -/
def Pi.reindex : piObj (f ∘ ε) ≅ piObj f :=
  HasLimit.isoOfEquivalence (Discrete.equivalence ε) (Discrete.natIso fun i => Iso.refl _)
#align category_theory.limits.pi.reindex CategoryTheory.Limits.Pi.reindex
-/

#print CategoryTheory.Limits.Pi.reindex_hom_π /-
@[simp, reassoc.1]
theorem Pi.reindex_hom_π (b : β) : (Pi.reindex ε f).Hom ≫ Pi.π f (ε b) = Pi.π (f ∘ ε) b :=
  by
  dsimp [pi.reindex]
  simp only [has_limit.iso_of_equivalence_hom_π, discrete.nat_iso_inv_app,
    equivalence.equivalence_mk'_counit, discrete.equivalence_counit_iso, discrete.nat_iso_hom_app,
    eq_to_iso.hom, eq_to_hom_map]
  dsimp
  simpa [eq_to_hom_map] using
    limit.w (discrete.functor (f ∘ ε)) (discrete.eq_to_hom' (ε.symm_apply_apply b))
#align category_theory.limits.pi.reindex_hom_π CategoryTheory.Limits.Pi.reindex_hom_π
-/

#print CategoryTheory.Limits.Pi.reindex_inv_π /-
@[simp, reassoc.1]
theorem Pi.reindex_inv_π (b : β) : (Pi.reindex ε f).inv ≫ Pi.π (f ∘ ε) b = Pi.π f (ε b) := by
  simp [iso.inv_comp_eq]
#align category_theory.limits.pi.reindex_inv_π CategoryTheory.Limits.Pi.reindex_inv_π
-/

end

section

variable [HasCoproduct f] [HasCoproduct (f ∘ ε)]

#print CategoryTheory.Limits.Sigma.reindex /-
/-- Reindex a categorical coproduct via an equivalence of the index types. -/
def Sigma.reindex : sigmaObj (f ∘ ε) ≅ sigmaObj f :=
  HasColimit.isoOfEquivalence (Discrete.equivalence ε) (Discrete.natIso fun i => Iso.refl _)
#align category_theory.limits.sigma.reindex CategoryTheory.Limits.Sigma.reindex
-/

#print CategoryTheory.Limits.Sigma.ι_reindex_hom /-
@[simp, reassoc.1]
theorem Sigma.ι_reindex_hom (b : β) :
    Sigma.ι (f ∘ ε) b ≫ (Sigma.reindex ε f).Hom = Sigma.ι f (ε b) :=
  by
  dsimp [sigma.reindex]
  simp only [has_colimit.iso_of_equivalence_hom_π, equivalence.equivalence_mk'_unit,
    discrete.equivalence_unit_iso, discrete.nat_iso_hom_app, eq_to_iso.hom, eq_to_hom_map,
    discrete.nat_iso_inv_app]
  dsimp
  simp [eq_to_hom_map, ←
    colimit.w (discrete.functor f) (discrete.eq_to_hom' (ε.apply_symm_apply (ε b)))]
#align category_theory.limits.sigma.ι_reindex_hom CategoryTheory.Limits.Sigma.ι_reindex_hom
-/

#print CategoryTheory.Limits.Sigma.ι_reindex_inv /-
@[simp, reassoc.1]
theorem Sigma.ι_reindex_inv (b : β) :
    Sigma.ι f (ε b) ≫ (Sigma.reindex ε f).inv = Sigma.ι (f ∘ ε) b := by simp [iso.comp_inv_eq]
#align category_theory.limits.sigma.ι_reindex_inv CategoryTheory.Limits.Sigma.ι_reindex_inv
-/

end

end Reindex

end CategoryTheory.Limits