import for_mathlib.bicartesian3

noncomputable theory

universe u

open category_theory category_theory.limits

section part1

-- jmc: feel free to generalize to arbitrary abelian cats
variables {A B C D : Ab.{u}} {f : A ⟶ B} {g : B ⟶ C} {h : C ⟶ D}
variables {α : A ⟶ A} {β : B ⟶ B} {γ : C ⟶ C} {δ : D ⟶ D}
open_locale zero_object

lemma bicartesian_of_id_of_end_of_end_of_id
  (H : exact_seq Ab.{u} [f, g, h])
  (sq1 : commsq f α β f) (sq2 : commsq g β γ g) (sq3 : commsq h γ δ h)
  (hα : α = -𝟙 _) (hδ : δ = -𝟙 _) :
  sq2.bicartesian :=
begin
  have aux : _ := _,
  rw commsq.bicartesian_iff_isos _ _ aux aux sq2.kernel sq2 sq2.cokernel,
  swap,
  { apply exact.cons, { exact exact_kernel_ι },
    apply exact.exact_seq, { apply abelian.exact_cokernel } },
  split,
  { let t : A ⟶ kernel g := kernel.lift g f ((exact_iff_exact_seq _ _).2 (H.extract 0 2)).w,
    haveI : is_iso α,
    { rw hα,
      apply_instance },
    refine @abelian.is_iso_of_is_iso_of_is_iso_of_is_iso_of_is_iso _ _ _
      (kernel t) A (kernel g) 0
      (kernel t) A (kernel g) 0
      (kernel.ι t) t 0
      (kernel.ι t) t 0
      (-𝟙 _) α _ 0
      _ _ _ 0 0 0 0 0 _ _ _ _ _ _ _ _ _ _ _,
    { simp only [preadditive.neg_comp, category.id_comp, preadditive.comp_neg, category.comp_id,
        hα] },
    { simp only [← cancel_mono (kernel.ι g), sq1.w, category.assoc, kernel.lift_ι,
        kernel.lift_ι_assoc] },
    { exact subsingleton.elim _ _ },
    { exact subsingleton.elim _ _ },
    { exact exact_kernel_ι },
    { exact exact_epi_zero t },
    { exact exact_of_zero 0 0 },
    { exact exact_kernel_ι },
    { exact exact_epi_zero t },
    { exact exact_of_zero 0 0 } },
  { let t : cokernel g ⟶ D := cokernel.desc g h ((exact_iff_exact_seq _ _).2 (H.extract 1 2)).w,
    haveI : is_iso δ,
    { rw hδ,
      apply_instance },
    refine @abelian.is_iso_of_is_iso_of_is_iso_of_is_iso_of_is_iso _ _ _
      0 0 (cokernel g) D 0 0 (cokernel g) D
      0 0 t 0 0 t
      0 0 _ δ
      _ _ _ (cokernel t) (cokernel t) (cokernel.π t) (cokernel.π t) (-𝟙 _) _ _ _ _ _ _ _ _ _ _ _,
    { exact subsingleton.elim _ _ },
    { exact subsingleton.elim _ _ },
    { simp only [← cancel_epi (cokernel.π g), sq3.w, cokernel.π_desc_assoc, category.assoc,
        cokernel.π_desc] },
    { simp only [hδ, preadditive.neg_comp, category.id_comp, preadditive.comp_neg,
        category.comp_id] },
    { exact exact_of_zero 0 0 },
    { exact exact_zero_mono t },
    { exact abelian.exact_cokernel t },
    { exact exact_of_zero 0 0 },
    { exact exact_zero_mono t },
    { exact abelian.exact_cokernel t } }
end

end part1

section part2
open_locale zero_object

-- jmc: this part does not depend on the first section,
-- it's the same file because two lemmas have the same theme

-- jmc: feel free to generalize to arbitrary abelian cats
variables {A₁₁ A₁₂ A₁₃ A₁₄ A₁₅ : Ab.{u}}
variables {A₂₁ A₂₂ A₂₃ A₂₄ A₂₅ : Ab.{u}}
-- horizontal maps are `f`s and vertical maps are `g`s
variables {f₁₁ : A₁₁ ⟶ A₁₂} {f₁₂ : A₁₂ ⟶ A₁₃} {f₁₃ : A₁₃ ⟶ A₁₄} {f₁₄ : A₁₄ ⟶ A₁₅}
variables {g₁₁ : A₁₁ ⟶ A₂₁} {g₁₂ : A₁₂ ⟶ A₂₂} {g₁₃ : A₁₃ ⟶ A₂₃} {g₁₄ : A₁₄ ⟶ A₂₄} {g₁₅ : A₁₅ ⟶ A₂₅}
variables {f₂₁ : A₂₁ ⟶ A₂₂} {f₂₂ : A₂₂ ⟶ A₂₃} {f₂₃ : A₂₃ ⟶ A₂₄} {f₂₄ : A₂₄ ⟶ A₂₅}

lemma exact_kernel_cokernel : exact_seq Ab.{u} [kernel.ι f₁₁, f₁₁, cokernel.π f₁₁] :=
begin
  apply exact.cons, { exact exact_kernel_ι },
  apply exact.exact_seq, { apply abelian.exact_cokernel }
end

lemma is_iso_kernel_map_of_bicartesian {sq : commsq f₁₁ g₁₁ g₁₂ f₂₁} (H : sq.bicartesian) :
  is_iso (kernel.map f₁₁ f₂₁ _ _ sq.w) :=
begin
  rw commsq.bicartesian_iff_isos _ _ _ _ sq.kernel sq sq.cokernel at H,
  { exact H.1 },
  { exact exact_kernel_cokernel },
  { exact exact_kernel_cokernel }
end

lemma is_iso_cokernel_map_of_bicartesian {sq : commsq f₁₁ g₁₁ g₁₂ f₂₁} (H : sq.bicartesian) :
  is_iso (cokernel.map f₁₁ f₂₁ _ _ sq.w) :=
begin
  rw commsq.bicartesian_iff_isos _ _ _ _ sq.kernel sq sq.cokernel at H,
  { exact H.2 },
  { exact exact_kernel_cokernel },
  { exact exact_kernel_cokernel }
end

section
variables (f₁₁)

lemma exact_epi_comp_iff [epi f₁₁] : exact (f₁₁ ≫ f₁₂) f₁₃ ↔ exact f₁₂ f₁₃ :=
begin
  refine ⟨λ h, _, λ h, exact_epi_comp h⟩,
  rw abelian.exact_iff at h,
  let hc := is_colimit_of_is_colimit_comp (colimit.is_colimit (parallel_pair (f₁₁ ≫ f₁₂) 0))
    (by rw [← cancel_epi f₁₁, ← category.assoc, cokernel_cofork.condition, comp_zero]),
  refine (abelian.exact_iff' _ _ (limit.is_limit _) hc).2 ⟨_, h.2⟩,
  exact zero_of_epi_comp f₁₁ (by rw [← h.1, category.assoc])
end

end

section
variables (f₁₃)

lemma exact_comp_mono_iff [mono f₁₃] : exact f₁₁ (f₁₂ ≫ f₁₃) ↔ exact f₁₁ f₁₂ :=
begin
  refine ⟨λ h, _, λ h, exact_comp_mono h⟩,
  rw abelian.exact_iff at h,
  let hc := is_limit_of_is_limit_comp (limit.is_limit (parallel_pair (f₁₂ ≫ f₁₃) 0))
    (by rw [← cancel_mono f₁₃, category.assoc, kernel_fork.condition, zero_comp]),
  refine (abelian.exact_iff' _ _ hc (colimit.is_colimit _)).2 ⟨_, h.2⟩,
  exact zero_of_comp_mono f₁₃ (by rw [← h.1, category.assoc])
end

end

lemma iso_of_bicartesian_of_bicartesian
  (h_ex₁ : exact_seq Ab.{u} [f₁₁, f₁₂, f₁₃, f₁₄])
  (h_ex₂ : exact_seq Ab.{u} [f₂₁, f₂₂, f₂₃, f₂₄])
  (sq1 : commsq f₁₁ g₁₁ g₁₂ f₂₁) (sq2 : commsq f₁₂ g₁₂ g₁₃ f₂₂)
  (sq3 : commsq f₁₃ g₁₃ g₁₄ f₂₃) (sq4 : commsq f₁₄ g₁₄ g₁₅ f₂₄)
  (H1 : sq1.bicartesian) (H4 : sq4.bicartesian) :
  is_iso g₁₃ :=
begin
  haveI := is_iso_cokernel_map_of_bicartesian H1,
  haveI := is_iso_kernel_map_of_bicartesian H4,
  let f₁₂' := cokernel.desc f₁₁ f₁₂ ((exact_iff_exact_seq _ _).2 (h_ex₁.extract 0 2)).w,
  let f₁₃' := kernel.lift f₁₄ f₁₃ ((exact_iff_exact_seq _ _).2 (h_ex₁.extract 2 2)).w,
  let f₂₂' := cokernel.desc f₂₁ f₂₂ ((exact_iff_exact_seq _ _).2 (h_ex₂.extract 0 2)).w,
  let f₂₃' := kernel.lift f₂₄ f₂₃ ((exact_iff_exact_seq _ _).2 (h_ex₂.extract 2 2)).w,
  refine @abelian.is_iso_of_is_iso_of_is_iso_of_is_iso_of_is_iso _ _ _
    0 (cokernel f₁₁) A₁₃ (kernel f₁₄) 0 (cokernel f₂₁) A₂₃ (kernel f₂₄)
    0 f₁₂' f₁₃' 0 f₂₂' f₂₃'
    0 (cokernel.map f₁₁ f₂₁ _ _ sq1.w) g₁₃ (kernel.map f₁₄ f₂₄ _ _ sq4.w)
    _ _ _ 0 0 0 0 0 _ _ _ _ _ _ _ _ _ _ _,
  { exact subsingleton.elim _ _ },
  { simp only [← cancel_epi (cokernel.π f₁₁), sq2.w, cokernel.π_desc_assoc, category.assoc,
      cokernel.π_desc] },
  { simp only [← cancel_mono (kernel.ι f₂₄), sq3.w, category.assoc, kernel.lift_ι,
      kernel.lift_ι_assoc] },
  { exact subsingleton.elim _ _ },
  { exact exact_zero_mono f₁₂' },
  { rw [← exact_epi_comp_iff (cokernel.π f₁₁), cokernel.π_desc,
      ← exact_comp_mono_iff (kernel.ι f₁₄), kernel.lift_ι],
    exact (exact_iff_exact_seq _ _).2 (h_ex₁.extract 1 2) },
  { exact exact_epi_zero f₁₃' },
  { exact exact_zero_mono f₂₂' },
  { rw [← exact_epi_comp_iff (cokernel.π f₂₁), cokernel.π_desc,
      ← exact_comp_mono_iff (kernel.ι f₂₄), kernel.lift_ι],
    exact (exact_iff_exact_seq _ _).2 (h_ex₂.extract 1 2) },
  { exact exact_epi_zero f₂₃' }
end

lemma iso_of_zero_of_bicartesian
  (h_ex₁ : exact_seq Ab.{u} [f₁₂, f₁₃, f₁₄])
  (h_ex₂ : exact_seq Ab.{u} [f₂₂, f₂₃, f₂₄])
  (hz₁ : is_zero A₁₂) (hz₂ : is_zero A₂₂)
  (sq2 : commsq f₁₂ g₁₂ g₁₃ f₂₂) (sq3 : commsq f₁₃ g₁₃ g₁₄ f₂₃)
  (sq4 : commsq f₁₄ g₁₄ g₁₅ f₂₄) (H4 : sq4.bicartesian) :
  is_iso g₁₃ :=
begin
  have aux₁ : exact (0 : A₁₂ ⟶ A₁₂) f₁₂,
  { have : mono f₁₂ := ⟨λ _ x y h, hz₁.eq_of_tgt _ _⟩, rwa (abelian.tfae_mono A₁₂ f₁₂).out 2 0 },
  have aux₂ : exact (0 : A₂₂ ⟶ A₂₂) f₂₂,
  { have : mono f₂₂ := ⟨λ _ x y h, hz₂.eq_of_tgt _ _⟩, rwa (abelian.tfae_mono A₂₂ f₂₂).out 2 0 },
  refine iso_of_bicartesian_of_bicartesian (aux₁.cons h_ex₁) (aux₂.cons h_ex₂) _ sq2 sq3 sq4 _ H4,
  { exact g₁₂ },
  { exact commsq.of_eq (zero_comp.trans comp_zero.symm) },
  { apply commsq.bicartesian.of_is_limit_of_is_colimt,
    { refine pullback_cone.is_limit.mk _ (λ s, 0)
        (λ s, hz₁.eq_of_tgt _ _) (λ s, hz₂.eq_of_tgt _ _) _,
      intros, apply hz₁.eq_of_tgt, },
    { refine pushout_cocone.is_colimit.mk _ (λ s, 0)
        (λ s, hz₁.eq_of_src _ _) (λ s, hz₂.eq_of_src _ _) _,
      intros, apply hz₂.eq_of_src, } },
end

end part2


-- La_tactica_de_cuerpos.lean
-- La táctica de cuerpos
-- José A. Alonso Jiménez
-- Sevilla, 12 de agosto de 2021
-- ---------------------------------------------------------------------

import data.complex.basic
import tactic

example (a b c d x y : ℂ) (hx : x ≠ 0) (hy : y ≠ 0) :
  a + b / x + c / x^2 + d / x^3 = a + x⁻¹ * (y * b / y + (d / x + c) / x) :=
begin
  field_simp,
  ring,
end


-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Tim Baumann, Stephen Morgan, Scott Morrison

import category_theory.natural_transformation

namespace category_theory

universes v₁ v₂ v₃ u₁ u₂ u₃ -- declare the `v`'s first; see `category_theory.category` for an explanation

open nat_trans

variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : category.{v₂} D]
include 𝒞 𝒟

/--
`functor.category C D` gives the category structure on functors and natural transformations
between categories `C` and `D`.

Notice that if `C` and `D` are both small categories at the same universe level,
this is another small category at that level.
However if `C` and `D` are both large categories at the same universe level,
this is a small category at the next higher level.
-/
instance functor.category : category.{(max u₁ v₂)} (C ⥤ D) :=
{ hom     := λ F G, F ⟹ G,
  id      := λ F, nat_trans.id F,
  comp    := λ _ _ _ α β, α ⊟ β }

variables {C D} {E : Type u₃} [ℰ : category.{v₃} E]

namespace functor.category

section

@[simp] lemma id_app (F : C ⥤ D) (X : C) : (𝟙 F : F ⟹ F).app X = 𝟙 (F.obj X) := rfl
@[simp] lemma comp_app {F G H : C ⥤ D} (α : F ⟶ G) (β : G ⟶ H) (X : C) :
  (α ≫ β).app X = α.app X ≫ β.app X := rfl
end

namespace nat_trans
-- This section gives two lemmas about natural transformations
-- between functors into functor categories,
-- spelling them out in components.

include ℰ

lemma app_naturality {F G : C ⥤ (D ⥤ E)} (T : F ⟹ G) (X : C) {Y Z : D} (f : Y ⟶ Z) :
  ((F.obj X).map f) ≫ ((T.app X).app Z) = ((T.app X).app Y) ≫ ((G.obj X).map f) :=
(T.app X).naturality f

lemma naturality_app {F G : C ⥤ (D ⥤ E)} (T : F ⟹ G) (Z : D) {X Y : C} (f : X ⟶ Y) :
  ((F.map f).app Z) ≫ ((T.app Y).app Z) = ((T.app X).app Z) ≫ ((G.map f).app Z) :=
congr_fun (congr_arg app (T.naturality f)) Z

end nat_trans

end functor.category

namespace functor

include ℰ

protected def flip (F : C ⥤ (D ⥤ E)) : D ⥤ (C ⥤ E) :=
{ obj := λ k,
  { obj := λ j, (F.obj j).obj k,
    map := λ j j' f, (F.map f).app k,
    map_id' := λ X, begin rw category_theory.functor.map_id, refl end,
    map_comp' := λ X Y Z f g, by rw [functor.map_comp, ←functor.category.comp_app] },
  map := λ c c' f,
  { app := λ j, (F.obj j).map f,
    naturality' := λ X Y g, by dsimp; rw ←nat_trans.naturality } }.

@[simp] lemma flip_obj_map (F : C ⥤ (D ⥤ E)) {c c' : C} (f : c ⟶ c') (d : D) :
  ((F.flip).obj d).map f = (F.map f).app d := rfl

end functor

end category_theory


import analysis.p_series
import number_theory.arithmetic_function
import algebra.squarefree
import algebra.order.field
import data.list.intervals
import data.real.sqrt
import tactic
import measure_theory.integral.interval_integral
import analysis.special_functions.integrals
import general

noncomputable theory
-- open_locale classical
open nat finset list finsupp set function filter measure_theory
open_locale topological_space interval big_operators filter ennreal asymptotics

namespace squarefree_sums

lemma fff
{a : ℕ}
:
(2 : ℝ)⁻¹ * real.sqrt ↑a ≤ (2 : ℝ)⁻¹ * (sqrt a + 1) :=
begin
  have : 0 ≤ real.sqrt ↑a,
  {
    rw real.le_sqrt,
    simp, simp,
  },
  exact mul_le_mul (by simp) real_sqrt_le_nat_sqrt_succ this (by simp),
end

lemma ffff
{c : ℕ}
:
(2 : ℝ)⁻¹ * (↑c + 1 + 1) = (2 : ℝ)⁻¹ * ↑c + 1 :=
begin
  ring,
end

lemma huu
{a b c : ℝ}
(ha : a ≠ 0)
:
a * b = a * c ↔ b = c
:=
begin
  exact mul_right_inj' ha,
end

lemma ffa
{c : ℕ}
(hc : 2 ≤ c)
:
(2 : ℝ)⁻¹ * ↑c + 1 ≤ ↑c ↔ (c : ℝ) + 1 ≤ 2 * ↑c :=
begin
  split,
  intros h,
  norm_cast,
  linarith [hc],
  have bb' : c + 1 ≤ 2 * c, {
    linarith,
  },

  intros h,
  have : (2 : ℝ)⁻¹ * ↑c + 1 = (2 : ℝ)⁻¹ * (↑c + 2), ring,
  rw this,
  rw inv_mul_le_iff,
  norm_cast,
  linarith,
  linarith,
end

lemma ffb
{b c : ℕ}
(hb : 200 ≤ b)
(hbc : sqrt b = c + 1)
:
2 ≤ c :=
begin
  have : 14 = sqrt 200, {
    rw nat.eq_sqrt,
    split,
    linarith,
    linarith,
  },
  have : sqrt 200 ≤ sqrt b, exact nat.sqrt_le_sqrt hb,
  linarith,
end

lemma extraordinarily_annoying
{b c : ℕ}
(hb : 200 ≤ b)
(hbc : sqrt b = c + 1)
:
- ↑c ^ (-(2 : ℝ) + 1) / (-(2 : ℝ) + 1) ≤ 2 * |(b : ℝ) ^ (2 : ℝ)⁻¹| / ↑b
:=
begin
  have hc : 2 ≤ c, exact ffb hb hbc,
  have blah : ((-(1 : ℝ)) / 2) = -(((1 : ℝ)) / 2), {
    simp,
    ring,
  },
  have : (-(2 : ℝ) + 1) = -1, ring,
  have fooblah : (2 : ℝ)⁻¹ = (1 : ℝ) / 2, simp,
  ring_nf,
  rw this,
  rw abs_of_nonneg,
  have : 2 * (b : ℝ)⁻¹ * ↑b ^ (2 : ℝ)⁻¹ = 2 * (b : ℝ) ^ (-(1 : ℝ) / 2), {
    rw ← real.rpow_neg_one,
    rw blah,
    rw fooblah,
    rw mul_assoc,
    rw ← real.rpow_add,
    ring_nf,
    norm_cast,
    linarith [hb],
  },
  rw this,
  rw ← inv_le_inv,
  rw real.rpow_neg,
  simp,
  have : (2 * (b : ℝ) ^ ((-(1 : ℝ)) / 2))⁻¹ = (2 : ℝ)⁻¹ * b ^ ((1 : ℝ) / 2), {
    rw mul_inv₀,
    have : (2 : ℝ)⁻¹ ≠ 0, simp,
    rw mul_right_inj' this,
    rw ← real.rpow_neg,
    ring_nf,
    norm_cast,
    linarith [hb],
  },
  rw this,
  rw ← real.sqrt_eq_rpow,
  transitivity,
  exact fff,
  rw hbc,
  simp,
  rw ffff,
  rw ffa hc,
  norm_cast,
  linarith,
  norm_cast,
  calc 0 ≤ 2 : by linarith ... ≤ c : hc,
  apply mul_pos,
  linarith,
  rw blah,
  rw real.rpow_neg,
  rw inv_pos,
  rw ← real.sqrt_eq_rpow,
  rw lt_iff_le_and_ne,
  split,
  rw real.le_sqrt,
  simp,
  norm_cast,
  linarith [hb],
  symmetry,
  by_contradiction H,
  rw real.sqrt_eq_zero' at H,
  norm_cast at H,
  linarith [hb, H],
  norm_cast,
  linarith [hb],
  rw real.rpow_neg,
  rw inv_pos,
  norm_cast,
  linarith [hc],
  norm_cast,
  linarith [hc],
  rw fooblah,
  rw ← real.sqrt_eq_rpow,
  rw real.le_sqrt,
  simp,
  norm_cast,
  simp,
end

lemma asdfasdf
{b : ℕ}
(hb : 200 ≤ b) :
∃ (c : ℕ), sqrt b = c + 1 ∧ 0 < c :=
begin
  have sqrt200 : 14 = sqrt 200, {
    rw nat.eq_sqrt,
    split,
    linarith,
    linarith,
  },
  have : sqrt 200 ≤ sqrt b, exact nat.sqrt_le_sqrt hb,
  use (sqrt b).pred,
  split,
  rw add_one,
  exact (nat.succ_pred_eq_of_pos (calc 0 < 14 : by linarith ... = sqrt 200 : sqrt200 ... ≤ sqrt b : this)).symm,
  calc 0 < 13 : by linarith ... = (14 : ℕ).pred : by simp ... = (sqrt 200).pred : congr_arg pred sqrt200 ... ≤ (sqrt b).pred : pred_le_pred this,
end

end squarefree_sums


import analysis.analytic.basic
import hp.tactic.hp_interactive
import examples.analysis
universe u

namespace examples

variables {X Y: Type} [met_space X] [met_space Y] {A B : set X}

example {A B : set X} : is_open A → is_open B → is_open (A ∩ B)
| oa ob y ⟨ha, hb⟩ :=
  let ⟨εa, h2a, h3a⟩ := oa y ha in
  let ⟨εb, h2b, h3b⟩ := ob y hb in
  ⟨ εa ⊓ εb
  , lt_min h2a h2b
  , λ x dh,
    ⟨ h3a _ $ lt_of_lt_of_le dh $ min_le_left  _ _
    , h3b _ $ lt_of_lt_of_le dh $ min_le_right _ _
    ⟩
  ⟩

example {A B : set X} : is_open A → is_open B → is_open (A ∩ B) :=
begin
  assume: is_open A,
  assume: is_open B,
  assume y : X,
  assume: y ∈ A ∩ B,
  show ∃ (ε : ℝ), ε > 0 ∧ ∀ (x : X), (dist x y < ε) → ((A ∩ B) x),
  cases ‹y ∈ A ∩ B›,
  obtain ⟨η, η_pos, h_η⟩ : ∃ η, (η > 0) ∧ ∀ x, dist x y < η → x ∈ A,
    apply ‹is_open A›, apply ‹y ∈ A›,
  obtain ⟨θ, θ_pos, h_θ⟩ : ∃ θ, (θ > 0) ∧ ∀ x, dist x y < θ → x ∈ B,
    apply ‹is_open B›, apply ‹y ∈ B›,
  let ε := min η θ,
  have : ε > 0,
    apply lt_min ‹η > 0› ‹θ > 0›,
  use [ε, ‹ε > 0›],
  assume x : X,
  assume : dist x y < ε,
  show x ∈ A ∩ B,
  have : dist x y < η,
    calc dist x y < min η θ : ‹dist x y < ε›
              ... ≤ η       : min_le_left _ _,
  have : dist x y < θ,
    calc dist x y < min η θ : ‹dist x y < ε›
              ... ≤ θ       : min_le_right _ _,
  split,
  show x ∈ A,
    apply h_η, apply ‹dist x y < η›,
  show x ∈ B,
    apply h_θ, apply ‹dist x y < θ›,
end

end examples


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

! This file was ported from Lean 3 source module category_theory.functor.reflects_isomorphisms
! leanprover-community/mathlib commit ef7acf407d265ad4081c8998687e994fa80ba70c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Balanced
import Mathbin.CategoryTheory.Functor.EpiMono
import Mathbin.CategoryTheory.Functor.FullyFaithful

/-!
# Functors which reflect isomorphisms

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

A functor `F` reflects isomorphisms if whenever `F.map f` is an isomorphism, `f` was too.

It is formalized as a `Prop` valued typeclass `reflects_isomorphisms F`.

Any fully faithful functor reflects isomorphisms.
-/

open CategoryTheory CategoryTheory.Functor

namespace CategoryTheory

universe v₁ v₂ v₃ u₁ u₂ u₃

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

section ReflectsIso

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

variable {E : Type u₃} [Category.{v₃} E]

#print CategoryTheory.ReflectsIsomorphisms /-
/-- Define what it means for a functor `F : C ⥤ D` to reflect isomorphisms: for any
morphism `f : A ⟶ B`, if `F.map f` is an isomorphism then `f` is as well.
Note that we do not assume or require that `F` is faithful.
-/
class ReflectsIsomorphisms (F : C ⥤ D) : Prop where
  reflects : ∀ {A B : C} (f : A ⟶ B) [IsIso (F.map f)], IsIso f
#align category_theory.reflects_isomorphisms CategoryTheory.ReflectsIsomorphisms
-/

/- warning: category_theory.is_iso_of_reflects_iso -> CategoryTheory.isIso_of_reflects_iso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] {A : C} {B : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) A B) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F A) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F B) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F A B f)] [_inst_5 : CategoryTheory.ReflectsIsomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], CategoryTheory.IsIso.{u1, u3} C _inst_1 A B f
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] {A : C} {B : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) A B) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.IsIso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) A) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) B) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) A B f)] [_inst_5 : CategoryTheory.ReflectsIsomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], CategoryTheory.IsIso.{u1, u3} C _inst_1 A B f
Case conversion may be inaccurate. Consider using '#align category_theory.is_iso_of_reflects_iso CategoryTheory.isIso_of_reflects_isoₓ'. -/
/-- If `F` reflects isos and `F.map f` is an iso, then `f` is an iso. -/
theorem isIso_of_reflects_iso {A B : C} (f : A ⟶ B) (F : C ⥤ D) [IsIso (F.map f)]
    [ReflectsIsomorphisms F] : IsIso f :=
  ReflectsIsomorphisms.reflects F f
#align category_theory.is_iso_of_reflects_iso CategoryTheory.isIso_of_reflects_iso

#print CategoryTheory.of_full_and_faithful /-
instance (priority := 100) of_full_and_faithful (F : C ⥤ D) [Full F] [Faithful F] :
    ReflectsIsomorphisms F
    where reflects X Y f i :=
    ⟨⟨F.preimage (inv (F.map f)), ⟨F.map_injective (by simp), F.map_injective (by simp)⟩⟩⟩
#align category_theory.of_full_and_faithful CategoryTheory.of_full_and_faithful
-/

instance (F : C ⥤ D) (G : D ⥤ E) [ReflectsIsomorphisms F] [ReflectsIsomorphisms G] :
    ReflectsIsomorphisms (F ⋙ G) :=
  ⟨fun _ _ f (hf : IsIso (G.map _)) => by
    skip
    haveI := is_iso_of_reflects_iso (F.map f) G
    exact is_iso_of_reflects_iso f F⟩

#print CategoryTheory.reflectsIsomorphisms_of_reflectsMonomorphisms_of_reflectsEpimorphisms /-
instance (priority := 100) reflectsIsomorphisms_of_reflectsMonomorphisms_of_reflectsEpimorphisms
    [Balanced C] (F : C ⥤ D) [ReflectsMonomorphisms F] [ReflectsEpimorphisms F] :
    ReflectsIsomorphisms F
    where reflects A B f hf := by
    skip
    haveI : epi f := epi_of_epi_map F inferInstance
    haveI : mono f := mono_of_mono_map F inferInstance
    exact is_iso_of_mono_of_epi f
#align category_theory.reflects_isomorphisms_of_reflects_monomorphisms_of_reflects_epimorphisms CategoryTheory.reflectsIsomorphisms_of_reflectsMonomorphisms_of_reflectsEpimorphisms
-/

end ReflectsIso

end CategoryTheory


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

! This file was ported from Lean 3 source module algebra.group_with_zero.defs
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Group.Defs
import Mathbin.Logic.Nontrivial
import Mathbin.Algebra.NeZero

/-!
# Typeclasses for groups with an adjoined zero element

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

This file provides just the typeclass definitions, and the projection lemmas that expose their
members.

## Main definitions

* `group_with_zero`
* `comm_group_with_zero`
-/

universe u

-- We have to fix the universe of `G₀` here, since the default argument to
-- `group_with_zero.div'` cannot contain a universe metavariable.
variable {G₀ : Type u} {M₀ M₀' G₀' : Type _}

#print MulZeroClass /-
/-- Typeclass for expressing that a type `M₀` with multiplication and a zero satisfies
`0 * a = 0` and `a * 0 = 0` for all `a : M₀`. -/
@[protect_proj]
class MulZeroClass (M₀ : Type _) extends Mul M₀, Zero M₀ where
  zero_mul : ∀ a : M₀, 0 * a = 0
  mul_zero : ∀ a : M₀, a * 0 = 0
#align mul_zero_class MulZeroClass
-/

section MulZeroClass

variable [MulZeroClass M₀] {a b : M₀}

/- warning: zero_mul -> MulZeroClass.zero_mul is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))) a) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))) a) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))
Case conversion may be inaccurate. Consider using '#align zero_mul MulZeroClass.zero_mulₓ'. -/
@[ematch, simp]
theorem MulZeroClass.zero_mul (a : M₀) : 0 * a = 0 :=
  MulZeroClass.zero_mul a
#align zero_mul MulZeroClass.zero_mul

/- warning: mul_zero -> MulZeroClass.mul_zero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))
Case conversion may be inaccurate. Consider using '#align mul_zero MulZeroClass.mul_zeroₓ'. -/
@[ematch, simp]
theorem MulZeroClass.mul_zero (a : M₀) : a * 0 = 0 :=
  MulZeroClass.mul_zero a
#align mul_zero MulZeroClass.mul_zero

end MulZeroClass

#print IsLeftCancelMulZero /-
/-- A mixin for left cancellative multiplication by nonzero elements. -/
@[protect_proj]
class IsLeftCancelMulZero (M₀ : Type u) [Mul M₀] [Zero M₀] : Prop where
  mul_left_cancel_of_ne_zero : ∀ {a b c : M₀}, a ≠ 0 → a * b = a * c → b = c
#align is_left_cancel_mul_zero IsLeftCancelMulZero
-/

section IsLeftCancelMulZero

variable [Mul M₀] [Zero M₀] [IsLeftCancelMulZero M₀] {a b c : M₀}

#print mul_left_cancel₀ /-
theorem mul_left_cancel₀ (ha : a ≠ 0) (h : a * b = a * c) : b = c :=
  IsLeftCancelMulZero.mul_left_cancel_of_ne_zero ha h
#align mul_left_cancel₀ mul_left_cancel₀
-/

#print mul_right_injective₀ /-
theorem mul_right_injective₀ (ha : a ≠ 0) : Function.Injective ((· * ·) a) := fun b c =>
  mul_left_cancel₀ ha
#align mul_right_injective₀ mul_right_injective₀
-/

end IsLeftCancelMulZero

#print IsRightCancelMulZero /-
/-- A mixin for right cancellative multiplication by nonzero elements. -/
@[protect_proj]
class IsRightCancelMulZero (M₀ : Type u) [Mul M₀] [Zero M₀] : Prop where
  mul_right_cancel_of_ne_zero : ∀ {a b c : M₀}, b ≠ 0 → a * b = c * b → a = c
#align is_right_cancel_mul_zero IsRightCancelMulZero
-/

section IsRightCancelMulZero

variable [Mul M₀] [Zero M₀] [IsRightCancelMulZero M₀] {a b c : M₀}

#print mul_right_cancel₀ /-
theorem mul_right_cancel₀ (hb : b ≠ 0) (h : a * b = c * b) : a = c :=
  IsRightCancelMulZero.mul_right_cancel_of_ne_zero hb h
#align mul_right_cancel₀ mul_right_cancel₀
-/

#print mul_left_injective₀ /-
theorem mul_left_injective₀ (hb : b ≠ 0) : Function.Injective fun a => a * b := fun a c =>
  mul_right_cancel₀ hb
#align mul_left_injective₀ mul_left_injective₀
-/

end IsRightCancelMulZero

#print IsCancelMulZero /-
/-- A mixin for cancellative multiplication by nonzero elements. -/
@[protect_proj]
class IsCancelMulZero (M₀ : Type u) [Mul M₀] [Zero M₀] extends IsLeftCancelMulZero M₀,
  IsRightCancelMulZero M₀ : Prop
#align is_cancel_mul_zero IsCancelMulZero
-/

section CommSemigroupWithZero

variable [CommSemigroup M₀] [Zero M₀]

/- warning: is_left_cancel_mul_zero.to_is_right_cancel_mul_zero -> IsLeftCancelMulZero.to_isRightCancelMulZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsLeftCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsRightCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsLeftCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsRightCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
Case conversion may be inaccurate. Consider using '#align is_left_cancel_mul_zero.to_is_right_cancel_mul_zero IsLeftCancelMulZero.to_isRightCancelMulZeroₓ'. -/
theorem IsLeftCancelMulZero.to_isRightCancelMulZero [IsLeftCancelMulZero M₀] :
    IsRightCancelMulZero M₀ :=
  ⟨fun a b c ha h => mul_left_cancel₀ ha <| (mul_comm _ _).trans <| h.trans (mul_comm _ _)⟩
#align is_left_cancel_mul_zero.to_is_right_cancel_mul_zero IsLeftCancelMulZero.to_isRightCancelMulZero

/- warning: is_right_cancel_mul_zero.to_is_left_cancel_mul_zero -> IsRightCancelMulZero.to_isLeftCancelMulZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsRightCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsLeftCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsRightCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsLeftCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
Case conversion may be inaccurate. Consider using '#align is_right_cancel_mul_zero.to_is_left_cancel_mul_zero IsRightCancelMulZero.to_isLeftCancelMulZeroₓ'. -/
theorem IsRightCancelMulZero.to_isLeftCancelMulZero [IsRightCancelMulZero M₀] :
    IsLeftCancelMulZero M₀ :=
  ⟨fun a b c ha h => mul_right_cancel₀ ha <| (mul_comm _ _).trans <| h.trans (mul_comm _ _)⟩
#align is_right_cancel_mul_zero.to_is_left_cancel_mul_zero IsRightCancelMulZero.to_isLeftCancelMulZero

/- warning: is_left_cancel_mul_zero.to_is_cancel_mul_zero -> IsLeftCancelMulZero.to_isCancelMulZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsLeftCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsLeftCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
Case conversion may be inaccurate. Consider using '#align is_left_cancel_mul_zero.to_is_cancel_mul_zero IsLeftCancelMulZero.to_isCancelMulZeroₓ'. -/
theorem IsLeftCancelMulZero.to_isCancelMulZero [IsLeftCancelMulZero M₀] : IsCancelMulZero M₀ :=
  { ‹IsLeftCancelMulZero M₀›, IsLeftCancelMulZero.to_isRightCancelMulZero with }
#align is_left_cancel_mul_zero.to_is_cancel_mul_zero IsLeftCancelMulZero.to_isCancelMulZero

/- warning: is_right_cancel_mul_zero.to_is_cancel_mul_zero -> IsRightCancelMulZero.to_isCancelMulZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsRightCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsCancelMulZero.{u1} M₀ (Semigroup.toHasMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : CommSemigroup.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : IsRightCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2], IsCancelMulZero.{u1} M₀ (Semigroup.toMul.{u1} M₀ (CommSemigroup.toSemigroup.{u1} M₀ _inst_1)) _inst_2
Case conversion may be inaccurate. Consider using '#align is_right_cancel_mul_zero.to_is_cancel_mul_zero IsRightCancelMulZero.to_isCancelMulZeroₓ'. -/
theorem IsRightCancelMulZero.to_isCancelMulZero [IsRightCancelMulZero M₀] : IsCancelMulZero M₀ :=
  { ‹IsRightCancelMulZero M₀›, IsRightCancelMulZero.to_isLeftCancelMulZero with }
#align is_right_cancel_mul_zero.to_is_cancel_mul_zero IsRightCancelMulZero.to_isCancelMulZero

end CommSemigroupWithZero

#print NoZeroDivisors /-
/-- Predicate typeclass for expressing that `a * b = 0` implies `a = 0` or `b = 0`
for all `a` and `b` of type `G₀`. -/
class NoZeroDivisors (M₀ : Type _) [Mul M₀] [Zero M₀] : Prop where
  eq_zero_or_eq_zero_of_mul_eq_zero : ∀ {a b : M₀}, a * b = 0 → a = 0 ∨ b = 0
#align no_zero_divisors NoZeroDivisors
-/

export NoZeroDivisors (eq_zero_or_eq_zero_of_mul_eq_zero)

#print SemigroupWithZero /-
/-- A type `S₀` is a "semigroup with zero” if it is a semigroup with zero element, and `0` is left
and right absorbing. -/
@[protect_proj]
class SemigroupWithZero (S₀ : Type _) extends Semigroup S₀, MulZeroClass S₀
#align semigroup_with_zero SemigroupWithZero
-/

#print MulZeroOneClass /-
/- By defining this _after_ `semigroup_with_zero`, we ensure that searches for `mul_zero_class` find
this class first. -/
/-- A typeclass for non-associative monoids with zero elements. -/
@[protect_proj]
class MulZeroOneClass (M₀ : Type _) extends MulOneClass M₀, MulZeroClass M₀
#align mul_zero_one_class MulZeroOneClass
-/

#print MonoidWithZero /-
/-- A type `M₀` is a “monoid with zero” if it is a monoid with zero element, and `0` is left
and right absorbing. -/
@[protect_proj]
class MonoidWithZero (M₀ : Type _) extends Monoid M₀, MulZeroOneClass M₀
#align monoid_with_zero MonoidWithZero
-/

#print MonoidWithZero.toSemigroupWithZero /-
-- see Note [lower instance priority]
instance (priority := 100) MonoidWithZero.toSemigroupWithZero (M₀ : Type _) [MonoidWithZero M₀] :
    SemigroupWithZero M₀ :=
  { ‹MonoidWithZero M₀› with }
#align monoid_with_zero.to_semigroup_with_zero MonoidWithZero.toSemigroupWithZero
-/

#print CancelMonoidWithZero /-
/-- A type `M` is a `cancel_monoid_with_zero` if it is a monoid with zero element, `0` is left
and right absorbing, and left/right multiplication by a non-zero element is injective. -/
@[protect_proj]
class CancelMonoidWithZero (M₀ : Type _) extends MonoidWithZero M₀ where
  mul_left_cancel_of_ne_zero : ∀ {a b c : M₀}, a ≠ 0 → a * b = a * c → b = c
  mul_right_cancel_of_ne_zero : ∀ {a b c : M₀}, b ≠ 0 → a * b = c * b → a = c
#align cancel_monoid_with_zero CancelMonoidWithZero
-/

/-- A `cancel_monoid_with_zero` satisfies `is_cancel_mul_zero`. -/
instance (priority := 100) CancelMonoidWithZero.to_isCancelMulZero [CancelMonoidWithZero M₀] :
    IsCancelMulZero M₀ :=
  { ‹CancelMonoidWithZero M₀› with }
#align cancel_monoid_with_zero.to_is_cancel_mul_zero CancelMonoidWithZero.to_isCancelMulZero

#print CommMonoidWithZero /-
/-- A type `M` is a commutative “monoid with zero” if it is a commutative monoid with zero
element, and `0` is left and right absorbing. -/
@[protect_proj]
class CommMonoidWithZero (M₀ : Type _) extends CommMonoid M₀, MonoidWithZero M₀
#align comm_monoid_with_zero CommMonoidWithZero
-/

#print CancelCommMonoidWithZero /-
/-- A type `M` is a `cancel_comm_monoid_with_zero` if it is a commutative monoid with zero element,
 `0` is left and right absorbing,
  and left/right multiplication by a non-zero element is injective. -/
@[protect_proj]
class CancelCommMonoidWithZero (M₀ : Type _) extends CommMonoidWithZero M₀ where
  mul_left_cancel_of_ne_zero : ∀ {a b c : M₀}, a ≠ 0 → a * b = a * c → b = c
#align cancel_comm_monoid_with_zero CancelCommMonoidWithZero
-/

#print CancelCommMonoidWithZero.toCancelMonoidWithZero /-
instance (priority := 100) CancelCommMonoidWithZero.toCancelMonoidWithZero
    [h : CancelCommMonoidWithZero M₀] : CancelMonoidWithZero M₀ :=
  { h, @IsLeftCancelMulZero.to_isRightCancelMulZero M₀ _ _ { h with } with }
#align cancel_comm_monoid_with_zero.to_cancel_monoid_with_zero CancelCommMonoidWithZero.toCancelMonoidWithZero
-/

#print GroupWithZero /-
/-- A type `G₀` is a “group with zero” if it is a monoid with zero element (distinct from `1`)
such that every nonzero element is invertible.
The type is required to come with an “inverse” function, and the inverse of `0` must be `0`.

Examples include division rings and the ordered monoids that are the
target of valuations in general valuation theory.-/
@[protect_proj]
class GroupWithZero (G₀ : Type u) extends MonoidWithZero G₀, DivInvMonoid G₀, Nontrivial G₀ where
  inv_zero : (0 : G₀)⁻¹ = 0
  mul_inv_cancel : ∀ a : G₀, a ≠ 0 → a * a⁻¹ = 1
#align group_with_zero GroupWithZero
-/

section GroupWithZero

variable [GroupWithZero G₀]

@[simp]
theorem inv_zero : (0 : G₀)⁻¹ = 0 :=
  GroupWithZero.inv_zero
#align inv_zero inv_zero

/- warning: mul_inv_cancel -> mul_inv_cancel is a dubious translation:
lean 3 declaration is
  forall {G₀ : Type.{u1}} [_inst_1 : GroupWithZero.{u1} G₀] {a : G₀}, (Ne.{succ u1} G₀ a (OfNat.ofNat.{u1} G₀ 0 (OfNat.mk.{u1} G₀ 0 (Zero.zero.{u1} G₀ (MulZeroClass.toHasZero.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))))) -> (Eq.{succ u1} G₀ (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toHasMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) a (Inv.inv.{u1} G₀ (DivInvMonoid.toHasInv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1)) a)) (OfNat.ofNat.{u1} G₀ 1 (OfNat.mk.{u1} G₀ 1 (One.one.{u1} G₀ (MulOneClass.toHasOne.{u1} G₀ (MulZeroOneClass.toMulOneClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))))))
but is expected to have type
  forall {G₀ : Type.{u1}} [_inst_1 : GroupWithZero.{u1} G₀] {a : G₀}, (Ne.{succ u1} G₀ a (OfNat.ofNat.{u1} G₀ 0 (Zero.toOfNat0.{u1} G₀ (MonoidWithZero.toZero.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) -> (Eq.{succ u1} G₀ (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) a (Inv.inv.{u1} G₀ (GroupWithZero.toInv.{u1} G₀ _inst_1) a)) (OfNat.ofNat.{u1} G₀ 1 (One.toOfNat1.{u1} G₀ (Monoid.toOne.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))))
Case conversion may be inaccurate. Consider using '#align mul_inv_cancel mul_inv_cancelₓ'. -/
@[simp]
theorem mul_inv_cancel {a : G₀} (h : a ≠ 0) : a * a⁻¹ = 1 :=
  GroupWithZero.mul_inv_cancel a h
#align mul_inv_cancel mul_inv_cancel

end GroupWithZero

#print CommGroupWithZero /-
/-- A type `G₀` is a commutative “group with zero”
if it is a commutative monoid with zero element (distinct from `1`)
such that every nonzero element is invertible.
The type is required to come with an “inverse” function, and the inverse of `0` must be `0`. -/
@[protect_proj]
class CommGroupWithZero (G₀ : Type _) extends CommMonoidWithZero G₀, GroupWithZero G₀
#align comm_group_with_zero CommGroupWithZero
-/

section NeZero

attribute [field_simps] two_ne_zero three_ne_zero four_ne_zero

variable [MulZeroOneClass M₀] [Nontrivial M₀] {a b : M₀}

variable (M₀)

/- warning: ne_zero.one -> NeZero.one is a dubious translation:
lean 3 declaration is
  forall (M₀ : Type.{u1}) [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Nontrivial.{u1} M₀], NeZero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1)) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1)))))
but is expected to have type
  forall (M₀ : Type.{u1}) [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Nontrivial.{u1} M₀], NeZero.{u1} M₀ (MulZeroOneClass.toZero.{u1} M₀ _inst_1) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (MulOneClass.toOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align ne_zero.one NeZero.oneₓ'. -/
/-- In a nontrivial monoid with zero, zero and one are different. -/
instance NeZero.one : NeZero (1 : M₀) :=
  ⟨by
    intro h
    rcases exists_pair_ne M₀ with ⟨x, y, hx⟩
    apply hx
    calc
      x = 1 * x := by rw [one_mul]
      _ = 0 := by rw [h, MulZeroClass.zero_mul]
      _ = 1 * y := by rw [h, MulZeroClass.zero_mul]
      _ = y := by rw [one_mul]
      ⟩
#align ne_zero.one NeZero.one

variable {M₀}

/- warning: pullback_nonzero -> pullback_nonzero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Nontrivial.{u1} M₀] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀' -> M₀), (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_3)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1)))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_4)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1)))))) -> (Nontrivial.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Nontrivial.{u1} M₀] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀' -> M₀), (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_3))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroOneClass.toZero.{u1} M₀ _inst_1)))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_4))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (MulOneClass.toOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1))))) -> (Nontrivial.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align pullback_nonzero pullback_nonzeroₓ'. -/
/-- Pullback a `nontrivial` instance along a function sending `0` to `0` and `1` to `1`. -/
theorem pullback_nonzero [Zero M₀'] [One M₀'] (f : M₀' → M₀) (zero : f 0 = 0) (one : f 1 = 1) :
    Nontrivial M₀' :=
  ⟨⟨0, 1,
      mt (congr_arg f) <| by
        rw [zero, one]
        exact zero_ne_one⟩⟩
#align pullback_nonzero pullback_nonzero

end NeZero

section MulZeroClass

variable [MulZeroClass M₀]

/- warning: mul_eq_zero_of_left -> mul_eq_zero_of_left is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] {a : M₀}, (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) -> (forall (b : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] {a : M₀}, (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) -> (forall (b : M₀), Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_eq_zero_of_left mul_eq_zero_of_leftₓ'. -/
theorem mul_eq_zero_of_left {a : M₀} (h : a = 0) (b : M₀) : a * b = 0 :=
  h.symm ▸ MulZeroClass.zero_mul b
#align mul_eq_zero_of_left mul_eq_zero_of_left

/- warning: mul_eq_zero_of_right -> mul_eq_zero_of_right is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀) {b : M₀}, (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) -> (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] (a : M₀) {b : M₀}, (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) -> (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_eq_zero_of_right mul_eq_zero_of_rightₓ'. -/
theorem mul_eq_zero_of_right (a : M₀) {b : M₀} (h : b = 0) : a * b = 0 :=
  h.symm ▸ MulZeroClass.mul_zero a
#align mul_eq_zero_of_right mul_eq_zero_of_right

variable [NoZeroDivisors M₀] {a b : M₀}

/- warning: mul_eq_zero -> mul_eq_zero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Or (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Or (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))))
Case conversion may be inaccurate. Consider using '#align mul_eq_zero mul_eq_zeroₓ'. -/
/-- If `α` has no zero divisors, then the product of two elements equals zero iff one of them
equals zero. -/
@[simp]
theorem mul_eq_zero : a * b = 0 ↔ a = 0 ∨ b = 0 :=
  ⟨eq_zero_or_eq_zero_of_mul_eq_zero, fun o =>
    o.elim (fun h => mul_eq_zero_of_left h b) (mul_eq_zero_of_right a)⟩
#align mul_eq_zero mul_eq_zero

/- warning: zero_eq_mul -> zero_eq_mul is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b)) (Or (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b)) (Or (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Eq.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))))
Case conversion may be inaccurate. Consider using '#align zero_eq_mul zero_eq_mulₓ'. -/
/-- If `α` has no zero divisors, then the product of two elements equals zero iff one of them
equals zero. -/
@[simp]
theorem zero_eq_mul : 0 = a * b ↔ a = 0 ∨ b = 0 := by rw [eq_comm, mul_eq_zero]
#align zero_eq_mul zero_eq_mul

/- warning: mul_ne_zero_iff -> mul_ne_zero_iff is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (And (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Ne.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (And (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Ne.{succ u1} M₀ b (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))))
Case conversion may be inaccurate. Consider using '#align mul_ne_zero_iff mul_ne_zero_iffₓ'. -/
/-- If `α` has no zero divisors, then the product of two elements is nonzero iff both of them
are nonzero. -/
theorem mul_ne_zero_iff : a * b ≠ 0 ↔ a ≠ 0 ∧ b ≠ 0 :=
  mul_eq_zero.Not.trans not_or
#align mul_ne_zero_iff mul_ne_zero_iff

/- warning: mul_eq_zero_comm -> mul_eq_zero_comm is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) b a) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) b a) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_eq_zero_comm mul_eq_zero_commₓ'. -/
/-- If `α` has no zero divisors, then for elements `a, b : α`, `a * b` equals zero iff so is
`b * a`. -/
theorem mul_eq_zero_comm : a * b = 0 ↔ b * a = 0 :=
  mul_eq_zero.trans <| (or_comm' _ _).trans mul_eq_zero.symm
#align mul_eq_zero_comm mul_eq_zero_comm

/- warning: mul_ne_zero_comm -> mul_ne_zero_comm is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) b a) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀} {b : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) b a) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_ne_zero_comm mul_ne_zero_commₓ'. -/
/-- If `α` has no zero divisors, then for elements `a, b : α`, `a * b` is nonzero iff so is
`b * a`. -/
theorem mul_ne_zero_comm : a * b ≠ 0 ↔ b * a ≠ 0 :=
  mul_eq_zero_comm.Not
#align mul_ne_zero_comm mul_ne_zero_comm

/- warning: mul_self_eq_zero -> mul_self_eq_zero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a a) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Eq.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a a) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_self_eq_zero mul_self_eq_zeroₓ'. -/
theorem mul_self_eq_zero : a * a = 0 ↔ a = 0 := by simp
#align mul_self_eq_zero mul_self_eq_zero

/- warning: zero_eq_mul_self -> zero_eq_mul_self is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Eq.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a a)) (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Eq.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a a)) (Eq.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align zero_eq_mul_self zero_eq_mul_selfₓ'. -/
theorem zero_eq_mul_self : 0 = a * a ↔ a = 0 := by simp
#align zero_eq_mul_self zero_eq_mul_self

/- warning: mul_self_ne_zero -> mul_self_ne_zero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a a) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Ne.{succ u1} M₀ (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a a) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align mul_self_ne_zero mul_self_ne_zeroₓ'. -/
theorem mul_self_ne_zero : a * a ≠ 0 ↔ a ≠ 0 :=
  mul_self_eq_zero.Not
#align mul_self_ne_zero mul_self_ne_zero

/- warning: zero_ne_mul_self -> zero_ne_mul_self is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1) (MulZeroClass.toHasZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Ne.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a a)) (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1)))))
but is expected to have type
  forall {M₀ : Type.{u1}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : NoZeroDivisors.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1) (MulZeroClass.toZero.{u1} M₀ _inst_1)] {a : M₀}, Iff (Ne.{succ u1} M₀ (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a a)) (Ne.{succ u1} M₀ a (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1))))
Case conversion may be inaccurate. Consider using '#align zero_ne_mul_self zero_ne_mul_selfₓ'. -/
theorem zero_ne_mul_self : 0 ≠ a * a ↔ a ≠ 0 :=
  zero_eq_mul_self.Not
#align zero_ne_mul_self zero_ne_mul_self

end MulZeroClass


syntax:65 (name := myAdd1) term "+++" term:65 : term
syntax:65 (name := myAdd2) term "+++" term:65 : term

macro_rules (kind := myAdd1)
| `($a +++ $b) => `(Nat.add $a $b)

macro_rules (kind := myAdd2)
| `($a +++ $b) => `(Append.append $a $b)

#check (1:Nat) +++ 3

theorem tst1 : ((1:Nat) +++ 3) = 1 + 3 :=
rfl

#check fun (x : Nat) => if x +++ 3 = x then x else x + 1

#check [1, 2] +++ [3, 4]

theorem tst2 : ([1, 2] +++ [3, 4]) = [1, 2] ++ [3, 4] :=
rfl


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.homology.additive
import tactic.abel

/-!
# Chain homotopies

We define chain homotopies, and prove that homotopic chain maps induce the same map on homology.
-/

universes v u

open_locale classical
noncomputable theory

open category_theory category_theory.limits homological_complex

variables {ι : Type*}
variables {V : Type u} [category.{v} V] [preadditive V]

variables {c : complex_shape ι} {C D E : homological_complex V c}
variables (f g : C ⟶ D) (h k : D ⟶ E) (i : ι)

section

/-- The composition of `C.d i i' ≫ f i' i` if there is some `i'` coming after `i`,
and `0` otherwise. -/
def d_next (i : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X i ⟶ D.X i) :=
add_monoid_hom.mk' (λ f, match c.next i with
| none := 0
| some ⟨i',w⟩ := C.d i i' ≫ f i' i
end)
begin
  intros f g,
  rcases c.next i with _|⟨i',w⟩,
  exact (zero_add _).symm,
  exact preadditive.comp_add _ _ _ _ _ _,
end

/-- `f i' i` if `i'` comes after `i`, and 0 if there's no such `i'`.
Hopefully there won't be much need for this, except in `d_next_eq_d_from_from_next`
to see that `d_next` factors through `C.d_from i`. -/
def from_next [has_zero_object V] (i : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X_next i ⟶ D.X i) :=
add_monoid_hom.mk' (λ f, match c.next i with
| none := 0
| some ⟨i',w⟩ := (C.X_next_iso w).hom ≫ f i' i
end)
begin
  intros f g,
  rcases c.next i with _|⟨i',w⟩,
  exact (zero_add _).symm,
  exact preadditive.comp_add _ _ _ _ _ _,
end

lemma d_next_eq_d_from_from_next [has_zero_object V] (f : Π i j, C.X i ⟶ D.X j) (i : ι) :
  d_next i f = C.d_from i ≫ from_next i f :=
begin
  dsimp [d_next, from_next],
  rcases c.next i with ⟨⟩|⟨⟨i', w⟩⟩;
  { dsimp [d_next, from_next], simp },
end

lemma d_next_eq (f : Π i j, C.X i ⟶ D.X j) {i i' : ι} (w : c.rel i i') :
  d_next i f = C.d i i' ≫ f i' i :=
begin
  dsimp [d_next],
  rw c.next_eq_some w,
  refl,
end

@[simp] lemma d_next_comp_left (f : C ⟶ D) (g : Π i j, D.X i ⟶ E.X j) (i : ι) :
  d_next i (λ i j, f.f i ≫ g i j) = f.f i ≫ d_next i g :=
begin
  dsimp [d_next],
  rcases c.next i with _|⟨i',w⟩,
  { exact comp_zero.symm, },
  { dsimp [d_next],
    simp, },
end

@[simp] lemma d_next_comp_right (f : Π i j, C.X i ⟶ D.X j) (g : D ⟶ E) (i : ι) :
  d_next i (λ i j, f i j ≫ g.f j) = d_next i f ≫ g.f i :=
begin
  dsimp [d_next],
  rcases c.next i with _|⟨i',w⟩,
  { exact zero_comp.symm, },
  { dsimp [d_next],
    simp, },
end

/-- The composition of `f j j' ≫ D.d j' j` if there is some `j'` coming before `j`,
and `0` otherwise. -/
def prev_d (j : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X j ⟶ D.X j) :=
add_monoid_hom.mk' (λ f, match c.prev j with
| none := 0
| some ⟨j',w⟩ := f j j' ≫ D.d j' j
end)
begin
  intros f g,
  rcases c.prev j with _|⟨j',w⟩,
  exact (zero_add _).symm,
  exact preadditive.add_comp _ _ _ _ _ _,
end

/-- `f j j'` if `j'` comes after `j`, and 0 if there's no such `j'`.
Hopefully there won't be much need for this, except in `d_next_eq_d_from_from_next`
to see that `d_next` factors through `C.d_from i`. -/
def to_prev [has_zero_object V] (j : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X j ⟶ D.X_prev j) :=
add_monoid_hom.mk' (λ f, match c.prev j with
| none := 0
| some ⟨j',w⟩ := f j j' ≫ (D.X_prev_iso w).inv
end)
begin
  intros f g,
  rcases c.prev j with _|⟨j',w⟩,
  exact (zero_add _).symm,
  exact preadditive.add_comp _ _ _ _ _ _,
end

lemma prev_d_eq_to_prev_d_to [has_zero_object V] (f : Π i j, C.X i ⟶ D.X j) (j : ι) :
  prev_d j f = to_prev j f ≫ D.d_to j :=
begin
  dsimp [prev_d, to_prev],
  rcases c.prev j with ⟨⟩|⟨⟨j', w⟩⟩;
  { dsimp [prev_d, to_prev], simp },
end

lemma prev_d_eq (f : Π i j, C.X i ⟶ D.X j) {j j' : ι} (w : c.rel j' j) :
  prev_d j f = f j j' ≫ D.d j' j :=
begin
  dsimp [prev_d],
  rw c.prev_eq_some w,
  refl,
end

@[simp] lemma prev_d_comp_left (f : C ⟶ D) (g : Π i j, D.X i ⟶ E.X j) (j : ι) :
  prev_d j (λ i j, f.f i ≫ g i j) = f.f j ≫ prev_d j g :=
begin
  dsimp [prev_d],
  rcases c.prev j with _|⟨j',w⟩,
  { exact comp_zero.symm, },
  { dsimp [prev_d, hom.prev],
    simp, },
end

@[simp] lemma to_prev'_comp_right (f : Π i j, C.X i ⟶ D.X j) (g : D ⟶ E) (j : ι) :
  prev_d j (λ i j, f i j ≫ g.f j) = prev_d j f ≫ g.f j :=
begin
  dsimp [prev_d],
  rcases c.prev j with _|⟨j',w⟩,
  { exact zero_comp.symm, },
  { dsimp [prev_d],
    simp, },
end

lemma d_next_nat (C D : chain_complex V ℕ) (i : ℕ) (f : Π i j, C.X i ⟶ D.X j) :
  d_next i f = C.d i (i-1) ≫ f (i-1) i :=
begin
  cases i,
  { dsimp [d_next],
    rcases (complex_shape.down ℕ).next 0 with _|⟨j,hj⟩;
    dsimp [d_next],
    { rw [C.shape, zero_comp], dsimp, dec_trivial },
    { dsimp at hj, exact (nat.succ_ne_zero _ hj).elim } },
  rw d_next_eq, dsimp, refl
end

lemma prev_d_nat (C D : cochain_complex V ℕ) (i : ℕ) (f : Π i j, C.X i ⟶ D.X j) :
  prev_d i f = f i (i-1) ≫ D.d (i-1) i :=
begin
  cases i,
  { dsimp [prev_d],
    rcases (complex_shape.up ℕ).prev 0 with _|⟨j,hj⟩;
    dsimp [prev_d],
    { rw [D.shape, comp_zero], dsimp, dec_trivial },
    { dsimp at hj, exact (nat.succ_ne_zero _ hj).elim } },
  rw prev_d_eq, dsimp, refl
end

/--
A homotopy `h` between chain maps `f` and `g` consists of components `h i j : C.X i ⟶ D.X j`
which are zero unless `c.rel j i`, satisfying the homotopy condition.
-/
@[ext, nolint has_inhabited_instance]
structure homotopy (f g : C ⟶ D) :=
(hom : Π i j, C.X i ⟶ D.X j)
(zero' : ∀ i j, ¬ c.rel j i → hom i j = 0 . obviously)
(comm : ∀ i, f.f i = d_next i hom + prev_d i hom + g.f i . obviously')

variables {f g}
namespace homotopy

restate_axiom homotopy.zero'

/--
`f` is homotopic to `g` iff `f - g` is homotopic to `0`.
-/
def equiv_sub_zero : homotopy f g ≃ homotopy (f - g) 0 :=
{ to_fun := λ h,
  { hom := λ i j, h.hom i j,
    zero' := λ i j w, h.zero _ _ w,
    comm := λ i, by simp [h.comm] },
  inv_fun := λ h,
  { hom := λ i j, h.hom i j,
    zero' := λ i j w, h.zero _ _ w,
    comm := λ i, by simpa [sub_eq_iff_eq_add] using h.comm i },
  left_inv := by tidy,
  right_inv := by tidy, }

/-- Equal chain maps are homotopic. -/
@[simps]
def of_eq (h : f = g) : homotopy f g :=
{ hom := 0,
  zero' := λ _ _ _, rfl,
  comm := λ _, by simp only [add_monoid_hom.map_zero, zero_add, h] }

/-- Every chain map is homotopic to itself. -/
@[simps, refl]
def refl (f : C ⟶ D) : homotopy f f :=
of_eq (rfl : f = f)

/-- `f` is homotopic to `g` iff `g` is homotopic to `f`. -/
@[simps, symm]
def symm {f g : C ⟶ D} (h : homotopy f g) : homotopy g f :=
{ hom := -h.hom,
  zero' := λ i j w, by rw [pi.neg_apply, pi.neg_apply, h.zero i j w, neg_zero],
  comm := λ i, by rw [add_monoid_hom.map_neg, add_monoid_hom.map_neg, h.comm, ← neg_add,
      ← add_assoc, neg_add_self, zero_add] }

/-- homotopy is a transitive relation. -/
@[simps, trans]
def trans {e f g : C ⟶ D} (h : homotopy e f) (k : homotopy f g) : homotopy e g :=
{ hom := h.hom + k.hom,
  zero' := λ i j w, by rw [pi.add_apply, pi.add_apply, h.zero i j w, k.zero i j w, zero_add],
  comm := λ i, by { rw [add_monoid_hom.map_add, add_monoid_hom.map_add, h.comm, k.comm], abel }, }

/-- homotopy is closed under composition (on the right) -/
@[simps]
def comp_right {e f : C ⟶ D} (h : homotopy e f) (g : D ⟶ E) : homotopy (e ≫ g) (f ≫ g) :=
{ hom := λ i j, h.hom i j ≫ g.f j,
  zero' := λ i j w, by rw [h.zero i j w, zero_comp],
  comm := λ i, by simp only [h.comm i, d_next_comp_right, preadditive.add_comp,
    to_prev'_comp_right, comp_f], }

/-- homotopy is closed under composition (on the left) -/
@[simps]
def comp_left {f g : D ⟶ E} (h : homotopy f g) (e : C ⟶ D) : homotopy (e ≫ f) (e ≫ g) :=
{ hom := λ i j, e.f i ≫ h.hom i j,
  zero' := λ i j w, by rw [h.zero i j w, comp_zero],
  comm := λ i, by simp only [h.comm i, d_next_comp_left, preadditive.comp_add,
    prev_d_comp_left, comp_f], }

/-- homotopy is closed under composition -/
@[simps]
def comp {C₁ C₂ C₃ : homological_complex V c} {f₁ g₁ : C₁ ⟶ C₂} {f₂ g₂ : C₂ ⟶ C₃}
  (h₁ : homotopy f₁ g₁) (h₂ : homotopy f₂ g₂) : homotopy (f₁ ≫ f₂) (g₁ ≫ g₂) :=
(h₁.comp_right _).trans (h₂.comp_left _)

/-- a variant of `homotopy.comp_right` useful for dealing with homotopy equivalences. -/
@[simps]
def comp_right_id {f : C ⟶ C} (h : homotopy f (𝟙 C)) (g : C ⟶ D) : homotopy (f ≫ g) g :=
(h.comp_right g).trans (of_eq $ category.id_comp _)

/-- a variant of `homotopy.comp_left` useful for dealing with homotopy equivalences. -/
@[simps]
def comp_left_id {f : D ⟶ D} (h : homotopy f (𝟙 D)) (g : C ⟶ D) : homotopy (g ≫ f) g :=
(h.comp_left g).trans (of_eq $ category.comp_id _)

/-!
`homotopy.mk_inductive` allows us to build a homotopy inductively,
so that as we construct each component, we have available the previous two components,
and the fact that they satisfy the homotopy condition.

To simplify the situation, we only construct homotopies of the form `homotopy e 0`.
`homotopy.equiv_sub_zero` can provide the general case.

Notice however, that this construction does not have particularly good definitional properties:
we have to insert `eq_to_hom` in several places.
Hopefully this is okay in most applications, where we only need to have the existence of some
homotopy.
-/
section mk_inductive

variables {P Q : chain_complex V ℕ}

@[simp] lemma prev_d_chain_complex (f : Π i j, P.X i ⟶ Q.X j) (j : ℕ) :
  prev_d j f = f j (j+1) ≫ Q.d _ _ :=
begin
  dsimp [prev_d],
  simp only [chain_complex.prev],
  refl,
end

@[simp] lemma d_next_succ_chain_complex (f : Π i j, P.X i ⟶ Q.X j) (i : ℕ) :
  d_next (i+1) f = P.d _ _ ≫ f i (i+1) :=
begin
  dsimp [d_next],
  simp only [chain_complex.next_nat_succ],
  refl,
end

@[simp] lemma d_next_zero_chain_complex (f : Π i j, P.X i ⟶ Q.X j) :
  d_next 0 f = 0 :=
begin
  dsimp [d_next],
  simp only [chain_complex.next_nat_zero],
  refl,
end

variables (e : P ⟶ Q)
  (zero : P.X 0 ⟶ Q.X 1)
  (comm_zero : e.f 0 = zero ≫ Q.d 1 0)
  (one : P.X 1 ⟶ Q.X 2)
  (comm_one : e.f 1 = P.d 1 0 ≫ zero + one ≫ Q.d 2 1)
  (succ : ∀ (n : ℕ)
    (p : Σ' (f : P.X n ⟶ Q.X (n+1)) (f' : P.X (n+1) ⟶ Q.X (n+2)),
      e.f (n+1) = P.d (n+1) n ≫ f + f' ≫ Q.d (n+2) (n+1)),
    Σ' f'' : P.X (n+2) ⟶ Q.X (n+3), e.f (n+2) = P.d (n+2) (n+1) ≫ p.2.1 + f'' ≫ Q.d (n+3) (n+2))

include comm_one comm_zero

/--
An auxiliary construction for `mk_inductive`.

Here we build by induction a family of diagrams,
but don't require at the type level that these successive diagrams actually agree.
They do in fact agree, and we then capture that at the type level (i.e. by constructing a homotopy)
in `mk_inductive`.

At this stage, we don't check the homotopy condition in degree 0,
because it "falls off the end", and is easier to treat using `X_next` and `X_prev`,
which we do in `mk_inductive_aux₂`.
-/
@[simp, nolint unused_arguments]
def mk_inductive_aux₁ :
  Π n, Σ' (f : P.X n ⟶ Q.X (n+1)) (f' : P.X (n+1) ⟶ Q.X (n+2)),
    e.f (n+1) = P.d (n+1) n ≫ f + f' ≫ Q.d (n+2) (n+1)
| 0 := ⟨zero, one, comm_one⟩
| 1 := ⟨one, (succ 0 ⟨zero, one, comm_one⟩).1, (succ 0 ⟨zero, one, comm_one⟩).2⟩
| (n+2) :=
  ⟨(mk_inductive_aux₁ (n+1)).2.1,
    (succ (n+1) (mk_inductive_aux₁ (n+1))).1,
    (succ (n+1) (mk_inductive_aux₁ (n+1))).2⟩

section

variable [has_zero_object V]

/--
An auxiliary construction for `mk_inductive`.
-/
@[simp]
def mk_inductive_aux₂ :
  Π n, Σ' (f : P.X_next n ⟶ Q.X n) (f' : P.X n ⟶ Q.X_prev n), e.f n = P.d_from n ≫ f + f' ≫ Q.d_to n
| 0 := ⟨0, zero ≫ (Q.X_prev_iso rfl).inv, by simpa using comm_zero⟩
| (n+1) := let I := mk_inductive_aux₁ e zero comm_zero one comm_one succ n in
  ⟨(P.X_next_iso rfl).hom ≫ I.1, I.2.1 ≫ (Q.X_prev_iso rfl).inv, by simpa using I.2.2⟩

lemma mk_inductive_aux₃ (i : ℕ) :
  (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.1 ≫ (Q.X_prev_iso rfl).hom
    = (P.X_next_iso rfl).inv ≫ (mk_inductive_aux₂ e zero comm_zero one comm_one succ (i+1)).1 :=
by rcases i with (_|_|i); { dsimp, simp, }

/--
A constructor for a `homotopy e 0`, for `e` a chain map between `ℕ`-indexed chain complexes,
working by induction.

You need to provide the components of the homotopy in degrees 0 and 1,
show that these satisfy the homotopy condition,
and then give a construction of each component,
and the fact that it satisfies the homotopy condition,
using as an inductive hypothesis the data and homotopy condition for the previous two components.
-/
def mk_inductive : homotopy e 0 :=
{ hom := λ i j, if h : i + 1 = j then
    (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.1 ≫ (Q.X_prev_iso h).hom
  else
    0,
  zero' := λ i j w, by rwa dif_neg,
  comm := λ i, begin
    dsimp, simp only [add_zero],
    convert (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.2,
    { rcases i with (_|_|_|i),
      { dsimp,
        simp only [d_next_zero_chain_complex, d_from_eq_zero, limits.comp_zero], },
      all_goals
      { simp only [d_next_succ_chain_complex],
        dsimp,
        simp only [category.comp_id, category.assoc, iso.inv_hom_id, d_from_comp_X_next_iso_assoc,
          dite_eq_ite, if_true, eq_self_iff_true]}, },
    { cases i,
      all_goals
      { simp only [prev_d_chain_complex],
        dsimp,
        simp only [category.comp_id, category.assoc, iso.inv_hom_id, X_prev_iso_comp_d_to,
          dite_eq_ite, if_true, eq_self_iff_true], }, },
  end, }

end

end mk_inductive

end homotopy

/--
A homotopy equivalence between two chain complexes consists of a chain map each way,
and homotopies from the compositions to the identity chain maps.

Note that this contains data;
arguably it might be more useful for many applications if we truncated it to a Prop.
-/
structure homotopy_equiv (C D : homological_complex V c) :=
(hom : C ⟶ D)
(inv : D ⟶ C)
(homotopy_hom_inv_id : homotopy (hom ≫ inv) (𝟙 C))
(homotopy_inv_hom_id : homotopy (inv ≫ hom) (𝟙 D))

namespace homotopy_equiv

/-- Any complex is homotopy equivalent to itself. -/
@[refl] def refl (C : homological_complex V c) : homotopy_equiv C C :=
{ hom := 𝟙 C,
  inv := 𝟙 C,
  homotopy_hom_inv_id := by simp,
  homotopy_inv_hom_id := by simp, }

instance : inhabited (homotopy_equiv C C) := ⟨refl C⟩

/-- Being homotopy equivalent is a symmetric relation. -/
@[symm] def symm
  {C D : homological_complex V c} (f : homotopy_equiv C D) :
  homotopy_equiv D C :=
{ hom := f.inv,
  inv := f.hom,
  homotopy_hom_inv_id := f.homotopy_inv_hom_id,
  homotopy_inv_hom_id := f.homotopy_hom_inv_id, }

/-- Homotopy equivalence is a transitive relation. -/
@[trans] def trans
  {C D E : homological_complex V c} (f : homotopy_equiv C D) (g : homotopy_equiv D E) :
  homotopy_equiv C E :=
{ hom := f.hom ≫ g.hom,
  inv := g.inv ≫ f.inv,
  homotopy_hom_inv_id := by simpa using
    ((g.homotopy_hom_inv_id.comp_right_id f.inv).comp_left f.hom).trans f.homotopy_hom_inv_id,
  homotopy_inv_hom_id := by simpa using
    ((f.homotopy_inv_hom_id.comp_right_id g.hom).comp_left g.inv).trans g.homotopy_inv_hom_id, }

end homotopy_equiv

variables [has_equalizers V] [has_cokernels V] [has_images V] [has_image_maps V]

variable [has_zero_object V]

/--
Homotopic maps induce the same map on homology.
-/
theorem homology_map_eq_of_homotopy (h : homotopy f g) (i : ι) :
  (homology_functor V c i).map f = (homology_functor V c i).map g :=
begin
  dsimp [homology_functor],
  apply eq_of_sub_eq_zero,
  ext,
  simp only [homology.π_map, comp_zero, preadditive.comp_sub],
  dsimp [kernel_subobject_map],
  simp_rw [h.comm i],
  simp only [zero_add, zero_comp, d_next_eq_d_from_from_next, kernel_subobject_arrow_comp_assoc,
    preadditive.comp_add],
  rw [←preadditive.sub_comp],
  simp only [category_theory.subobject.factor_thru_add_sub_factor_thru_right],
  erw [subobject.factor_thru_of_le (D.boundaries_le_cycles i)],
  { simp, },
  { rw [prev_d_eq_to_prev_d_to, ←category.assoc],
    apply image_subobject_factors_comp_self, },
end

/-- Homotopy equivalent complexes have isomorphic homologies. -/
def homology_obj_iso_of_homotopy_equiv (f : homotopy_equiv C D) (i : ι) :
  (homology_functor V c i).obj C ≅ (homology_functor V c i).obj D :=
{ hom := (homology_functor V c i).map f.hom,
  inv := (homology_functor V c i).map f.inv,
  hom_inv_id' := begin
    rw [←functor.map_comp, homology_map_eq_of_homotopy f.homotopy_hom_inv_id,
      category_theory.functor.map_id],
  end,
  inv_hom_id' := begin
    rw [←functor.map_comp, homology_map_eq_of_homotopy f.homotopy_inv_hom_id,
      category_theory.functor.map_id],
  end, }

end

namespace category_theory

variables {W : Type*} [category W] [preadditive W]

/-- An additive functor takes homotopies to homotopies. -/
@[simps]
def functor.map_homotopy (F : V ⥤ W) [F.additive] {f g : C ⟶ D} (h : homotopy f g) :
  homotopy ((F.map_homological_complex c).map f) ((F.map_homological_complex c).map g) :=
{ hom := λ i j, F.map (h.hom i j),
  zero' := λ i j w, by { rw [h.zero i j w, F.map_zero], },
  comm := λ i, begin
    have := h.comm i,
    dsimp [d_next, prev_d] at *,
    rcases c.next i with _|⟨inext,wn⟩;
    rcases c.prev i with _|⟨iprev,wp⟩;
    dsimp [d_next, prev_d] at *;
    { intro h,
      simp [h] },
  end, }

/-- An additive functor preserves homotopy equivalences. -/
@[simps]
def functor.map_homotopy_equiv (F : V ⥤ W) [F.additive] (h : homotopy_equiv C D) :
  homotopy_equiv ((F.map_homological_complex c).obj C) ((F.map_homological_complex c).obj D) :=
{ hom := (F.map_homological_complex c).map h.hom,
  inv := (F.map_homological_complex c).map h.inv,
  homotopy_hom_inv_id := begin
    rw [←(F.map_homological_complex c).map_comp, ←(F.map_homological_complex c).map_id],
    exact F.map_homotopy h.homotopy_hom_inv_id,
  end,
  homotopy_inv_hom_id := begin
    rw [←(F.map_homological_complex c).map_comp, ←(F.map_homological_complex c).map_id],
    exact F.map_homotopy h.homotopy_inv_hom_id,
  end }

end category_theory


/-
Copyright (c) 2018 Rohan Mitta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rohan Mitta, Kevin Buzzard, Alistair Tucker, Johannes Hölzl, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.function.iterate
import Mathlib.topology.metric_space.basic
import Mathlib.category_theory.endomorphism
import Mathlib.category_theory.types
import Mathlib.PostPort

universes u v w x

namespace Mathlib

/-!
# Lipschitz continuous functions

A map `f : α → β` between two (extended) metric spaces is called *Lipschitz continuous*
with constant `K ≥ 0` if for all `x, y` we have `edist (f x) (f y) ≤ K * edist x y`.
For a metric space, the latter inequality is equivalent to `dist (f x) (f y) ≤ K * dist x y`.
There is also a version asserting this inequality only for `x` and `y` in some set `s`.

In this file we provide various ways to prove that various combinations of Lipschitz continuous
functions are Lipschitz continuous. We also prove that Lipschitz continuous functions are
uniformly continuous.

## Main definitions and lemmas

* `lipschitz_with K f`: states that `f` is Lipschitz with constant `K : ℝ≥0`
* `lipschitz_on_with K f`: states that `f` is Lipschitz with constant `K : ℝ≥0` on a set `s`
* `lipschitz_with.uniform_continuous`: a Lipschitz function is uniformly continuous
* `lipschitz_on_with.uniform_continuous_on`: a function which is Lipschitz on a set is uniformly
  continuous on that set.

## Implementation notes

The parameter `K` has type `ℝ≥0`. This way we avoid conjuction in the definition and have
coercions both to `ℝ` and `ennreal`. Constructors whose names end with `'` take `K : ℝ` as an
argument, and return `lipschitz_with (nnreal.of_real K) f`.
-/

/-- A function `f` is Lipschitz continuous with constant `K ≥ 0` if for all `x, y`
we have `dist (f x) (f y) ≤ K * dist x y` -/
def lipschitz_with {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] (K : nnreal)
    (f : α → β) :=
  ∀ (x y : α), edist (f x) (f y) ≤ ↑K * edist x y

theorem lipschitz_with_iff_dist_le_mul {α : Type u} {β : Type v} [metric_space α] [metric_space β]
    {K : nnreal} {f : α → β} : lipschitz_with K f ↔ ∀ (x y : α), dist (f x) (f y) ≤ ↑K * dist x y :=
  sorry

theorem lipschitz_with.dist_le_mul {α : Type u} {β : Type v} [metric_space α] [metric_space β]
    {K : nnreal} {f : α → β} : lipschitz_with K f → ∀ (x y : α), dist (f x) (f y) ≤ ↑K * dist x y :=
  iff.mp lipschitz_with_iff_dist_le_mul

/-- A function `f` is Lipschitz continuous with constant `K ≥ 0` on `s` if for all `x, y` in `s`
we have `dist (f x) (f y) ≤ K * dist x y` -/
def lipschitz_on_with {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] (K : nnreal)
    (f : α → β) (s : set α) :=
  ∀ {x : α}, x ∈ s → ∀ {y : α}, y ∈ s → edist (f x) (f y) ≤ ↑K * edist x y

@[simp] theorem lipschitz_on_with_empty {α : Type u} {β : Type v} [emetric_space α]
    [emetric_space β] (K : nnreal) (f : α → β) : lipschitz_on_with K f ∅ :=
  fun (x : α) (x_in : x ∈ ∅) (y : α) (y_in : y ∈ ∅) => false.elim x_in

theorem lipschitz_on_with.mono {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {s : set α} {t : set α} {f : α → β} (hf : lipschitz_on_with K f t) (h : s ⊆ t) :
    lipschitz_on_with K f s :=
  fun (x : α) (x_in : x ∈ s) (y : α) (y_in : y ∈ s) => hf (h x_in) (h y_in)

theorem lipschitz_on_with_iff_dist_le_mul {α : Type u} {β : Type v} [metric_space α]
    [metric_space β] {K : nnreal} {s : set α} {f : α → β} :
    lipschitz_on_with K f s ↔
        ∀ (x : α), x ∈ s → ∀ (y : α), y ∈ s → dist (f x) (f y) ≤ ↑K * dist x y :=
  sorry

theorem lipschitz_on_with.dist_le_mul {α : Type u} {β : Type v} [metric_space α] [metric_space β]
    {K : nnreal} {s : set α} {f : α → β} :
    lipschitz_on_with K f s →
        ∀ (x : α), x ∈ s → ∀ (y : α), y ∈ s → dist (f x) (f y) ≤ ↑K * dist x y :=
  iff.mp lipschitz_on_with_iff_dist_le_mul

@[simp] theorem lipschitz_on_univ {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {f : α → β} : lipschitz_on_with K f set.univ ↔ lipschitz_with K f :=
  sorry

theorem lipschitz_on_with_iff_restrict {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {f : α → β} {s : set α} :
    lipschitz_on_with K f s ↔ lipschitz_with K (set.restrict f s) :=
  sorry

namespace lipschitz_with

theorem edist_le_mul {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] {K : nnreal}
    {f : α → β} (h : lipschitz_with K f) (x : α) (y : α) : edist (f x) (f y) ≤ ↑K * edist x y :=
  h x y

theorem edist_lt_top {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] {K : nnreal}
    {f : α → β} (hf : lipschitz_with K f) {x : α} {y : α} (h : edist x y < ⊤) :
    edist (f x) (f y) < ⊤ :=
  lt_of_le_of_lt (hf x y) (ennreal.mul_lt_top ennreal.coe_lt_top h)

theorem mul_edist_le {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] {K : nnreal}
    {f : α → β} (h : lipschitz_with K f) (x : α) (y : α) : ↑K⁻¹ * edist (f x) (f y) ≤ edist x y :=
  sorry

protected theorem of_edist_le {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {f : α → β} (h : ∀ (x y : α), edist (f x) (f y) ≤ edist x y) : lipschitz_with 1 f :=
  sorry

protected theorem weaken {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] {K : nnreal}
    {f : α → β} (hf : lipschitz_with K f) {K' : nnreal} (h : K ≤ K') : lipschitz_with K' f :=
  fun (x y : α) => le_trans (hf x y) (ennreal.mul_right_mono (iff.mpr ennreal.coe_le_coe h))

theorem ediam_image_le {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] {K : nnreal}
    {f : α → β} (hf : lipschitz_with K f) (s : set α) :
    emetric.diam (f '' s) ≤ ↑K * emetric.diam s :=
  sorry

/-- A Lipschitz function is uniformly continuous -/
protected theorem uniform_continuous {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {f : α → β} (hf : lipschitz_with K f) : uniform_continuous f :=
  sorry

/-- A Lipschitz function is continuous -/
protected theorem continuous {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {f : α → β} (hf : lipschitz_with K f) : continuous f :=
  uniform_continuous.continuous (lipschitz_with.uniform_continuous hf)

protected theorem const {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] (b : β) :
    lipschitz_with 0 fun (a : α) => b :=
  sorry

protected theorem id {α : Type u} [emetric_space α] : lipschitz_with 1 id :=
  lipschitz_with.of_edist_le fun (x y : α) => le_refl (edist (id x) (id y))

protected theorem subtype_val {α : Type u} [emetric_space α] (s : set α) :
    lipschitz_with 1 subtype.val :=
  lipschitz_with.of_edist_le
    fun (x y : Subtype fun (x : α) => x ∈ s) => le_refl (edist (subtype.val x) (subtype.val y))

protected theorem subtype_coe {α : Type u} [emetric_space α] (s : set α) : lipschitz_with 1 coe :=
  lipschitz_with.subtype_val s

protected theorem restrict {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {f : α → β} (hf : lipschitz_with K f) (s : set α) :
    lipschitz_with K (set.restrict f s) :=
  fun (x y : ↥s) => hf ↑x ↑y

protected theorem comp {α : Type u} {β : Type v} {γ : Type w} [emetric_space α] [emetric_space β]
    [emetric_space γ] {Kf : nnreal} {Kg : nnreal} {f : β → γ} {g : α → β} (hf : lipschitz_with Kf f)
    (hg : lipschitz_with Kg g) : lipschitz_with (Kf * Kg) (f ∘ g) :=
  sorry

protected theorem prod_fst {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] :
    lipschitz_with 1 prod.fst :=
  lipschitz_with.of_edist_le
    fun (x y : α × β) =>
      le_max_left (edist (prod.fst x) (prod.fst y)) (edist (prod.snd x) (prod.snd y))

protected theorem prod_snd {α : Type u} {β : Type v} [emetric_space α] [emetric_space β] :
    lipschitz_with 1 prod.snd :=
  lipschitz_with.of_edist_le
    fun (x y : α × β) =>
      le_max_right (edist (prod.fst x) (prod.fst y)) (edist (prod.snd x) (prod.snd y))

protected theorem prod {α : Type u} {β : Type v} {γ : Type w} [emetric_space α] [emetric_space β]
    [emetric_space γ] {f : α → β} {Kf : nnreal} (hf : lipschitz_with Kf f) {g : α → γ} {Kg : nnreal}
    (hg : lipschitz_with Kg g) : lipschitz_with (max Kf Kg) fun (x : α) => (f x, g x) :=
  sorry

protected theorem uncurry {α : Type u} {β : Type v} {γ : Type w} [emetric_space α] [emetric_space β]
    [emetric_space γ] {f : α → β → γ} {Kα : nnreal} {Kβ : nnreal}
    (hα : ∀ (b : β), lipschitz_with Kα fun (a : α) => f a b)
    (hβ : ∀ (a : α), lipschitz_with Kβ (f a)) : lipschitz_with (Kα + Kβ) (function.uncurry f) :=
  sorry

protected theorem iterate {α : Type u} [emetric_space α] {K : nnreal} {f : α → α}
    (hf : lipschitz_with K f) (n : ℕ) : lipschitz_with (K ^ n) (nat.iterate f n) :=
  sorry

theorem edist_iterate_succ_le_geometric {α : Type u} [emetric_space α] {K : nnreal} {f : α → α}
    (hf : lipschitz_with K f) (x : α) (n : ℕ) :
    edist (nat.iterate f n x) (nat.iterate f (n + 1) x) ≤ edist x (f x) * ↑K ^ n :=
  sorry

protected theorem mul {α : Type u} [emetric_space α] {f : category_theory.End α}
    {g : category_theory.End α} {Kf : nnreal} {Kg : nnreal} (hf : lipschitz_with Kf f)
    (hg : lipschitz_with Kg g) : lipschitz_with (Kf * Kg) (f * g) :=
  lipschitz_with.comp hf hg

/-- The product of a list of Lipschitz continuous endomorphisms is a Lipschitz continuous
endomorphism. -/
protected theorem list_prod {α : Type u} {ι : Type x} [emetric_space α]
    (f : ι → category_theory.End α) (K : ι → nnreal) (h : ∀ (i : ι), lipschitz_with (K i) (f i))
    (l : List ι) : lipschitz_with (list.prod (list.map K l)) (list.prod (list.map f l)) :=
  sorry

protected theorem pow {α : Type u} [emetric_space α] {f : category_theory.End α} {K : nnreal}
    (h : lipschitz_with K f) (n : ℕ) : lipschitz_with (K ^ n) (f ^ n) :=
  sorry

protected theorem of_dist_le' {α : Type u} {β : Type v} [metric_space α] [metric_space β]
    {f : α → β} {K : ℝ} (h : ∀ (x y : α), dist (f x) (f y) ≤ K * dist x y) :
    lipschitz_with (nnreal.of_real K) f :=
  of_dist_le_mul
    fun (x y : α) =>
      le_trans (h x y) (mul_le_mul_of_nonneg_right (nnreal.le_coe_of_real K) dist_nonneg)

protected theorem mk_one {α : Type u} {β : Type v} [metric_space α] [metric_space β] {f : α → β}
    (h : ∀ (x y : α), dist (f x) (f y) ≤ dist x y) : lipschitz_with 1 f :=
  sorry

/-- For functions to `ℝ`, it suffices to prove `f x ≤ f y + K * dist x y`; this version
doesn't assume `0≤K`. -/
protected theorem of_le_add_mul' {α : Type u} [metric_space α] {f : α → ℝ} (K : ℝ)
    (h : ∀ (x y : α), f x ≤ f y + K * dist x y) : lipschitz_with (nnreal.of_real K) f :=
  sorry

/-- For functions to `ℝ`, it suffices to prove `f x ≤ f y + K * dist x y`; this version
assumes `0≤K`. -/
protected theorem of_le_add_mul {α : Type u} [metric_space α] {f : α → ℝ} (K : nnreal)
    (h : ∀ (x y : α), f x ≤ f y + ↑K * dist x y) : lipschitz_with K f :=
  sorry

protected theorem of_le_add {α : Type u} [metric_space α] {f : α → ℝ}
    (h : ∀ (x y : α), f x ≤ f y + dist x y) : lipschitz_with 1 f :=
  sorry

protected theorem le_add_mul {α : Type u} [metric_space α] {f : α → ℝ} {K : nnreal}
    (h : lipschitz_with K f) (x : α) (y : α) : f x ≤ f y + ↑K * dist x y :=
  iff.mp sub_le_iff_le_add' (le_trans (le_abs_self (f x - f y)) (dist_le_mul h x y))

protected theorem iff_le_add_mul {α : Type u} [metric_space α] {f : α → ℝ} {K : nnreal} :
    lipschitz_with K f ↔ ∀ (x y : α), f x ≤ f y + ↑K * dist x y :=
  { mp := lipschitz_with.le_add_mul, mpr := lipschitz_with.of_le_add_mul K }

theorem nndist_le {α : Type u} {β : Type v} [metric_space α] [metric_space β] {K : nnreal}
    {f : α → β} (hf : lipschitz_with K f) (x : α) (y : α) : nndist (f x) (f y) ≤ K * nndist x y :=
  dist_le_mul hf x y

theorem diam_image_le {α : Type u} {β : Type v} [metric_space α] [metric_space β] {K : nnreal}
    {f : α → β} (hf : lipschitz_with K f) (s : set α) (hs : metric.bounded s) :
    metric.diam (f '' s) ≤ ↑K * metric.diam s :=
  sorry

protected theorem dist_left {α : Type u} [metric_space α] (y : α) :
    lipschitz_with 1 fun (x : α) => dist x y :=
  sorry

protected theorem dist_right {α : Type u} [metric_space α] (x : α) : lipschitz_with 1 (dist x) :=
  lipschitz_with.of_le_add fun (y z : α) => dist_triangle_right x y z

protected theorem dist {α : Type u} [metric_space α] :
    lipschitz_with (bit0 1) (function.uncurry dist) :=
  lipschitz_with.uncurry lipschitz_with.dist_left lipschitz_with.dist_right

theorem dist_iterate_succ_le_geometric {α : Type u} [metric_space α] {K : nnreal} {f : α → α}
    (hf : lipschitz_with K f) (x : α) (n : ℕ) :
    dist (nat.iterate f n x) (nat.iterate f (n + 1) x) ≤ dist x (f x) * ↑K ^ n :=
  sorry

end lipschitz_with

namespace lipschitz_on_with

protected theorem uniform_continuous_on {α : Type u} {β : Type v} [emetric_space α]
    [emetric_space β] {K : nnreal} {s : set α} {f : α → β} (hf : lipschitz_on_with K f s) :
    uniform_continuous_on f s :=
  iff.mpr uniform_continuous_on_iff_restrict
    (lipschitz_with.uniform_continuous (iff.mp lipschitz_on_with_iff_restrict hf))

protected theorem continuous_on {α : Type u} {β : Type v} [emetric_space α] [emetric_space β]
    {K : nnreal} {s : set α} {f : α → β} (hf : lipschitz_on_with K f s) : continuous_on f s :=
  uniform_continuous_on.continuous_on (lipschitz_on_with.uniform_continuous_on hf)

end lipschitz_on_with

/-- If a function is locally Lipschitz around a point, then it is continuous at this point. -/
theorem continuous_at_of_locally_lipschitz {α : Type u} {β : Type v} [metric_space α]
    [metric_space β] {f : α → β} {x : α} {r : ℝ} (hr : 0 < r) (K : ℝ)
    (h : ∀ (y : α), dist y x < r → dist (f y) (f x) ≤ K * dist y x) : continuous_at f x :=
  sorry

end Mathlib


import tactic
import data.list.basic

namespace list
variables {α : Type*}

@[simp] theorem append_inj'_iff (s₁ s₂ : list α) {t₁ t₂ : list α} (hl : length t₁ = length t₂) :
  s₁ ++ t₁ = s₂ ++ t₂ ↔ s₁ = s₂ ∧ t₁ = t₂ :=
⟨λ h, append_inj' h hl, by cc⟩

@[simp] theorem init_nil : (@list.nil α).init = [] := rfl

@[simp] theorem init_singleton (x : α) : [x].init = [] := rfl

@[simp] lemma last'_reverse {α : Type*} (xs : list α) : xs.reverse.last' = xs.head' :=
by cases xs; simp

lemma last'_eq_head'_reverse {α : Type*} (xs : list α) : xs.last' = xs.reverse.head' :=
by simpa using xs.reverse.last'_reverse

/-- Take cases on the last element of the list. Like `reverse_rec_on`, this can be used
for constructing data as well. -/
def reverse_cases_on {α : Type*} {C : list α → Sort*} (l : list α) (h₀ : C [])
  (case : ∀ st lt, C (st ++ [lt])) : C l :=
by { induction l using list.reverse_rec_on, { exact h₀, }, { apply case, } }

/-! ### after -/
section after

lemma after_spec {α : Type*} (p : α → Prop) [decidable_pred p]
  (as : list α) (x : α) (xs : list α) (has : ∀ a ∈ as, ¬p a) (hx : p x) :
  (as ++ x :: xs).after p = xs :=
begin
  induction as with hd tl ih, { simp [after, hx], },
  simp [after, has hd, ih (λ a ha, has a (or.inr ha))],
end

lemma after_sublist {α : Type*} (p : α → Prop) [decidable_pred p] (xs : list α) :
  xs.after p <+ xs :=
begin
  induction xs, { refl, },
  simp only [after], split_ifs; apply sublist.cons, { refl, }, { assumption, }
end

lemma after_sublist_strict {α : Type*} (p : α → Prop) [decidable_pred p] (x : α) (xs : list α) :
  (x :: xs).after p <+ xs :=
by { simp only [after], split_ifs, { refl, }, { apply after_sublist, } }

lemma after_length_lt {α : Type*} (p : α → Prop) [decidable_pred p] {xs : list α} :
  (xs.after p).length < xs.length ↔ 0 < xs.length :=
⟨pos_of_gt, begin
  cases xs, { simp, },
  intro, simpa [nat.lt_succ_iff] using length_le_of_sublist (after_sublist_strict p _ _),
end⟩

end after

/-! ### take_while -/
section take_while

lemma take_while_spec {α : Type*} (p : α → Prop) [decidable_pred p]
  (as : list α) (x : α) (xs : list α) (has : ∀ a ∈ as, p a) (hx : ¬ p x) :
  (as ++ x :: xs).take_while p = as :=
begin
  induction as with hd tl ih, { simp [take_while, hx], },
  simp [take_while, has hd, ih (λ a ha, has a (or.inr ha))],
end

end take_while

end list


/-
Copyright (c) 2022 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.subobject.lattice
import category_theory.essentially_small
import category_theory.simple

/-!
# Artinian and noetherian categories

An artinian category is a category in which objects do not
have infinite decreasing sequences of subobjects.

A noetherian category is a category in which objects do not
have infinite increasing sequences of subobjects.

We show that any nonzero artinian object has a simple subobject.

## Future work
The Jordan-Hölder theorem, following https://stacks.math.columbia.edu/tag/0FCK.
-/

namespace category_theory
open category_theory.limits

variables {C : Type*} [category C]

/--
A noetherian object is an object
which does not have infinite increasing sequences of subobjects.

See https://stacks.math.columbia.edu/tag/0FCG
-/
class noetherian_object (X : C) : Prop :=
(subobject_gt_well_founded : well_founded ((>) : subobject X → subobject X → Prop))

/--
An artinian object is an object
which does not have infinite decreasing sequences of subobjects.

See https://stacks.math.columbia.edu/tag/0FCF
-/
class artinian_object (X : C) : Prop :=
(subobject_lt_well_founded [] : well_founded ((<) : subobject X → subobject X → Prop))

variables (C)

/-- A category is noetherian if it is essentially small and all objects are noetherian. -/
class noetherian extends essentially_small C :=
(noetherian_object : ∀ (X : C), noetherian_object X)

attribute [priority 100, instance] noetherian.noetherian_object

/-- A category is artinian if it is essentially small and all objects are artinian. -/
class artinian extends essentially_small C :=
(artinian_object : ∀ (X : C), artinian_object X)

attribute [priority 100, instance] artinian.artinian_object

variables {C}

open subobject
variables [has_zero_morphisms C] [has_zero_object C]

lemma exists_simple_subobject {X : C} [artinian_object X] (h : ¬ is_zero X) :
  ∃ (Y : subobject X), simple (Y : C) :=
begin
  haveI : nontrivial (subobject X) := nontrivial_of_not_is_zero h,
  haveI := is_atomic_of_order_bot_well_founded_lt (artinian_object.subobject_lt_well_founded X),
  have := is_atomic.eq_bot_or_exists_atom_le (⊤ : subobject X),
  obtain ⟨Y, s⟩ := (is_atomic.eq_bot_or_exists_atom_le (⊤ : subobject X)).resolve_left top_ne_bot,
  exact ⟨Y, (subobject_simple_iff_is_atom _).mpr s.1⟩,
end

/-- Choose an arbitrary simple subobject of a non-zero artinian object. -/
noncomputable def simple_subobject {X : C} [artinian_object X] (h : ¬ is_zero X) : C :=
(exists_simple_subobject h).some

/-- The monomorphism from the arbitrary simple subobject of a non-zero artinian object. -/
@[derive mono]
noncomputable def simple_subobject_arrow {X : C} [artinian_object X] (h : ¬ is_zero X) :
  simple_subobject h ⟶ X :=
(exists_simple_subobject h).some.arrow

instance {X : C} [artinian_object X] (h : ¬ is_zero X) : simple (simple_subobject h) :=
(exists_simple_subobject h).some_spec

end category_theory